Diversify ViewPair/Scanner into ViewPair/GuaranteedViewPair and Scanner/GuaranteedScanner

Also cleans the Scanner impl.
This commit is contained in:
Luke Parker
2024-07-03 13:35:19 -04:00
parent 64e74c52ec
commit daa0f8f7d5
32 changed files with 1458 additions and 1233 deletions

View File

@@ -15,8 +15,10 @@ test!(
builder.add_payment(addr, 5);
(builder.build().unwrap(), (arbitrary_data,))
},
|_, tx: Transaction, mut scanner: Scanner, data: (Vec<u8>,)| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
|rpc, block, tx: Transaction, mut scanner: Scanner, data: (Vec<u8>,)| async move {
let output =
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.arbitrary_data()[0], data.0);
},
@@ -40,8 +42,10 @@ test!(
builder.add_payment(addr, 5);
(builder.build().unwrap(), data)
},
|_, tx: Transaction, mut scanner: Scanner, data: Vec<Vec<u8>>| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
|rpc, block, tx: Transaction, mut scanner: Scanner, data: Vec<Vec<u8>>| async move {
let output =
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.arbitrary_data(), data);
},
@@ -66,8 +70,10 @@ test!(
builder.add_payment(addr, 5);
(builder.build().unwrap(), data)
},
|_, tx: Transaction, mut scanner: Scanner, data: Vec<u8>| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
|rpc, block, tx: Transaction, mut scanner: Scanner, data: Vec<u8>| async move {
let output =
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.arbitrary_data(), vec![data]);
},

View File

