Merge branch 'develop' into crypto-tweaks

This commit is contained in:
Luke Parker
2023-03-16 16:43:04 -04:00
committed by GitHub
173 changed files with 29638 additions and 3517 deletions

View File

@@ -1,12 +1,15 @@
use monero_serai::{rpc::Rpc, wallet::TransactionError, transaction::Transaction};
use monero_serai::{
wallet::{TransactionError, extra::MAX_ARBITRARY_DATA_SIZE},
transaction::Transaction,
};
mod runner;
test!(
add_single_data_less_than_255,
add_single_data_less_than_max,
(
|_, mut builder: Builder, addr| async move {
let arbitrary_data = vec![b'\0', 254];
let arbitrary_data = vec![b'\0'; MAX_ARBITRARY_DATA_SIZE - 1];
// make sure we can add to tx
let result = builder.add_data(arbitrary_data.clone());
@@ -15,8 +18,7 @@ test!(
builder.add_payment(addr, 5);
(builder.build().unwrap(), (arbitrary_data,))
},
|rpc: Rpc, signed: Transaction, mut scanner: Scanner, data: (Vec<u8>,)| async move {
let tx = rpc.get_transaction(signed.hash()).await.unwrap();
|_, tx: Transaction, mut scanner: Scanner, data: (Vec<u8>,)| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.arbitrary_data()[0], data.0);
@@ -25,10 +27,10 @@ test!(
);
test!(
add_multiple_data_less_than_255,
add_multiple_data_less_than_max,
(
|_, mut builder: Builder, addr| async move {
let data = vec![b'\0', 254];
let data = vec![b'\0'; MAX_ARBITRARY_DATA_SIZE - 1];
// Add tx multiple times
for _ in 0 .. 5 {
@@ -39,8 +41,7 @@ test!(
builder.add_payment(addr, 5);
(builder.build().unwrap(), data)
},
|rpc: Rpc, signed: Transaction, mut scanner: Scanner, data: Vec<u8>| async move {
let tx = rpc.get_transaction(signed.hash()).await.unwrap();
|_, tx: Transaction, mut scanner: Scanner, data: Vec<u8>| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.arbitrary_data(), vec![data; 5]);
@@ -49,24 +50,24 @@ test!(
);
test!(
add_single_data_more_than_255,
add_single_data_more_than_max,
(
|_, mut builder: Builder, addr| async move {
// Make a data that is bigger than 255 bytes
let mut data = vec![b'a'; 256];
// Make a data that is bigger than the maximum
let mut data = vec![b'a'; MAX_ARBITRARY_DATA_SIZE + 1];
// Make sure we get an error if we try to add it to the TX
assert_eq!(builder.add_data(data.clone()), Err(TransactionError::TooMuchData));
// Reduce data size and retry. The data will now be 255 bytes long, exactly
// Reduce data size and retry. The data will now be 255 bytes long (including the added
// marker), exactly
data.pop();
assert!(builder.add_data(data.clone()).is_ok());
builder.add_payment(addr, 5);
(builder.build().unwrap(), data)
},
|rpc: Rpc, signed: Transaction, mut scanner: Scanner, data: Vec<u8>| async move {
let tx = rpc.get_transaction(signed.hash()).await.unwrap();
|_, tx: Transaction, mut scanner: Scanner, data: Vec<u8>| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.arbitrary_data(), vec![data]);

View File

@@ -1,4 +1,5 @@
use core::ops::Deref;
use std::collections::HashSet;
use lazy_static::lazy_static;
@@ -10,10 +11,11 @@ use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
use tokio::sync::Mutex;
use monero_serai::{
Protocol, random_scalar,
random_scalar,
wallet::{
ViewPair,
address::{Network, AddressType, AddressMeta, MoneroAddress},
ViewPair, Scanner,
address::{Network, AddressType, AddressSpec, AddressMeta, MoneroAddress},
SpendableOutput,
},
rpc::Rpc,
};
@@ -41,7 +43,7 @@ pub async fn mine_until_unlocked(rpc: &Rpc, addr: &str, tx_hash: [u8; 32]) {
let mut height = rpc.get_height().await.unwrap();
let mut found = false;
while !found {
let block = rpc.get_block(height - 1).await.unwrap();
let block = rpc.get_block_by_number(height - 1).await.unwrap();
found = match block.txs.iter().find(|&&x| x == tx_hash) {
Some(_) => true,
None => {
@@ -56,6 +58,22 @@ pub async fn mine_until_unlocked(rpc: &Rpc, addr: &str, tx_hash: [u8; 32]) {
rpc.generate_blocks(addr, 9).await.unwrap();
}
// Mines 60 blocks and returns an unlocked miner TX output.
#[allow(dead_code)]
pub async fn get_miner_tx_output(rpc: &Rpc, view: &ViewPair) -> SpendableOutput {
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
// Mine 60 blocks to unlock a miner TX
let start = rpc.get_height().await.unwrap();
rpc
.generate_blocks(&view.address(Network::Mainnet, AddressSpec::Standard).to_string(), 60)
.await
.unwrap();
let block = rpc.get_block_by_number(start).await.unwrap();
scanner.scan(rpc, &block).await.unwrap().swap_remove(0).ignore_timelock().swap_remove(0)
}
pub async fn rpc() -> Rpc {
let rpc = Rpc::new("http://127.0.0.1:18081".to_string()).unwrap();
@@ -73,7 +91,9 @@ pub async fn rpc() -> Rpc {
// Mine 40 blocks to ensure decoy availability
rpc.generate_blocks(&addr, 40).await.unwrap();
assert!(!matches!(rpc.get_protocol().await.unwrap(), Protocol::Unsupported(_)));
// Make sure we recognize the protocol
rpc.get_protocol().await.unwrap();
rpc
}
@@ -138,12 +158,12 @@ macro_rules! test {
use monero_serai::{
random_scalar,
wallet::{
address::Network, ViewPair, Scanner, SignableTransaction,
address::{Network, AddressSpec}, ViewPair, Scanner, Change, SignableTransaction,
SignableTransactionBuilder,
},
};
use runner::{random_address, rpc, mine_until_unlocked};
use runner::{random_address, rpc, mine_until_unlocked, get_miner_tx_output};
type Builder = SignableTransactionBuilder;
@@ -169,33 +189,23 @@ macro_rules! test {
keys[&Participant::new(1).unwrap()].group_key().0
};
let view = ViewPair::new(spend_pub, Zeroizing::new(random_scalar(&mut OsRng)));
let rpc = rpc().await;
let (addr, miner_tx) = {
let mut scanner =
Scanner::from_view(view.clone(), Network::Mainnet, Some(HashSet::new()));
let addr = scanner.address();
let view = ViewPair::new(spend_pub, Zeroizing::new(random_scalar(&mut OsRng)));
let addr = view.address(Network::Mainnet, AddressSpec::Standard);
// mine 60 blocks to unlock a miner tx
let start = rpc.get_height().await.unwrap();
rpc.generate_blocks(&addr.to_string(), 60).await.unwrap();
let block = rpc.get_block(start).await.unwrap();
(
addr,
scanner.scan(
&rpc,
&block
).await.unwrap().swap_remove(0).ignore_timelock().swap_remove(0)
)
};
let miner_tx = get_miner_tx_output(&rpc, &view).await;
let builder = SignableTransactionBuilder::new(
rpc.get_protocol().await.unwrap(),
rpc.get_fee().await.unwrap(),
Some(random_address().2),
Some(Change::new(
&ViewPair::new(
&random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE,
Zeroizing::new(random_scalar(&mut OsRng))
),
false
)),
);
let sign = |tx: SignableTransaction| {
@@ -247,7 +257,7 @@ macro_rules! test {
mine_until_unlocked(&rpc, &random_address().2.to_string(), signed.hash()).await;
let tx = rpc.get_transaction(signed.hash()).await.unwrap();
let scanner =
Scanner::from_view(view.clone(), Network::Mainnet, Some(HashSet::new()));
Scanner::from_view(view.clone(), Some(HashSet::new()));
($first_checks)(rpc.clone(), tx, scanner, state).await
});
#[allow(unused_variables, unused_mut, unused_assignments)]
@@ -268,7 +278,7 @@ macro_rules! test {
#[allow(unused_assignments)]
{
let scanner =
Scanner::from_view(view.clone(), Network::Mainnet, Some(HashSet::new()));
Scanner::from_view(view.clone(), Some(HashSet::new()));
carried_state =
Box::new(($checks)(rpc.clone(), tx, scanner, state).await);
}

300
coins/monero/tests/scan.rs Normal file
View File

@@ -0,0 +1,300 @@
use rand::RngCore;
use monero_serai::{transaction::Transaction, wallet::address::SubaddressIndex};
mod runner;
test!(
scan_standard_address,
(
|_, mut builder: Builder, _| async move {
let view = runner::random_address().1;
let scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
builder.add_payment(view.address(Network::Mainnet, AddressSpec::Standard), 5);
(builder.build().unwrap(), scanner)
},
|_, tx: Transaction, _, mut state: Scanner| async move {
let output = state.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
},
),
);
test!(
scan_subaddress,
(
|_, mut builder: Builder, _| async move {
let subaddress = SubaddressIndex::new(0, 1).unwrap();
let view = runner::random_address().1;
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
scanner.register_subaddress(subaddress);
builder.add_payment(view.address(Network::Mainnet, AddressSpec::Subaddress(subaddress)), 5);
(builder.build().unwrap(), (scanner, subaddress))
},
|_, tx: Transaction, _, mut state: (Scanner, SubaddressIndex)| async move {
let output = state.0.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.subaddress, Some(state.1));
},
),
);
test!(
scan_integrated_address,
(
|_, mut builder: Builder, _| async move {
let view = runner::random_address().1;
let scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
let mut payment_id = [0u8; 8];
OsRng.fill_bytes(&mut payment_id);
builder.add_payment(view.address(Network::Mainnet, AddressSpec::Integrated(payment_id)), 5);
(builder.build().unwrap(), (scanner, payment_id))
},
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8])| async move {
let output = state.0.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.payment_id, state.1);
},
),
);
test!(
scan_featured_standard,
(
|_, mut builder: Builder, _| async move {
let view = runner::random_address().1;
let scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured { subaddress: None, payment_id: None, guaranteed: false },
),
5,
);
(builder.build().unwrap(), scanner)
},
|_, tx: Transaction, _, mut state: Scanner| async move {
let output = state.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
},
),
);
test!(
scan_featured_subaddress,
(
|_, mut builder: Builder, _| async move {
let subaddress = SubaddressIndex::new(0, 2).unwrap();
let view = runner::random_address().1;
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
scanner.register_subaddress(subaddress);
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured {
subaddress: Some(subaddress),
payment_id: None,
guaranteed: false,
},
),
5,
);
(builder.build().unwrap(), (scanner, subaddress))
},
|_, tx: Transaction, _, mut state: (Scanner, SubaddressIndex)| async move {
let output = state.0.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.subaddress, Some(state.1));
},
),
);
test!(
scan_featured_integrated,
(
|_, mut builder: Builder, _| async move {
let view = runner::random_address().1;
let scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
let mut payment_id = [0u8; 8];
OsRng.fill_bytes(&mut payment_id);
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured {
subaddress: None,
payment_id: Some(payment_id),
guaranteed: false,
},
),
5,
);
(builder.build().unwrap(), (scanner, payment_id))
},
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8])| async move {
let output = state.0.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.payment_id, state.1);
},
),
);
test!(
scan_featured_integrated_subaddress,
(
|_, mut builder: Builder, _| async move {
let subaddress = SubaddressIndex::new(0, 3).unwrap();
let view = runner::random_address().1;
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
scanner.register_subaddress(subaddress);
let mut payment_id = [0u8; 8];
OsRng.fill_bytes(&mut payment_id);
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured {
subaddress: Some(subaddress),
payment_id: Some(payment_id),
guaranteed: false,
},
),
5,
);
(builder.build().unwrap(), (scanner, payment_id, subaddress))
},
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8], SubaddressIndex)| async move {
let output = state.0.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.payment_id, state.1);
assert_eq!(output.metadata.subaddress, Some(state.2));
},
),
);
test!(
scan_guaranteed_standard,
(
|_, mut builder: Builder, _| async move {
let view = runner::random_address().1;
let scanner = Scanner::from_view(view.clone(), None);
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured { subaddress: None, payment_id: None, guaranteed: true },
),
5,
);
(builder.build().unwrap(), scanner)
},
|_, tx: Transaction, _, mut state: Scanner| async move {
let output = state.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
},
),
);
test!(
scan_guaranteed_subaddress,
(
|_, mut builder: Builder, _| async move {
let subaddress = SubaddressIndex::new(1, 0).unwrap();
let view = runner::random_address().1;
let mut scanner = Scanner::from_view(view.clone(), None);
scanner.register_subaddress(subaddress);
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured {
subaddress: Some(subaddress),
payment_id: None,
guaranteed: true,
},
),
5,
);
(builder.build().unwrap(), (scanner, subaddress))
},
|_, tx: Transaction, _, mut state: (Scanner, SubaddressIndex)| async move {
let output = state.0.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.subaddress, Some(state.1));
},
),
);
test!(
scan_guaranteed_integrated,
(
|_, mut builder: Builder, _| async move {
let view = runner::random_address().1;
let scanner = Scanner::from_view(view.clone(), None);
let mut payment_id = [0u8; 8];
OsRng.fill_bytes(&mut payment_id);
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured {
subaddress: None,
payment_id: Some(payment_id),
guaranteed: true,
},
),
5,
);
(builder.build().unwrap(), (scanner, payment_id))
},
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8])| async move {
let output = state.0.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.payment_id, state.1);
},
),
);
test!(
scan_guaranteed_integrated_subaddress,
(
|_, mut builder: Builder, _| async move {
let subaddress = SubaddressIndex::new(1, 1).unwrap();
let view = runner::random_address().1;
let mut scanner = Scanner::from_view(view.clone(), None);
scanner.register_subaddress(subaddress);
let mut payment_id = [0u8; 8];
OsRng.fill_bytes(&mut payment_id);
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured {
subaddress: Some(subaddress),
payment_id: Some(payment_id),
guaranteed: true,
},
),
5,
);
(builder.build().unwrap(), (scanner, payment_id, subaddress))
},
|_, tx: Transaction, _, mut state: (Scanner, [u8; 8], SubaddressIndex)| async move {
let output = state.0.scan_transaction(&tx).not_locked().swap_remove(0);
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.payment_id, state.1);
assert_eq!(output.metadata.subaddress, Some(state.2));
},
),
);

