mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-12 22:19:26 +00:00
Diversify ViewPair/Scanner into ViewPair/GuaranteedViewPair and Scanner/GuaranteedScanner
Also cleans the Scanner impl.
This commit is contained in:
@@ -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]);
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user