diff --git a/processor/src/multisigs/db.rs b/processor/src/multisigs/db.rs index d7c30379..3356cd85 100644 --- a/processor/src/multisigs/db.rs +++ b/processor/src/multisigs/db.rs @@ -5,10 +5,9 @@ use ciphersuite::Ciphersuite; pub use serai_db::*; use scale::{Encode, Decode}; -#[rustfmt::skip] use serai_client::{ - primitives::ExternalAddress, - in_instructions::primitives::InInstructionWithBalance + primitives::{Balance, ExternalAddress}, + in_instructions::primitives::InInstructionWithBalance, }; use crate::{ @@ -172,23 +171,23 @@ impl MultisigsDb { res } - fn forwarded_output_key(amount: u64) -> Vec { - Self::multisigs_key(b"forwarded_output", amount.to_le_bytes()) + fn forwarded_output_key(balance: Balance) -> Vec { + Self::multisigs_key(b"forwarded_output", balance.encode()) } pub fn save_forwarded_output( txn: &mut D::Transaction<'_>, instruction: InInstructionWithBalance, ) { - let key = Self::forwarded_output_key(instruction.balance.amount.0); + let key = Self::forwarded_output_key(instruction.balance); let mut existing = txn.get(&key).unwrap_or(vec![]); existing.extend(instruction.encode()); txn.put(key, existing); } pub fn take_forwarded_output( txn: &mut D::Transaction<'_>, - amount: u64, + balance: Balance, ) -> Option { - let key = Self::forwarded_output_key(amount); + let key = Self::forwarded_output_key(balance); let outputs = txn.get(&key)?; let mut outputs_ref = outputs.as_slice(); diff --git a/processor/src/multisigs/mod.rs b/processor/src/multisigs/mod.rs index a9a694f9..9e244c6b 100644 --- a/processor/src/multisigs/mod.rs +++ b/processor/src/multisigs/mod.rs @@ -7,7 +7,7 @@ use scale::{Encode, Decode}; use messages::SubstrateContext; use serai_client::{ - primitives::{MAX_DATA_LEN, ExternalAddress, BlockHash}, + primitives::{MAX_DATA_LEN, NetworkId, Coin, ExternalAddress, BlockHash}, in_instructions::primitives::{ InInstructionWithBalance, Batch, RefundableInInstruction, Shorthand, MAX_BATCH_SIZE, }, @@ -157,7 +157,20 @@ impl MultisigManager { assert!(current_keys.len() <= 2); let mut actively_signing = vec![]; for (_, key) in ¤t_keys { - schedulers.push(Scheduler::from_db(raw_db, *key).unwrap()); + schedulers.push( + Scheduler::from_db( + raw_db, + *key, + match N::NETWORK { + NetworkId::Serai => panic!("adding a key for Serai"), + NetworkId::Bitcoin => Coin::Bitcoin, + // TODO: This is incomplete to DAI + NetworkId::Ethereum => Coin::Ether, + NetworkId::Monero => Coin::Monero, + }, + ) + .unwrap(), + ); // Load any TXs being actively signed let key = key.to_bytes(); @@ -234,7 +247,17 @@ impl MultisigManager { let viewer = Some(MultisigViewer { activation_block, key: external_key, - scheduler: Scheduler::::new::(txn, external_key), + scheduler: Scheduler::::new::( + txn, + external_key, + match N::NETWORK { + NetworkId::Serai => panic!("adding a key for Serai"), + NetworkId::Bitcoin => Coin::Bitcoin, + // TODO: This is incomplete to DAI + NetworkId::Ethereum => Coin::Ether, + NetworkId::Monero => Coin::Monero, + }, + ), }); if self.existing.is_none() { @@ -295,12 +318,7 @@ impl MultisigManager { assert_eq!(balance.coin.network(), N::NETWORK); if let Ok(address) = N::Address::try_from(address.consume()) { - // TODO: Add coin to payment - payments.push(Payment { - address, - data: data.map(|data| data.consume()), - amount: balance.amount.0, - }); + payments.push(Payment { address, data: data.map(|data| data.consume()), balance }); } } @@ -344,7 +362,7 @@ impl MultisigManager { inputs: vec![output.clone()], // Uses a payment as this will still be successfully sent due to fee amortization, // and because change is currently always a Serai key - payments: vec![Payment { address: refund_to, data: None, amount: output.balance().amount.0 }], + payments: vec![Payment { address: refund_to, data: None, balance: output.balance() }], change: None, } } @@ -542,7 +560,7 @@ impl MultisigManager { // // This is unnecessary, due to the current flow around Eventuality resolutions and the // current bounds naturally found being sufficiently amenable, yet notable for the future - if scheduler.can_use_branch(output.amount()) { + if scheduler.can_use_branch(output.balance()) { // We could simply call can_use_branch, yet it'd have an edge case where if we receive // two outputs for 100, and we could use one such output, we'd handle both. // @@ -852,7 +870,7 @@ impl MultisigManager { } if let Some(instruction) = - MultisigsDb::::take_forwarded_output(txn, output.amount()) + MultisigsDb::::take_forwarded_output(txn, output.balance()) { instruction } else { diff --git a/processor/src/multisigs/scanner.rs b/processor/src/multisigs/scanner.rs index caaa9c4b..6908be3f 100644 --- a/processor/src/multisigs/scanner.rs +++ b/processor/src/multisigs/scanner.rs @@ -553,7 +553,7 @@ impl Scanner { // TODO: These lines are the ones which will cause a really long-lived lock acquisiton for output in network.get_outputs(&block, key).await { assert_eq!(output.key(), key); - if output.amount() >= N::DUST { + if output.balance().amount.0 >= N::DUST { outputs.push(output); } } @@ -580,10 +580,10 @@ impl Scanner { for output in &outputs { let id = output.id(); info!( - "block {} had output {} worth {}", + "block {} had output {} worth {:?}", hex::encode(&block_id), hex::encode(&id), - output.amount(), + output.balance(), ); // On Bitcoin, the output ID should be unique for a given chain diff --git a/processor/src/multisigs/scheduler.rs b/processor/src/multisigs/scheduler.rs index ac45ef76..4a7d980d 100644 --- a/processor/src/multisigs/scheduler.rs +++ b/processor/src/multisigs/scheduler.rs @@ -5,6 +5,8 @@ use std::{ use ciphersuite::{group::GroupEncoding, Ciphersuite}; +use serai_client::primitives::{Coin, Amount, Balance}; + use crate::{ networks::{OutputType, Output, Network}, DbTxn, Db, Payment, Plan, @@ -14,6 +16,7 @@ use crate::{ #[derive(PartialEq, Eq, Debug)] pub struct Scheduler { key: ::G, + coin: Coin, // Serai, when it has more outputs expected than it can handle in a single tranaction, will // schedule the outputs to be handled later. Immediately, it just creates additional outputs @@ -51,7 +54,11 @@ impl Scheduler { self.payments.is_empty() } - fn read(key: ::G, reader: &mut R) -> io::Result { + fn read( + key: ::G, + coin: Coin, + reader: &mut R, + ) -> io::Result { let mut read_plans = || -> io::Result<_> { let mut all_plans = HashMap::new(); let mut all_plans_len = [0; 4]; @@ -95,7 +102,7 @@ impl Scheduler { payments.push_back(Payment::read(reader)?); } - Ok(Scheduler { key, queued_plans, plans, utxos, payments }) + Ok(Scheduler { key, coin, queued_plans, plans, utxos, payments }) } // TODO2: Get rid of this @@ -130,13 +137,18 @@ impl Scheduler { payment.write(&mut res).unwrap(); } - debug_assert_eq!(&Self::read(self.key, &mut res.as_slice()).unwrap(), self); + debug_assert_eq!(&Self::read(self.key, self.coin, &mut res.as_slice()).unwrap(), self); res } - pub fn new(txn: &mut D::Transaction<'_>, key: ::G) -> Self { + pub fn new( + txn: &mut D::Transaction<'_>, + key: ::G, + coin: Coin, + ) -> Self { let res = Scheduler { key, + coin, queued_plans: HashMap::new(), plans: HashMap::new(), utxos: vec![], @@ -147,18 +159,19 @@ impl Scheduler { res } - pub fn from_db(db: &D, key: ::G) -> io::Result { + pub fn from_db(db: &D, key: ::G, coin: Coin) -> io::Result { let scheduler = db.get(scheduler_key::(&key)).unwrap_or_else(|| { panic!("loading scheduler from DB without scheduler for {}", hex::encode(key.to_bytes())) }); let mut reader_slice = scheduler.as_slice(); let reader = &mut reader_slice; - Self::read(key, reader) + Self::read(key, coin, reader) } - pub fn can_use_branch(&self, amount: u64) -> bool { - self.plans.contains_key(&amount) + pub fn can_use_branch(&self, balance: Balance) -> bool { + assert_eq!(balance.coin, self.coin); + self.plans.contains_key(&balance.amount.0) } fn execute( @@ -170,11 +183,14 @@ impl Scheduler { let mut change = false; let mut max = N::MAX_OUTPUTS; - let payment_amounts = - |payments: &Vec>| payments.iter().map(|payment| payment.amount).sum::(); + let payment_amounts = |payments: &Vec>| { + payments.iter().map(|payment| payment.balance.amount.0).sum::() + }; // Requires a change output - if inputs.iter().map(Output::amount).sum::() != payment_amounts(&payments) { + if inputs.iter().map(|output| output.balance().amount.0).sum::() != + payment_amounts(&payments) + { change = true; max -= 1; } @@ -208,7 +224,14 @@ impl Scheduler { // Create the payment for the plan // Push it to the front so it's not moved into a branch until all lower-depth items are - payments.insert(0, Payment { address: branch_address.clone(), data: None, amount }); + payments.insert( + 0, + Payment { + address: branch_address.clone(), + data: None, + balance: Balance { coin: self.coin, amount: Amount(amount) }, + }, + ); } Plan { @@ -230,12 +253,12 @@ impl Scheduler { for utxo in utxos.drain(..) { if utxo.kind() == OutputType::Branch { - let amount = utxo.amount(); + let amount = utxo.balance().amount.0; if let Some(plans) = self.plans.get_mut(&amount) { // Execute the first set of payments possible with an output of this amount let payments = plans.pop_front().unwrap(); // They won't be equal if we dropped payments due to being dust - assert!(amount >= payments.iter().map(|payment| payment.amount).sum::()); + assert!(amount >= payments.iter().map(|payment| payment.balance.amount.0).sum::()); // If we've grabbed the last plan for this output amount, remove it from the map if plans.is_empty() { @@ -264,6 +287,13 @@ impl Scheduler { key_for_any_change: ::G, force_spend: bool, ) -> Vec> { + for utxo in &utxos { + assert_eq!(utxo.balance().coin, self.coin); + } + for payment in &payments { + assert_eq!(payment.balance.coin, self.coin); + } + // Drop payments to our own branch address /* created_output will be called any time we send to a branch address. If it's called, and it @@ -297,7 +327,7 @@ impl Scheduler { } // Sort UTXOs so the highest valued ones are first - self.utxos.sort_by(|a, b| a.amount().cmp(&b.amount()).reverse()); + self.utxos.sort_by(|a, b| a.balance().amount.0.cmp(&b.balance().amount.0).reverse()); // We always want to aggregate our UTXOs into a single UTXO in the name of simplicity // We may have more UTXOs than will fit into a TX though @@ -333,7 +363,7 @@ impl Scheduler { } // We want to use all possible UTXOs for all possible payments - let mut balance = utxos.iter().map(Output::amount).sum::(); + let mut balance = utxos.iter().map(|output| output.balance().amount.0).sum::(); // If we can't fulfill the next payment, we have encountered an instance of the UTXO // availability problem @@ -345,7 +375,7 @@ impl Scheduler { // granting us access to our full balance let mut executing = vec![]; while !self.payments.is_empty() { - let amount = self.payments[0].amount; + let amount = self.payments[0].balance.amount.0; if balance.checked_sub(amount).is_some() { balance -= amount; executing.push(self.payments.pop_front().unwrap()); @@ -420,7 +450,7 @@ impl Scheduler { // Get the payments this output is expected to handle let queued = self.queued_plans.get_mut(&expected).unwrap(); let mut payments = queued.pop_front().unwrap(); - assert_eq!(expected, payments.iter().map(|payment| payment.amount).sum::()); + assert_eq!(expected, payments.iter().map(|payment| payment.balance.amount.0).sum::()); // If this was the last set of payments at this amount, remove it if queued.is_empty() { self.queued_plans.remove(&expected); @@ -436,7 +466,7 @@ impl Scheduler { { let mut to_amortize = actual - expected; // If the payments are worth less than this fee we need to amortize, return, dropping them - if payments.iter().map(|payment| payment.amount).sum::() < to_amortize { + if payments.iter().map(|payment| payment.balance.amount.0).sum::() < to_amortize { return; } while to_amortize != 0 { @@ -449,18 +479,20 @@ impl Scheduler { // Only subtract the overage once overage = 0; - let subtractable = payment.amount.min(to_subtract); + let subtractable = payment.balance.amount.0.min(to_subtract); to_amortize -= subtractable; - payment.amount -= subtractable; + payment.balance.amount.0 -= subtractable; } } } // Drop payments now below the dust threshold - let payments = - payments.into_iter().filter(|payment| payment.amount >= N::DUST).collect::>(); + let payments = payments + .into_iter() + .filter(|payment| payment.balance.amount.0 >= N::DUST) + .collect::>(); // Sanity check this was done properly - assert!(actual >= payments.iter().map(|payment| payment.amount).sum::()); + assert!(actual >= payments.iter().map(|payment| payment.balance.amount.0).sum::()); // If there's no payments left, return if payments.is_empty() { diff --git a/processor/src/networks/bitcoin.rs b/processor/src/networks/bitcoin.rs index d418cc65..d363e126 100644 --- a/processor/src/networks/bitcoin.rs +++ b/processor/src/networks/bitcoin.rs @@ -43,7 +43,7 @@ use bitcoin_serai::bitcoin::{ }; use serai_client::{ - primitives::{MAX_DATA_LEN, Coin as SeraiCoin, NetworkId, Amount, Balance}, + primitives::{MAX_DATA_LEN, Coin, NetworkId, Amount, Balance}, networks::bitcoin::Address, }; @@ -126,7 +126,7 @@ impl OutputTrait for Output { } fn balance(&self) -> Balance { - Balance { coin: SeraiCoin::Bitcoin, amount: Amount(self.output.value()) } + Balance { coin: Coin::Bitcoin, amount: Amount(self.output.value()) } } fn data(&self) -> &[u8] { @@ -384,6 +384,10 @@ impl Bitcoin { change: &Option
, calculating_fee: bool, ) -> Result, NetworkError> { + for payment in payments { + assert_eq!(payment.balance.coin, Coin::Bitcoin); + } + // TODO2: Use an fee representative of several blocks, cached inside Self let block_for_fee = self.get_block(block_number).await?; let fee = self.median_fee(&block_for_fee).await?; @@ -396,7 +400,7 @@ impl Bitcoin { // If we're solely estimating the fee, don't specify the actual amount // This won't affect the fee calculation yet will ensure we don't hit a not enough funds // error - if calculating_fee { Self::DUST } else { payment.amount }, + if calculating_fee { Self::DUST } else { payment.balance.amount.0 }, ) }) .collect::>(); diff --git a/processor/src/networks/mod.rs b/processor/src/networks/mod.rs index 3a5e246a..0122f241 100644 --- a/processor/src/networks/mod.rs +++ b/processor/src/networks/mod.rs @@ -106,10 +106,6 @@ pub trait Output: Send + Sync + Sized + Clone + PartialEq + Eq + Deb fn presumed_origin(&self) -> Option; fn balance(&self) -> Balance; - // TODO: Remove this? - fn amount(&self) -> u64 { - self.balance().amount.0 - } fn data(&self) -> &[u8]; fn write(&self, writer: &mut W) -> io::Result<()>; @@ -208,7 +204,7 @@ fn drop_branches( let mut branch_outputs = vec![]; for payment in payments { if payment.address == N::branch_address(key) { - branch_outputs.push(PostFeeBranch { expected: payment.amount, actual: None }); + branch_outputs.push(PostFeeBranch { expected: payment.balance.amount.0, actual: None }); } } branch_outputs @@ -279,6 +275,7 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Eq + Debug { /// For any received output, there's the cost to spend the output. This value MUST exceed the /// cost to spend said output, and should by a notable margin (not just 2x, yet an order of /// magnitude). + // TODO: Dust needs to be diversified per Coin const DUST: u64; /// The cost to perform input aggregation with a 2-input 1-output TX. @@ -356,8 +353,9 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Eq + Debug { let plan_id = plan.id(); let Plan { key, inputs, mut payments, change } = plan; - let theoretical_change_amount = inputs.iter().map(|input| input.amount()).sum::() - - payments.iter().map(|payment| payment.amount).sum::(); + let theoretical_change_amount = + inputs.iter().map(|input| input.balance().amount.0).sum::() - + payments.iter().map(|payment| payment.balance.amount.0).sum::(); let Some(tx_fee) = self.needed_fee(block_number, &plan_id, &inputs, &payments, &change).await? else { @@ -381,7 +379,7 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Eq + Debug { // as well let total_fee = tx_fee + if change.is_some() { operating_costs } else { 0 }; - let original_outputs = payments.iter().map(|payment| payment.amount).sum::(); + let original_outputs = payments.iter().map(|payment| payment.balance.amount.0).sum::(); // If this isn't enough for the total fee, drop and move on if original_outputs < total_fee { let mut remaining_operating_costs = operating_costs; @@ -395,7 +393,7 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Eq + Debug { } let initial_payment_amounts = - payments.iter().map(|payment| payment.amount).collect::>(); + payments.iter().map(|payment| payment.balance.amount.0).collect::>(); // Amortize the transaction fee across outputs let mut remaining_fee = total_fee; @@ -409,16 +407,16 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Eq + Debug { // Only subtract the overage once overage = 0; - let subtractable = payment.amount.min(this_payment_fee); + let subtractable = payment.balance.amount.0.min(this_payment_fee); remaining_fee -= subtractable; - payment.amount -= subtractable; + payment.balance.amount.0 -= subtractable; } } // If any payment is now below the dust threshold, set its value to 0 so it'll be dropped for payment in &mut payments { - if payment.amount < Self::DUST { - payment.amount = 0; + if payment.balance.amount.0 < Self::DUST { + payment.balance.amount.0 = 0; } } @@ -428,7 +426,11 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Eq + Debug { if payment.address == Self::branch_address(key) { branch_outputs.push(PostFeeBranch { expected: initial_amount, - actual: if payment.amount == 0 { None } else { Some(payment.amount) }, + actual: if payment.balance.amount.0 == 0 { + None + } else { + Some(payment.balance.amount.0) + }, }); } } @@ -437,7 +439,7 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Eq + Debug { payments = payments .drain(..) .filter(|payment| { - if payment.amount != 0 { + if payment.balance.amount.0 != 0 { true } else { log::debug!("dropping dust payment from plan {}", hex::encode(plan_id)); @@ -447,7 +449,7 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Eq + Debug { .collect(); // Sanity check the fee was successfully amortized - let new_outputs = payments.iter().map(|payment| payment.amount).sum::(); + let new_outputs = payments.iter().map(|payment| payment.balance.amount.0).sum::(); assert!((new_outputs + total_fee) <= original_outputs); ( @@ -483,9 +485,10 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Eq + Debug { }; if change.is_some() { - let on_chain_expected_change = inputs.iter().map(|input| input.amount()).sum::() - - payments.iter().map(|payment| payment.amount).sum::() - - tx_fee; + let on_chain_expected_change = + inputs.iter().map(|input| input.balance().amount.0).sum::() - + payments.iter().map(|payment| payment.balance.amount.0).sum::() - + tx_fee; // If the change value is less than the dust threshold, it becomes an operating cost // This may be slightly inaccurate as dropping payments may reduce the fee, raising the // change above dust diff --git a/processor/src/networks/monero.rs b/processor/src/networks/monero.rs index acb4a78b..dca7392b 100644 --- a/processor/src/networks/monero.rs +++ b/processor/src/networks/monero.rs @@ -30,7 +30,7 @@ use monero_serai::{ use tokio::time::sleep; pub use serai_client::{ - primitives::{MAX_DATA_LEN, Coin as SeraiCoin, NetworkId, Amount, Balance}, + primitives::{MAX_DATA_LEN, Coin, NetworkId, Amount, Balance}, networks::monero::Address, }; @@ -82,7 +82,7 @@ impl OutputTrait for Output { } fn balance(&self) -> Balance { - Balance { coin: SeraiCoin::Monero, amount: Amount(self.0.commitment().amount) } + Balance { coin: Coin::Monero, amount: Amount(self.0.commitment().amount) } } fn data(&self) -> &[u8] { @@ -255,6 +255,10 @@ impl Monero { change: &Option
, calculating_fee: bool, ) -> Result, NetworkError> { + for payment in payments { + assert_eq!(payment.balance.coin, Coin::Monero); + } + // TODO2: Use an fee representative of several blocks, cached inside Self let block_for_fee = self.get_block(block_number).await?; let fee_rate = self.median_fee(&block_for_fee).await?; @@ -313,7 +317,7 @@ impl Monero { .address(MoneroNetwork::Mainnet, AddressSpec::Standard), ) .unwrap(), - amount: 0, + balance: Balance { coin: Coin::Monero, amount: Amount(0) }, data: None, }); } @@ -322,7 +326,9 @@ impl Monero { .into_iter() // If we're solely estimating the fee, don't actually specify an amount // This won't affect the fee calculation yet will ensure we don't hit an out of funds error - .map(|payment| (payment.address.into(), if calculating_fee { 0 } else { payment.amount })) + .map(|payment| { + (payment.address.into(), if calculating_fee { 0 } else { payment.balance.amount.0 }) + }) .collect::>(); match MSignableTransaction::new( diff --git a/processor/src/plan.rs b/processor/src/plan.rs index 4b0e0895..6dcf74da 100644 --- a/processor/src/plan.rs +++ b/processor/src/plan.rs @@ -1,17 +1,20 @@ use std::io; +use scale::{Encode, Decode}; + use transcript::{Transcript, RecommendedTranscript}; use ciphersuite::group::GroupEncoding; use frost::curve::Ciphersuite; +use serai_client::primitives::Balance; + use crate::networks::{Output, Network}; #[derive(Clone, PartialEq, Eq, Debug)] pub struct Payment { pub address: N::Address, pub data: Option>, - // TODO: Balance - pub amount: u64, + pub balance: Balance, } impl Payment { @@ -21,7 +24,8 @@ impl Payment { if let Some(data) = self.data.as_ref() { transcript.append_message(b"data", data); } - transcript.append_message(b"amount", self.amount.to_le_bytes()); + transcript.append_message(b"coin", self.balance.coin.encode()); + transcript.append_message(b"amount", self.balance.amount.0.to_le_bytes()); } pub fn write(&self, writer: &mut W) -> io::Result<()> { @@ -40,7 +44,7 @@ impl Payment { writer.write_all(data)?; } - writer.write_all(&self.amount.to_le_bytes()) + writer.write_all(&self.balance.encode()) } pub fn read(reader: &mut R) -> io::Result { @@ -63,11 +67,10 @@ impl Payment { None }; - let mut buf = [0; 8]; - reader.read_exact(&mut buf)?; - let amount = u64::from_le_bytes(buf); + let balance = Balance::decode(&mut scale::IoReader(reader)) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid balance"))?; - Ok(Payment { address, data, amount }) + Ok(Payment { address, data, balance }) } } diff --git a/processor/src/tests/signer.rs b/processor/src/tests/signer.rs index 95e3e81f..b83c0428 100644 --- a/processor/src/tests/signer.rs +++ b/processor/src/tests/signer.rs @@ -10,6 +10,8 @@ use frost::{ use serai_db::{DbTxn, Db, MemDb}; +use serai_client::primitives::{NetworkId, Coin, Amount, Balance}; + use messages::sign::*; use crate::{ Payment, Plan, @@ -170,7 +172,19 @@ pub async fn test_signer(network: N) { Plan { key, inputs: outputs.clone(), - payments: vec![Payment { address: N::address(key), data: None, amount }], + payments: vec![Payment { + address: N::address(key), + data: None, + balance: Balance { + coin: match N::NETWORK { + NetworkId::Serai => panic!("test_signer called with Serai"), + NetworkId::Bitcoin => Coin::Bitcoin, + NetworkId::Ethereum => Coin::Ether, + NetworkId::Monero => Coin::Monero, + }, + amount: Amount(amount), + }, + }], change: Some(N::change_address(key)), }, 0, @@ -201,7 +215,7 @@ pub async fn test_signer(network: N) { // Adjust the amount for the fees let amount = amount - tx.fee(&network).await; // Check either output since Monero will randomize its output order - assert!((outputs[0].amount() == amount) || (outputs[1].amount() == amount)); + assert!((outputs[0].balance().amount.0 == amount) || (outputs[1].balance().amount.0 == amount)); // Check the eventualities pass for eventuality in eventualities { diff --git a/processor/src/tests/wallet.rs b/processor/src/tests/wallet.rs index fa88d784..3c8700c4 100644 --- a/processor/src/tests/wallet.rs +++ b/processor/src/tests/wallet.rs @@ -8,6 +8,8 @@ use tokio::time::timeout; use serai_db::{DbTxn, Db, MemDb}; +use serai_client::primitives::{NetworkId, Coin, Amount, Balance}; + use crate::{ Payment, Plan, networks::{Output, Transaction, Block, Network}, @@ -64,12 +66,33 @@ pub async fn test_wallet(network: N) { txn.commit(); let mut txn = db.txn(); - let mut scheduler = Scheduler::new::(&mut txn, key); + let mut scheduler = Scheduler::new::( + &mut txn, + key, + match N::NETWORK { + NetworkId::Serai => panic!("test_wallet called with Serai"), + NetworkId::Bitcoin => Coin::Bitcoin, + NetworkId::Ethereum => Coin::Ether, + NetworkId::Monero => Coin::Monero, + }, + ); let amount = 2 * N::DUST; let plans = scheduler.schedule::( &mut txn, outputs.clone(), - vec![Payment { address: N::address(key), data: None, amount }], + vec![Payment { + address: N::address(key), + data: None, + balance: Balance { + coin: match N::NETWORK { + NetworkId::Serai => panic!("test_wallet called with Serai"), + NetworkId::Bitcoin => Coin::Bitcoin, + NetworkId::Ethereum => Coin::Ether, + NetworkId::Monero => Coin::Monero, + }, + amount: Amount(amount), + }, + }], key, false, ); @@ -79,7 +102,19 @@ pub async fn test_wallet(network: N) { vec![Plan { key, inputs: outputs.clone(), - payments: vec![Payment { address: N::address(key), data: None, amount }], + payments: vec![Payment { + address: N::address(key), + data: None, + balance: Balance { + coin: match N::NETWORK { + NetworkId::Serai => panic!("test_wallet called with Serai"), + NetworkId::Bitcoin => Coin::Bitcoin, + NetworkId::Ethereum => Coin::Ether, + NetworkId::Monero => Coin::Monero, + }, + amount: Amount(amount), + } + }], change: Some(N::change_address(key)), }] ); @@ -113,7 +148,7 @@ pub async fn test_wallet(network: N) { let outputs = network.get_outputs(&block, key).await; assert_eq!(outputs.len(), 2); let amount = amount - tx.fee(&network).await; - assert!((outputs[0].amount() == amount) || (outputs[1].amount() == amount)); + assert!((outputs[0].balance().amount.0 == amount) || (outputs[1].balance().amount.0 == amount)); for eventuality in eventualities { assert!(network.confirm_completion(&eventuality, &tx));