View File

@@ -1,6 +1,7 @@
use monero_serai::{
wallet::{ReceivedOutput, SpendableOutput},
wallet::{extra::Extra, address::SubaddressIndex, ReceivedOutput, SpendableOutput},
transaction::Transaction,
rpc::Rpc,
};
mod runner;
@@ -49,3 +50,69 @@ test!(
},
),
);
test!(
// Ideally, this would be single_R, yet it isn't feasible to apply allow(non_snake_case) here
single_r_subaddress_send,
(
// Consume this builder for an output we can use in the future
// This is needed because we can't get the input from the passed in builder
|_, mut builder: Builder, addr| async move {
builder.add_payment(addr, 1000000000000);
(builder.build().unwrap(), ())
},
|_, tx: Transaction, mut scanner: Scanner, _| async move {
let mut outputs = scanner.scan_transaction(&tx).not_locked();
outputs.sort_by(|x, y| x.commitment().amount.cmp(&y.commitment().amount));
assert_eq!(outputs[0].commitment().amount, 1000000000000);
outputs
},
),
(
|rpc: Rpc, _, _, mut outputs: Vec<ReceivedOutput>| async move {
let change_view = ViewPair::new(
&random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE,
Zeroizing::new(random_scalar(&mut OsRng)),
);
let mut builder = SignableTransactionBuilder::new(
rpc.get_protocol().await.unwrap(),
rpc.get_fee().await.unwrap(),
Some(Change::new(&change_view, false)),
);
builder.add_input(SpendableOutput::from(&rpc, outputs.swap_remove(0)).await.unwrap());
// Send to a subaddress
let sub_view = ViewPair::new(
&random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE,
Zeroizing::new(random_scalar(&mut OsRng)),
);
builder.add_payment(
sub_view
.address(Network::Mainnet, AddressSpec::Subaddress(SubaddressIndex::new(0, 1).unwrap())),
1,
);
(builder.build().unwrap(), (change_view, sub_view))
},
|_, tx: Transaction, _, views: (ViewPair, ViewPair)| async move {
// Make sure the change can pick up its output
let mut change_scanner = Scanner::from_view(views.0, Some(HashSet::new()));
assert!(change_scanner.scan_transaction(&tx).not_locked().len() == 1);
// Make sure the subaddress can pick up its output
let mut sub_scanner = Scanner::from_view(views.1, Some(HashSet::new()));
sub_scanner.register_subaddress(SubaddressIndex::new(0, 1).unwrap());
let sub_outputs = sub_scanner.scan_transaction(&tx).not_locked();
assert!(sub_outputs.len() == 1);
assert_eq!(sub_outputs[0].commitment().amount, 1);
// Make sure only one R was included in TX extra
assert!(Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref())
.unwrap()
.keys()
.unwrap()
.1
.is_none());
},
),
);