@@ -3,7 +3,7 @@ use monero_wallet::{
DEFAULT_LOCK_WINDOW,
transaction::Transaction,
rpc::{OutputResponse, Rpc},
scan::SpendableOutput,
WalletOutput,
};
mod runner;
@@ -16,16 +16,18 @@ test!(
builder.add_payment(addr, 2000000000000);
(builder.build().unwrap(), ())
},
|rpc: SimpleRequestRpc, tx: Transaction, mut scanner: Scanner, ()| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let output =
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 2000000000000);
SpendableOutput::from(&rpc, output).await.unwrap()
output
},
),
(
// Then make a second tx1
|rct_type: RctType, rpc: SimpleRequestRpc, mut builder: Builder, addr, state: _| async move {
let output_tx0: SpendableOutput = state;
let output_tx0: WalletOutput = state;
let decoys = Decoys::fingerprintable_canonical_select(
&mut OsRng,
&rpc,
@@ -43,25 +45,26 @@ test!(
(builder.build().unwrap(), (rct_type, output_tx0))
},
// Then make sure DSA selects freshly unlocked output from tx1 as a decoy
|rpc: SimpleRequestRpc, tx: Transaction, mut scanner: Scanner, state: (_, _)| async move {
|rpc, block, tx: Transaction, mut scanner: Scanner, state: (_, _)| async move {
use rand_core::OsRng;
let rpc: SimpleRequestRpc = rpc;
let height = rpc.get_height().await.unwrap();
let output_tx1 =
SpendableOutput::from(&rpc, scanner.scan_transaction(&tx).not_locked().swap_remove(0))
.await
.unwrap();
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output_tx1.transaction(), tx.hash());
// Make sure output from tx1 is in the block in which it unlocks
let out_tx1: OutputResponse =
rpc.get_outs(&[output_tx1.global_index]).await.unwrap().swap_remove(0);
rpc.get_outs(&[output_tx1.index_on_blockchain()]).await.unwrap().swap_remove(0);
assert_eq!(out_tx1.height, height - DEFAULT_LOCK_WINDOW);
assert!(out_tx1.unlocked);
// Select decoys using spendable output from tx0 as the real, and make sure DSA selects
// the freshly unlocked output from tx1 as a decoy
let (rct_type, output_tx0): (RctType, SpendableOutput) = state;
let (rct_type, output_tx0): (RctType, WalletOutput) = state;
let mut selected_fresh_decoy = false;
let mut attempts = 1000;
while !selected_fresh_decoy && attempts > 0 {
@@ -75,7 +78,7 @@ test!(
.await
.unwrap();
selected_fresh_decoy = decoys[0].positions().contains(&output_tx1.global_index);
selected_fresh_decoy = decoys[0].positions().contains(&output_tx1.index_on_blockchain());
attempts -= 1;
}
@@ -93,16 +96,18 @@ test!(
builder.add_payment(addr, 2000000000000);
(builder.build().unwrap(), ())
},
|rpc: SimpleRequestRpc, tx: Transaction, mut scanner: Scanner, ()| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
|rpc: SimpleRequestRpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let output =
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 2000000000000);
SpendableOutput::from(&rpc, output).await.unwrap()
},
),
(
// Then make a second tx1
|rct_type: RctType, rpc: SimpleRequestRpc, mut builder: Builder, addr, state: _| async move {
let output_tx0: SpendableOutput = state;
|rct_type: RctType, rpc, mut builder: Builder, addr, output_tx0: WalletOutput| async move {
let rpc: SimpleRequestRpc = rpc;
let decoys = Decoys::select(
&mut OsRng,
&rpc,
@@ -120,25 +125,26 @@ test!(
(builder.build().unwrap(), (rct_type, output_tx0))
},
// Then make sure DSA selects freshly unlocked output from tx1 as a decoy
|rpc: SimpleRequestRpc, tx: Transaction, mut scanner: Scanner, state: (_, _)| async move {
|rpc, block, tx: Transaction, mut scanner: Scanner, state: (_, _)| async move {
use rand_core::OsRng;
let rpc: SimpleRequestRpc = rpc;
let height = rpc.get_height().await.unwrap();
let output_tx1 =
SpendableOutput::from(&rpc, scanner.scan_transaction(&tx).not_locked().swap_remove(0))
.await
.unwrap();
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output_tx1.transaction(), tx.hash());
// Make sure output from tx1 is in the block in which it unlocks
let out_tx1: OutputResponse =
rpc.get_outs(&[output_tx1.global_index]).await.unwrap().swap_remove(0);
rpc.get_outs(&[output_tx1.index_on_blockchain()]).await.unwrap().swap_remove(0);
assert_eq!(out_tx1.height, height - DEFAULT_LOCK_WINDOW);
assert!(out_tx1.unlocked);
// Select decoys using spendable output from tx0 as the real, and make sure DSA selects
// the freshly unlocked output from tx1 as a decoy
let (rct_type, output_tx0): (RctType, SpendableOutput) = state;
let (rct_type, output_tx0): (RctType, WalletOutput) = state;
let mut selected_fresh_decoy = false;
let mut attempts = 1000;
while !selected_fresh_decoy && attempts > 0 {
@@ -152,7 +158,7 @@ test!(
.await
.unwrap();
selected_fresh_decoy = decoys[0].positions().contains(&output_tx1.global_index);
selected_fresh_decoy = decoys[0].positions().contains(&output_tx1.index_on_blockchain());
attempts -= 1;
}

View File

@@ -21,7 +21,8 @@ test!(
AddressType::Legacy,
ED25519_BASEPOINT_POINT,
ED25519_BASEPOINT_POINT,
),
)
.unwrap(),
1,
);
builder.add_payment(
@@ -30,7 +31,8 @@ test!(
AddressType::LegacyIntegrated([0xaa; 8]),
ED25519_BASEPOINT_POINT,
ED25519_BASEPOINT_POINT,
),
)
.unwrap(),
2,
);
builder.add_payment(
@@ -39,7 +41,8 @@ test!(
AddressType::Subaddress,
ED25519_BASEPOINT_POINT,
ED25519_BASEPOINT_POINT,
),
)
.unwrap(),
3,
);
builder.add_payment(
@@ -48,7 +51,8 @@ test!(
AddressType::Featured { subaddress: false, payment_id: None, guaranteed: true },
ED25519_BASEPOINT_POINT,
ED25519_BASEPOINT_POINT,
),
)
.unwrap(),
4,
);
let tx = builder.build().unwrap();
@@ -59,7 +63,7 @@ test!(
);
(tx, eventuality)
},
|_, mut tx: Transaction, _, eventuality: Eventuality| async move {
|_, _, mut tx: Transaction, _, eventuality: Eventuality| async move {
// 4 explicitly outputs added and one change output
assert_eq!(tx.prefix().outputs.len(), 5);

View File

@@ -5,7 +5,7 @@ use monero_wallet::{
ringct::RctType,
rpc::FeeRate,
address::MoneroAddress,
scan::SpendableOutput,
WalletOutput,
send::{Change, SendError, SignableTransaction},
extra::MAX_ARBITRARY_DATA_SIZE,
};
@@ -15,7 +15,7 @@ use monero_wallet::{
pub struct SignableTransactionBuilder {
rct_type: RctType,
outgoing_view_key: Zeroizing<[u8; 32]>,
inputs: Vec<(SpendableOutput, Decoys)>,
inputs: Vec<(WalletOutput, Decoys)>,
payments: Vec<(MoneroAddress, u64)>,
change: Change,
data: Vec<Vec<u8>>,
@@ -40,12 +40,12 @@ impl SignableTransactionBuilder {
}
}
pub fn add_input(&mut self, input: (SpendableOutput, Decoys)) -> &mut Self {
pub fn add_input(&mut self, input: (WalletOutput, Decoys)) -> &mut Self {
self.inputs.push(input);
self
}
#[allow(unused)]
pub fn add_inputs(&mut self, inputs: &[(SpendableOutput, Decoys)]) -> &mut Self {
pub fn add_inputs(&mut self, inputs: &[(WalletOutput, Decoys)]) -> &mut Self {
self.inputs.extend(inputs.iter().cloned());
self
}

View File

@@ -1,5 +1,5 @@
use core::ops::Deref;
use std_shims::{sync::OnceLock, collections::HashSet};
use std_shims::sync::OnceLock;
use zeroize::Zeroizing;
use rand_core::OsRng;
@@ -12,10 +12,10 @@ use monero_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::{
ringct::RctType,
transaction::Transaction,
block::Block,
rpc::{Rpc, FeeRate},
ViewPair,
address::{Network, AddressType, AddressSpec, MoneroAddress},
scan::{SpendableOutput, Scanner},
address::{Network, AddressType, MoneroAddress},
ViewPair, GuaranteedViewPair, WalletOutput, Scanner,
};
mod builder;
@@ -41,20 +41,43 @@ pub fn random_address() -> (Scalar, ViewPair, MoneroAddress) {
AddressType::Legacy,
spend_pub,
view.deref() * ED25519_BASEPOINT_TABLE,
),
)
.unwrap(),
)
}
#[allow(unused)]
pub fn random_guaranteed_address() -> (Scalar, GuaranteedViewPair, MoneroAddress) {
let spend = Scalar::random(&mut OsRng);
let spend_pub = &spend * ED25519_BASEPOINT_TABLE;
let view = Zeroizing::new(Scalar::random(&mut OsRng));
(
spend,
GuaranteedViewPair::new(spend_pub, view.clone()).unwrap(),
MoneroAddress::new(
Network::Mainnet,
AddressType::Legacy,
spend_pub,
view.deref() * ED25519_BASEPOINT_TABLE,
)
.unwrap(),
)
}
// TODO: Support transactions already on-chain
// TODO: Don't have a side effect of mining blocks more blocks than needed under race conditions
pub async fn mine_until_unlocked(rpc: &SimpleRequestRpc, addr: &str, tx_hash: [u8; 32]) {
pub async fn mine_until_unlocked(rpc: &SimpleRequestRpc, addr: &str, tx_hash: [u8; 32]) -> Block {
// mine until tx is in a block
let mut height = rpc.get_height().await.unwrap();
let mut found = false;
let mut block = None;
while !found {
let block = rpc.get_block_by_number(height - 1).await.unwrap();
found = match block.txs.iter().find(|&&x| x == tx_hash) {
Some(_) => true,
let inner_block = rpc.get_block_by_number(height - 1).await.unwrap();
found = match inner_block.transactions.iter().find(|&&x| x == tx_hash) {
Some(_) => {
block = Some(inner_block);
true
}
None => {
height = rpc.generate_blocks(addr, 1).await.unwrap().1 + 1;
false
@@ -73,22 +96,21 @@ pub async fn mine_until_unlocked(rpc: &SimpleRequestRpc, addr: &str, tx_hash: [u
{
height = rpc.generate_blocks(addr, 1).await.unwrap().1 + 1;
}
block.unwrap()
}
// Mines 60 blocks and returns an unlocked miner TX output.
#[allow(dead_code)]
pub async fn get_miner_tx_output(rpc: &SimpleRequestRpc, view: &ViewPair) -> SpendableOutput {
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
pub async fn get_miner_tx_output(rpc: &SimpleRequestRpc, view: &ViewPair) -> WalletOutput {
let mut scanner = Scanner::new(view.clone());
// Mine 60 blocks to unlock a miner TX
let start = rpc.get_height().await.unwrap();
rpc
.generate_blocks(&view.address(Network::Mainnet, AddressSpec::Legacy).to_string(), 60)
.await
.unwrap();
rpc.generate_blocks(&view.legacy_address(Network::Mainnet).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)
scanner.scan(rpc, &block).await.unwrap().ignore_additional_timelock().swap_remove(0)
}
/// Make sure the weight and fee match the expected calculation.
@@ -119,6 +141,7 @@ pub async fn rpc() -> SimpleRequestRpc {
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
)
.unwrap()
.to_string();
// Mine 40 blocks to ensure decoy availability
@@ -164,7 +187,6 @@ macro_rules! test {
async_sequential! {
async fn $name() {
use core::{ops::Deref, any::Any};
use std::collections::HashSet;
#[cfg(feature = "multisig")]
use std::collections::HashMap;
@@ -184,10 +206,10 @@ macro_rules! test {
primitives::Decoys,
ringct::RctType,
rpc::FeePriority,
address::{Network, AddressSpec},
address::Network,
ViewPair,
DecoySelection,
scan::Scanner,
Scanner,
send::{Change, SignableTransaction},
};
@@ -226,7 +248,7 @@ macro_rules! test {
let mut outgoing_view = Zeroizing::new([0; 32]);
OsRng.fill_bytes(outgoing_view.as_mut());
let view = ViewPair::new(spend_pub, view_priv.clone());
let addr = view.address(Network::Mainnet, AddressSpec::Legacy);
let addr = view.legacy_address(Network::Mainnet);
let miner_tx = get_miner_tx_output(&rpc, &view).await;
@@ -244,7 +266,6 @@ macro_rules! test {
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
Zeroizing::new(Scalar::random(&mut OsRng))
),
false
),
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
);
@@ -293,12 +314,12 @@ macro_rules! test {
let fee_rate = tx.fee_rate().clone();
let signed = sign(tx).await;
rpc.publish_transaction(&signed).await.unwrap();
mine_until_unlocked(&rpc, &random_address().2.to_string(), signed.hash()).await;
let block =
mine_until_unlocked(&rpc, &random_address().2.to_string(), signed.hash()).await;
let tx = rpc.get_transaction(signed.hash()).await.unwrap();
check_weight_and_fee(&tx, fee_rate);
let scanner =
Scanner::from_view(view.clone(), Some(HashSet::new()));
($first_checks)(rpc.clone(), tx, scanner, state).await
let scanner = Scanner::new(view.clone());
($first_checks)(rpc.clone(), block, tx, scanner, state).await
});
#[allow(unused_variables, unused_mut, unused_assignments)]
let mut carried_state: Box<dyn Any> = temp;
@@ -314,7 +335,8 @@ macro_rules! test {
let fee_rate = tx.fee_rate().clone();
let signed = sign(tx).await;
rpc.publish_transaction(&signed).await.unwrap();
mine_until_unlocked(&rpc, &random_address().2.to_string(), signed.hash()).await;
let block =
mine_until_unlocked(&rpc, &random_address().2.to_string(), signed.hash()).await;
let tx = rpc.get_transaction(signed.hash()).await.unwrap();
if stringify!($name) != "spend_one_input_to_two_outputs_no_change" {
// Skip weight and fee check for the above test because when there is no change,
@@ -323,10 +345,8 @@ macro_rules! test {
}
#[allow(unused_assignments)]
{
let scanner =
Scanner::from_view(view.clone(), Some(HashSet::new()));
carried_state =
Box::new(($checks)(rpc.clone(), tx, scanner, state).await);
let scanner = Scanner::new(view.clone());
carried_state = Box::new(($checks)(rpc.clone(), block, tx, scanner, state).await);
}
)*
}

View File

@@ -1,5 +1,5 @@
use monero_serai::transaction::Transaction;
use monero_wallet::{rpc::Rpc, address::SubaddressIndex, extra::PaymentId};
use monero_wallet::{rpc::Rpc, address::SubaddressIndex, extra::PaymentId, GuaranteedScanner};
mod runner;
@@ -8,15 +8,16 @@ test!(
(
|_, 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::Legacy), 5);
let scanner = Scanner::new(view.clone());
builder.add_payment(view.legacy_address(Network::Mainnet), 5);
(builder.build().unwrap(), scanner)
},
|_, tx: Transaction, _, mut state: Scanner| async move {
let output = state.scan_transaction(&tx).not_locked().swap_remove(0);
|rpc, block, tx: Transaction, _, mut state: Scanner| async move {
let output = state.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
let dummy_payment_id = PaymentId::Encrypted([0u8; 8]);
assert_eq!(output.metadata.payment_id, Some(dummy_payment_id));
assert_eq!(output.payment_id(), Some(dummy_payment_id));
},
),
);
@@ -28,16 +29,18 @@ test!(
let subaddress = SubaddressIndex::new(0, 1).unwrap();
let view = runner::random_address().1;
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
let mut scanner = Scanner::new(view.clone());
scanner.register_subaddress(subaddress);
builder.add_payment(view.address(Network::Mainnet, AddressSpec::Subaddress(subaddress)), 5);
builder.add_payment(view.subaddress(Network::Mainnet, 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);
|rpc, block, tx: Transaction, _, mut state: (Scanner, SubaddressIndex)| async move {
let output =
state.0.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.subaddress, Some(state.1));
assert_eq!(output.subaddress(), Some(state.1));
},
),
);
@@ -47,160 +50,43 @@ test!(
(
|_, mut builder: Builder, _| async move {
let view = runner::random_address().1;
let scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
let scanner = Scanner::new(view.clone());
let mut payment_id = [0u8; 8];
OsRng.fill_bytes(&mut payment_id);
builder
.add_payment(view.address(Network::Mainnet, AddressSpec::LegacyIntegrated(payment_id)), 5);
builder.add_payment(view.legacy_integrated_address(Network::Mainnet, 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);
|rpc, block, tx: Transaction, _, mut state: (Scanner, [u8; 8])| async move {
let output =
state.0.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.payment_id, Some(PaymentId::Encrypted(state.1)));
assert_eq!(output.payment_id(), Some(PaymentId::Encrypted(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,
scan_guaranteed,
(
|_, 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()));
let view = runner::random_guaranteed_address().1;
let mut scanner = GuaranteedScanner::new(view.clone());
scanner.register_subaddress(subaddress);
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured {
subaddress: Some(subaddress),
payment_id: None,
guaranteed: false,
},
),
5,
);
builder.add_payment(view.address(Network::Mainnet, None, None), 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, Some(PaymentId::Encrypted(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, Some(PaymentId::Encrypted(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);
|rpc, block, tx: Transaction, _, mut state: (GuaranteedScanner, SubaddressIndex)| async move {
let output =
state.0.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.subaddress(), Some(state.1));
},
),
);
@@ -209,29 +95,21 @@ test!(
scan_guaranteed_subaddress,
(
|_, mut builder: Builder, _| async move {
let subaddress = SubaddressIndex::new(1, 0).unwrap();
let subaddress = SubaddressIndex::new(0, 2).unwrap();
let view = runner::random_address().1;
let mut scanner = Scanner::from_view(view.clone(), None);
let view = runner::random_guaranteed_address().1;
let mut scanner = GuaranteedScanner::new(view.clone());
scanner.register_subaddress(subaddress);
builder.add_payment(
view.address(
Network::Mainnet,
AddressSpec::Featured {
subaddress: Some(subaddress),
payment_id: None,
guaranteed: true,
},
),
5,
);
builder.add_payment(view.address(Network::Mainnet, Some(subaddress), None), 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);
|rpc, block, tx: Transaction, _, mut state: (GuaranteedScanner, SubaddressIndex)| async move {
let output =
state.0.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.subaddress, Some(state.1));
assert_eq!(output.subaddress(), Some(state.1));
},
),
);
@@ -240,63 +118,53 @@ test!(
scan_guaranteed_integrated,
(
|_, mut builder: Builder, _| async move {
let view = runner::random_address().1;
let scanner = Scanner::from_view(view.clone(), None);
let view = runner::random_guaranteed_address().1;
let scanner = GuaranteedScanner::new(view.clone());
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.add_payment(view.address(Network::Mainnet, None, Some(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);
|rpc, block, tx: Transaction, _, mut state: (GuaranteedScanner, [u8; 8])| async move {
let output =
state.0.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.payment_id, Some(PaymentId::Encrypted(state.1)));
assert_eq!(output.payment_id(), Some(PaymentId::Encrypted(state.1)));
},
),
);
#[rustfmt::skip]
test!(
scan_guaranteed_integrated_subaddress,
(
|_, mut builder: Builder, _| async move {
let subaddress = SubaddressIndex::new(1, 1).unwrap();
let subaddress = SubaddressIndex::new(0, 3).unwrap();
let view = runner::random_address().1;
let mut scanner = Scanner::from_view(view.clone(), None);
let view = runner::random_guaranteed_address().1;
let mut scanner = GuaranteedScanner::new(view.clone());
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.add_payment(view.address(Network::Mainnet, Some(subaddress), Some(payment_id)), 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);
|
rpc,
block,
tx: Transaction,
_,
mut state: (GuaranteedScanner, [u8; 8], SubaddressIndex),
| async move {
let output = state.0.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
assert_eq!(output.metadata.payment_id, Some(PaymentId::Encrypted(state.1)));
assert_eq!(output.metadata.subaddress, Some(state.2));
assert_eq!(output.payment_id(), Some(PaymentId::Encrypted(state.1)));
assert_eq!(output.subaddress(), Some(state.2));
},
),
);

View File

@@ -1,15 +1,11 @@
use std::collections::HashSet;
use rand_core::OsRng;
use monero_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::{
primitives::Decoys,
ringct::RctType,
transaction::Transaction,
rpc::Rpc,
address::SubaddressIndex,
extra::Extra,
scan::{ReceivedOutput, SpendableOutput},
DecoySelection,
primitives::Decoys, ringct::RctType, transaction::Transaction, rpc::Rpc,
address::SubaddressIndex, extra::Extra, WalletOutput, DecoySelection,
};
mod runner;
@@ -19,25 +15,20 @@ use runner::{SignableTransactionBuilder, ring_len};
async fn add_inputs(
rct_type: RctType,
rpc: &SimpleRequestRpc,
outputs: Vec<ReceivedOutput>,
outputs: Vec<WalletOutput>,
builder: &mut SignableTransactionBuilder,
) {
let mut spendable_outputs = Vec::with_capacity(outputs.len());
for output in outputs {
spendable_outputs.push(SpendableOutput::from(rpc, output).await.unwrap());
}
let decoys = Decoys::fingerprintable_canonical_select(
&mut OsRng,
rpc,
ring_len(rct_type),
rpc.get_height().await.unwrap(),
&spendable_outputs,
&outputs,
)
.await
.unwrap();
let inputs = spendable_outputs.into_iter().zip(decoys).collect::<Vec<_>>();
let inputs = outputs.into_iter().zip(decoys).collect::<Vec<_>>();
builder.add_inputs(&inputs);
}
@@ -49,8 +40,10 @@ test!(
builder.add_payment(addr, 5);
(builder.build().unwrap(), ())
},
|_, tx: Transaction, mut scanner: Scanner, ()| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let output =
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 5);
},
),
@@ -64,8 +57,11 @@ test!(
builder.add_payment(addr, 2000000000000);
(builder.build().unwrap(), ())
},
|_, tx: Transaction, mut scanner: Scanner, ()| async move {
let mut outputs = scanner.scan_transaction(&tx).not_locked();
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let mut outputs = scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked();
assert_eq!(outputs.len(), 2);
assert_eq!(outputs[0].transaction(), tx.hash());
assert_eq!(outputs[0].transaction(), tx.hash());
outputs.sort_by(|x, y| x.commitment().amount.cmp(&y.commitment().amount));
assert_eq!(outputs[0].commitment().amount, 1000000000000);
assert_eq!(outputs[1].commitment().amount, 2000000000000);
@@ -73,13 +69,15 @@ test!(
},
),
(
|rct_type: RctType, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|rct_type: RctType, rpc, mut builder: Builder, addr, outputs: Vec<WalletOutput>| async move {
add_inputs(rct_type, &rpc, outputs, &mut builder).await;
builder.add_payment(addr, 6);
(builder.build().unwrap(), ())
},
|_, tx: Transaction, mut scanner: Scanner, ()| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let output =
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 6);
},
),
@@ -95,15 +93,16 @@ test!(
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));
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let outputs = scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked();
assert_eq!(outputs.len(), 1);
assert_eq!(outputs[0].transaction(), tx.hash());
assert_eq!(outputs[0].commitment().amount, 1000000000000);
outputs
},
),
(
|rct_type, rpc: SimpleRequestRpc, _, _, outputs: Vec<ReceivedOutput>| async move {
|rct_type, rpc: SimpleRequestRpc, _, _, outputs: Vec<WalletOutput>| async move {
use monero_wallet::rpc::FeePriority;
let view_priv = Zeroizing::new(Scalar::random(&mut OsRng));
@@ -115,7 +114,7 @@ test!(
let mut builder = SignableTransactionBuilder::new(
rct_type,
outgoing_view,
Change::new(&change_view, false),
Change::new(&change_view),
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
);
add_inputs(rct_type, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
@@ -125,23 +124,23 @@ test!(
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
Zeroizing::new(Scalar::random(&mut OsRng)),
);
builder.add_payment(
sub_view
.address(Network::Mainnet, AddressSpec::Subaddress(SubaddressIndex::new(0, 1).unwrap())),
1,
);
builder
.add_payment(sub_view.subaddress(Network::Mainnet, SubaddressIndex::new(0, 1).unwrap()), 1);
(builder.build().unwrap(), (change_view, sub_view))
},
|_, tx: Transaction, _, views: (ViewPair, ViewPair)| async move {
|rpc, block, 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);
let mut change_scanner = Scanner::new(views.0);
assert!(
change_scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().len() == 1
);
// Make sure the subaddress can pick up its output
let mut sub_scanner = Scanner::from_view(views.1, Some(HashSet::new()));
let mut sub_scanner = Scanner::new(views.1);
sub_scanner.register_subaddress(SubaddressIndex::new(0, 1).unwrap());
let sub_outputs = sub_scanner.scan_transaction(&tx).not_locked();
let sub_outputs = sub_scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked();
assert!(sub_outputs.len() == 1);
assert_eq!(sub_outputs[0].transaction(), tx.hash());
assert_eq!(sub_outputs[0].commitment().amount, 1);
// Make sure only one R was included in TX extra
@@ -162,21 +161,24 @@ test!(
builder.add_payment(addr, 2000000000000);
(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));
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let outputs = scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked();
assert_eq!(outputs.len(), 1);
assert_eq!(outputs[0].transaction(), tx.hash());
assert_eq!(outputs[0].commitment().amount, 2000000000000);
outputs
},
),
(
|rct_type: RctType, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|rct_type: RctType, rpc, mut builder: Builder, addr, outputs: Vec<WalletOutput>| async move {
add_inputs(rct_type, &rpc, outputs, &mut builder).await;
builder.add_payment(addr, 2);
(builder.build().unwrap(), ())
},
|_, tx: Transaction, mut scanner: Scanner, ()| async move {
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let output =
scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
assert_eq!(output.commitment().amount, 2);
},
),
@@ -189,15 +191,16 @@ test!(
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));
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let outputs = scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked();
assert_eq!(outputs.len(), 1);
assert_eq!(outputs[0].transaction(), tx.hash());
assert_eq!(outputs[0].commitment().amount, 1000000000000);
outputs
},
),
(
|rct_type: RctType, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|rct_type: RctType, rpc, mut builder: Builder, addr, outputs: Vec<WalletOutput>| async move {
add_inputs(rct_type, &rpc, outputs, &mut builder).await;
for i in 0 .. 15 {
@@ -205,8 +208,8 @@ test!(
}
(builder.build().unwrap(), ())
},
|_, tx: Transaction, mut scanner: Scanner, ()| async move {
let mut scanned_tx = scanner.scan_transaction(&tx).not_locked();
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let mut scanned_tx = scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked();
let mut output_amounts = HashSet::new();
for i in 0 .. 15 {
@@ -214,10 +217,11 @@ test!(
}
for _ in 0 .. 15 {
let output = scanned_tx.swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
let amount = output.commitment().amount;
assert!(output_amounts.contains(&amount));
output_amounts.remove(&amount);
assert!(output_amounts.remove(&amount));
}
assert_eq!(output_amounts.len(), 0);
},
),
);
@@ -229,38 +233,36 @@ test!(
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));
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let outputs = scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked();
assert_eq!(outputs.len(), 1);
assert_eq!(outputs[0].transaction(), tx.hash());
assert_eq!(outputs[0].commitment().amount, 1000000000000);
outputs
},
),
(
|rct_type: RctType, rpc, mut builder: Builder, _, outputs: Vec<ReceivedOutput>| async move {
|rct_type: RctType, rpc, mut builder: Builder, _, outputs: Vec<WalletOutput>| async move {
add_inputs(rct_type, &rpc, outputs, &mut builder).await;
let view = runner::random_address().1;
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
let mut scanner = Scanner::new(view.clone());
let mut subaddresses = vec![];
for i in 0 .. 15 {
let subaddress = SubaddressIndex::new(0, i + 1).unwrap();
scanner.register_subaddress(subaddress);
builder.add_payment(
view.address(Network::Mainnet, AddressSpec::Subaddress(subaddress)),
u64::from(i + 1),
);
builder.add_payment(view.subaddress(Network::Mainnet, subaddress), u64::from(i + 1));
subaddresses.push(subaddress);
}
(builder.build().unwrap(), (scanner, subaddresses))
},
|_, tx: Transaction, _, mut state: (Scanner, Vec<SubaddressIndex>)| async move {
|rpc, block, tx: Transaction, _, mut state: (Scanner, Vec<SubaddressIndex>)| async move {
use std::collections::HashMap;
let mut scanned_tx = state.0.scan_transaction(&tx).not_locked();
let mut scanned_tx = state.0.scan(&rpc, &block).await.unwrap().not_additionally_locked();
let mut output_amounts_by_subaddress = HashMap::new();
for i in 0 .. 15 {
@@ -268,13 +270,15 @@ test!(
}
for _ in 0 .. 15 {
let output = scanned_tx.swap_remove(0);
assert_eq!(output.transaction(), tx.hash());
let amount = output.commitment().amount;
assert!(output_amounts_by_subaddress.contains_key(&amount));
assert_eq!(output.metadata.subaddress, Some(output_amounts_by_subaddress[&amount]));
output_amounts_by_subaddress.remove(&amount);
assert_eq!(
output.subaddress().unwrap(),
output_amounts_by_subaddress.remove(&amount).unwrap()
);
}
assert_eq!(output_amounts_by_subaddress.len(), 0);
},
),
);
@@ -286,15 +290,16 @@ test!(
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));
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let outputs = scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked();
assert_eq!(outputs.len(), 1);
assert_eq!(outputs[0].transaction(), tx.hash());
assert_eq!(outputs[0].commitment().amount, 1000000000000);
outputs
},
),
(
|rct_type, rpc: SimpleRequestRpc, _, addr, outputs: Vec<ReceivedOutput>| async move {
|rct_type, rpc: SimpleRequestRpc, _, addr, outputs: Vec<WalletOutput>| async move {
use monero_wallet::rpc::FeePriority;
let mut outgoing_view = Zeroizing::new([0; 32]);
@@ -311,8 +316,11 @@ test!(
(builder.build().unwrap(), ())
},
|_, tx: Transaction, mut scanner: Scanner, ()| async move {
let mut outputs = scanner.scan_transaction(&tx).not_locked();
|rpc, block, tx: Transaction, mut scanner: Scanner, ()| async move {
let mut outputs = scanner.scan(&rpc, &block).await.unwrap().not_additionally_locked();
assert_eq!(outputs.len(), 2);
assert_eq!(outputs[0].transaction(), tx.hash());
assert_eq!(outputs[1].transaction(), tx.hash());
outputs.sort_by(|x, y| x.commitment().amount.cmp(&y.commitment().amount));
assert_eq!(outputs[0].commitment().amount, 10000);
assert_eq!(outputs[1].commitment().amount, 50000);

View File

@@ -1,5 +1,3 @@
use std::collections::HashSet;
use rand_core::{OsRng, RngCore};
use serde::Deserialize;
@@ -9,13 +7,20 @@ use monero_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::{
transaction::Transaction,
rpc::Rpc,
address::{Network, AddressSpec, SubaddressIndex, MoneroAddress},
address::{Network, SubaddressIndex, MoneroAddress},
extra::{MAX_TX_EXTRA_NONCE_SIZE, Extra, PaymentId},
scan::Scanner,
Scanner,
};
mod runner;
#[derive(Clone, Copy, PartialEq, Eq)]
enum AddressSpec {
Legacy,
LegacyIntegrated([u8; 8]),
Subaddress(SubaddressIndex),
}
#[derive(Deserialize, Debug)]
struct EmptyResponse {}
@@ -70,7 +75,13 @@ async fn from_wallet_rpc_to_self(spec: AddressSpec) {
// make an addr
let (_, view_pair, _) = runner::random_address();
let addr = view_pair.address(Network::Mainnet, spec);
let addr = match spec {
AddressSpec::Legacy => view_pair.legacy_address(Network::Mainnet),
AddressSpec::LegacyIntegrated(payment_id) => {
view_pair.legacy_integrated_address(Network::Mainnet, payment_id)
}
AddressSpec::Subaddress(index) => view_pair.subaddress(Network::Mainnet, index),
};
// refresh & make a tx
let _: EmptyResponse = wallet_rpc.json_rpc_call("refresh", None).await.unwrap();
@@ -97,33 +108,34 @@ async fn from_wallet_rpc_to_self(spec: AddressSpec) {
// .unwrap();
// unlock it
runner::mine_until_unlocked(&daemon_rpc, &wallet_rpc_addr, tx_hash).await;
let block = runner::mine_until_unlocked(&daemon_rpc, &wallet_rpc_addr, tx_hash).await;
// Create the scanner
let mut scanner = Scanner::from_view(view_pair, Some(HashSet::new()));
let mut scanner = Scanner::new(view_pair);
if let AddressSpec::Subaddress(index) = spec {
scanner.register_subaddress(index);
}
// Retrieve it and scan it
let tx = daemon_rpc.get_transaction(tx_hash).await.unwrap();
let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0);
let output =
scanner.scan(&daemon_rpc, &block).await.unwrap().not_additionally_locked().swap_remove(0);
assert_eq!(output.transaction(), tx_hash);
// TODO: Needs https://github.com/monero-project/monero/pull/9260
// runner::check_weight_and_fee(&tx, fee_rate);
match spec {
AddressSpec::Subaddress(index) => {
assert_eq!(output.metadata.subaddress, Some(index));
assert_eq!(output.metadata.payment_id, Some(PaymentId::Encrypted([0u8; 8])));
assert_eq!(output.subaddress(), Some(index));
assert_eq!(output.payment_id(), Some(PaymentId::Encrypted([0u8; 8])));
}
AddressSpec::LegacyIntegrated(payment_id) => {
assert_eq!(output.metadata.payment_id, Some(PaymentId::Encrypted(payment_id)));
assert_eq!(output.metadata.subaddress, None);
assert_eq!(output.payment_id(), Some(PaymentId::Encrypted(payment_id)));
assert_eq!(output.subaddress(), None);
}
AddressSpec::Legacy | AddressSpec::Featured { .. } => {
assert_eq!(output.metadata.subaddress, None);
assert_eq!(output.metadata.payment_id, Some(PaymentId::Encrypted([0u8; 8])));
AddressSpec::Legacy => {
assert_eq!(output.subaddress(), None);
assert_eq!(output.payment_id(), Some(PaymentId::Encrypted([0u8; 8])));
}
}
assert_eq!(output.commitment().amount, 1000000000000);
@@ -176,7 +188,7 @@ test!(
.add_payment(MoneroAddress::from_str(Network::Mainnet, &wallet_rpc_addr).unwrap(), 1000000);
(builder.build().unwrap(), wallet_rpc)
},
|_, tx: Transaction, _, data: SimpleRequestRpc| async move {
|_, _, tx: Transaction, _, data: SimpleRequestRpc| async move {
// confirm receipt
let _: EmptyResponse = data.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data
@@ -210,7 +222,7 @@ test!(
.add_payment(MoneroAddress::from_str(Network::Mainnet, &addr.address).unwrap(), 1000000);
(builder.build().unwrap(), (wallet_rpc, addr.account_index))
},
|_, tx: Transaction, _, data: (SimpleRequestRpc, u32)| async move {
|_, _, tx: Transaction, _, data: (SimpleRequestRpc, u32)| async move {
// confirm receipt
let _: EmptyResponse = data.0.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data
@@ -262,7 +274,7 @@ test!(
]);
(builder.build().unwrap(), (wallet_rpc, daemon_rpc, addrs.address_index))
},
|_, tx: Transaction, _, data: (SimpleRequestRpc, SimpleRequestRpc, u32)| async move {
|_, _, tx: Transaction, _, data: (SimpleRequestRpc, SimpleRequestRpc, u32)| async move {
// confirm receipt
let _: EmptyResponse = data.0.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data
@@ -307,7 +319,7 @@ test!(
builder.add_payment(MoneroAddress::from_str(Network::Mainnet, &addr).unwrap(), 1000000);
(builder.build().unwrap(), (wallet_rpc, payment_id))
},
|_, tx: Transaction, _, data: (SimpleRequestRpc, [u8; 8])| async move {
|_, _, tx: Transaction, _, data: (SimpleRequestRpc, [u8; 8])| async move {
// confirm receipt
let _: EmptyResponse = data.0.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data
@@ -342,7 +354,7 @@ test!(
(builder.build().unwrap(), wallet_rpc)
},
|_, tx: Transaction, _, data: SimpleRequestRpc| async move {
|_, _, tx: Transaction, _, data: SimpleRequestRpc| async move {
// confirm receipt
let _: EmptyResponse = data.json_rpc_call("refresh", None).await.unwrap();
let transfer: TransfersResponse = data