View File

@@ -0,0 +1,245 @@
use std::{
collections::{HashSet, HashMap},
str::FromStr,
};
use rand_core::{OsRng, RngCore};
use serde::Deserialize;
use serde_json::json;
use monero_rpc::{
monero::{
Amount, Address,
cryptonote::{hash::Hash, subaddress::Index},
util::address::PaymentId,
},
TransferOptions, WalletClient,
};
use monero_serai::{
transaction::Transaction,
wallet::{
address::{Network, AddressSpec, SubaddressIndex, MoneroAddress},
extra::{MAX_TX_EXTRA_NONCE_SIZE, Extra},
Scanner,
},
rpc::Rpc,
};
mod runner;
async fn make_integrated_address(payment_id: [u8; 8]) -> String {
#[derive(Deserialize, Debug)]
struct IntegratedAddressResponse {
integrated_address: String,
}
let rpc = Rpc::new("http://127.0.0.1:6061".to_string()).unwrap();
let res = rpc
.json_rpc_call::<IntegratedAddressResponse>(
"make_integrated_address",
Some(json!({ "payment_id": hex::encode(payment_id) })),
)
.await
.unwrap();
res.integrated_address
}
async fn initialize_rpcs() -> (WalletClient, Rpc, monero_rpc::monero::Address) {
let wallet_rpc =
monero_rpc::RpcClientBuilder::new().build("http://127.0.0.1:6061").unwrap().wallet();
let daemon_rpc = runner::rpc().await;
let address_resp = wallet_rpc.get_address(0, None).await;
let wallet_rpc_addr = if address_resp.is_ok() {
address_resp.unwrap().address
} else {
wallet_rpc.create_wallet("wallet".to_string(), None, "English".to_string()).await.unwrap();
let addr = wallet_rpc.get_address(0, None).await.unwrap().address;
daemon_rpc.generate_blocks(&addr.to_string(), 70).await.unwrap();
addr
};
(wallet_rpc, daemon_rpc, wallet_rpc_addr)
}
async fn from_wallet_rpc_to_self(spec: AddressSpec) {
// initialize rpc
let (wallet_rpc, daemon_rpc, wallet_rpc_addr) = initialize_rpcs().await;
// make an addr
let (_, view_pair, _) = runner::random_address();
let addr = Address::from_str(&view_pair.address(Network::Mainnet, spec).to_string()[..]).unwrap();
// refresh & make a tx
wallet_rpc.refresh(None).await.unwrap();
let tx = wallet_rpc
.transfer(
HashMap::from([(addr, Amount::ONE_XMR)]),
monero_rpc::TransferPriority::Default,
TransferOptions::default(),
)
.await
.unwrap();
let tx_hash: [u8; 32] = tx.tx_hash.0.try_into().unwrap();
// unlock it
runner::mine_until_unlocked(&daemon_rpc, &wallet_rpc_addr.to_string(), tx_hash).await;
// create the scanner
let mut scanner = Scanner::from_view(view_pair, Some(HashSet::new()));
if let AddressSpec::Subaddress(index) = spec {
scanner.register_subaddress(index);
}
// retrieve it and confirm
let tx = daemon_rpc.get_transaction(tx_hash).await.unwrap();
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
match spec {
AddressSpec::Subaddress(index) => assert_eq!(output.metadata.subaddress, Some(index)),
AddressSpec::Integrated(payment_id) => {
assert_eq!(output.metadata.payment_id, payment_id);
assert_eq!(output.metadata.subaddress, None);
}
_ => assert_eq!(output.metadata.subaddress, None),
}
assert_eq!(output.commitment().amount, 1000000000000);
}
async_sequential!(
async fn receipt_of_wallet_rpc_tx_standard() {
from_wallet_rpc_to_self(AddressSpec::Standard).await;
}
async fn receipt_of_wallet_rpc_tx_subaddress() {
from_wallet_rpc_to_self(AddressSpec::Subaddress(SubaddressIndex::new(0, 1).unwrap())).await;
}
async fn receipt_of_wallet_rpc_tx_integrated() {
let mut payment_id = [0u8; 8];
OsRng.fill_bytes(&mut payment_id);
from_wallet_rpc_to_self(AddressSpec::Integrated(payment_id)).await;
}
);
test!(
send_to_wallet_rpc_standard,
(
|_, mut builder: Builder, _| async move {
// initialize rpc
let (wallet_rpc, _, wallet_rpc_addr) = initialize_rpcs().await;
// add destination
builder.add_payment(
MoneroAddress::from_str(Network::Mainnet, &wallet_rpc_addr.to_string()).unwrap(),
1000000,
);
(builder.build().unwrap(), (wallet_rpc,))
},
|_, tx: Transaction, _, data: (WalletClient,)| async move {
// confirm receipt
data.0.refresh(None).await.unwrap();
let transfer =
data.0.get_transfer(Hash::from_slice(&tx.hash()), None).await.unwrap().unwrap();
assert_eq!(transfer.amount.as_pico(), 1000000);
assert_eq!(transfer.subaddr_index, Index { major: 0, minor: 0 });
},
),
);
test!(
send_to_wallet_rpc_subaddress,
(
|_, mut builder: Builder, _| async move {
// initialize rpc
let (wallet_rpc, _, _) = initialize_rpcs().await;
// make the addr
let (subaddress, index) = wallet_rpc.create_address(0, None).await.unwrap();
builder.add_payment(
MoneroAddress::from_str(Network::Mainnet, &subaddress.to_string()).unwrap(),
1000000,
);
(builder.build().unwrap(), (wallet_rpc, index))
},
|_, tx: Transaction, _, data: (WalletClient, u32)| async move {
// confirm receipt
data.0.refresh(None).await.unwrap();
let transfer =
data.0.get_transfer(Hash::from_slice(&tx.hash()), None).await.unwrap().unwrap();
assert_eq!(transfer.amount.as_pico(), 1000000);
assert_eq!(transfer.subaddr_index, Index { major: 0, minor: data.1 });
// Make sure only one R was included in TX extra
assert!(Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref())
.unwrap()
.keys()
.unwrap()
.1
.is_none());
},
),
);
test!(
send_to_wallet_rpc_integrated,
(
|_, mut builder: Builder, _| async move {
// initialize rpc
let (wallet_rpc, _, _) = initialize_rpcs().await;
// make the addr
let mut payment_id = [0u8; 8];
OsRng.fill_bytes(&mut payment_id);
let addr = make_integrated_address(payment_id).await;
builder.add_payment(MoneroAddress::from_str(Network::Mainnet, &addr).unwrap(), 1000000);
(builder.build().unwrap(), (wallet_rpc, payment_id))
},
|_, tx: Transaction, _, data: (WalletClient, [u8; 8])| async move {
// confirm receipt
data.0.refresh(None).await.unwrap();
let transfer =
data.0.get_transfer(Hash::from_slice(&tx.hash()), None).await.unwrap().unwrap();
assert_eq!(transfer.amount.as_pico(), 1000000);
assert_eq!(transfer.subaddr_index, Index { major: 0, minor: 0 });
assert_eq!(transfer.payment_id.0, PaymentId::from_slice(&data.1));
},
),
);
test!(
send_to_wallet_rpc_with_arb_data,
(
|_, mut builder: Builder, _| async move {
// initialize rpc
let (wallet_rpc, _, wallet_rpc_addr) = initialize_rpcs().await;
// add destination
builder.add_payment(
MoneroAddress::from_str(Network::Mainnet, &wallet_rpc_addr.to_string()).unwrap(),
1000000,
);
// Make 2 data that is the full 255 bytes
for _ in 0 .. 2 {
// Subtract 1 since we prefix data with 127
let data = vec![b'a'; MAX_TX_EXTRA_NONCE_SIZE - 1];
assert!(builder.add_data(data).is_ok());
}
(builder.build().unwrap(), (wallet_rpc,))
},
|_, tx: Transaction, _, data: (WalletClient,)| async move {
// confirm receipt
data.0.refresh(None).await.unwrap();
let transfer =
data.0.get_transfer(Hash::from_slice(&tx.hash()), None).await.unwrap().unwrap();
assert_eq!(transfer.amount.as_pico(), 1000000);
assert_eq!(transfer.subaddr_index, Index { major: 0, minor: 0 });
},
),
);