mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 04:39:24 +00:00
monero-serai: fee calculation parity with Monero's wallet2 (#301)
* monero-serai: fee calculation parity with Monero's wallet2 * Minor lint --------- Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
This commit is contained in:
@@ -1,11 +1,44 @@
|
||||
use rand_core::OsRng;
|
||||
|
||||
use monero_serai::{
|
||||
transaction::Transaction,
|
||||
wallet::{extra::Extra, address::SubaddressIndex, ReceivedOutput, SpendableOutput},
|
||||
rpc::Rpc,
|
||||
wallet::{
|
||||
extra::Extra, address::SubaddressIndex, ReceivedOutput, SpendableOutput, Decoys,
|
||||
SignableTransactionBuilder,
|
||||
},
|
||||
rpc::{Rpc, HttpRpc},
|
||||
Protocol,
|
||||
};
|
||||
|
||||
mod runner;
|
||||
|
||||
// Set up inputs, select decoys, then add them to the TX builder
|
||||
async fn add_inputs(
|
||||
protocol: Protocol,
|
||||
rpc: &Rpc<HttpRpc>,
|
||||
outputs: Vec<ReceivedOutput>,
|
||||
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::select(
|
||||
&mut OsRng,
|
||||
rpc,
|
||||
protocol.ring_len(),
|
||||
rpc.get_height().await.unwrap() - 1,
|
||||
&spendable_outputs,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let inputs = spendable_outputs.into_iter().zip(decoys.into_iter()).collect::<Vec<_>>();
|
||||
|
||||
builder.add_inputs(&inputs);
|
||||
}
|
||||
|
||||
test!(
|
||||
spend_miner_output,
|
||||
(
|
||||
@@ -37,10 +70,8 @@ test!(
|
||||
},
|
||||
),
|
||||
(
|
||||
|rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
||||
for output in outputs {
|
||||
builder.add_input(SpendableOutput::from(&rpc, output).await.unwrap());
|
||||
}
|
||||
|protocol: Protocol, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
||||
add_inputs(protocol, &rpc, outputs, &mut builder).await;
|
||||
builder.add_payment(addr, 6);
|
||||
(builder.build().unwrap(), ())
|
||||
},
|
||||
@@ -69,18 +100,20 @@ test!(
|
||||
},
|
||||
),
|
||||
(
|
||||
|rpc: Rpc<_>, _, _, mut outputs: Vec<ReceivedOutput>| async move {
|
||||
|protocol, rpc: Rpc<_>, _, _, outputs: Vec<ReceivedOutput>| async move {
|
||||
use monero_serai::wallet::FeePriority;
|
||||
|
||||
let change_view = ViewPair::new(
|
||||
&random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE,
|
||||
Zeroizing::new(random_scalar(&mut OsRng)),
|
||||
);
|
||||
|
||||
let mut builder = SignableTransactionBuilder::new(
|
||||
rpc.get_protocol().await.unwrap(),
|
||||
rpc.get_fee().await.unwrap(),
|
||||
protocol,
|
||||
rpc.get_fee(protocol, FeePriority::Low).await.unwrap(),
|
||||
Some(Change::new(&change_view, false)),
|
||||
);
|
||||
builder.add_input(SpendableOutput::from(&rpc, outputs.swap_remove(0)).await.unwrap());
|
||||
add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
|
||||
|
||||
// Send to a subaddress
|
||||
let sub_view = ViewPair::new(
|
||||
@@ -116,3 +149,127 @@ test!(
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
test!(
|
||||
spend_one_input_to_one_output_plus_change,
|
||||
(
|
||||
|_, mut builder: Builder, addr| async move {
|
||||
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));
|
||||
assert_eq!(outputs[0].commitment().amount, 2000000000000);
|
||||
outputs
|
||||
},
|
||||
),
|
||||
(
|
||||
|protocol: Protocol, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
||||
add_inputs(protocol, &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);
|
||||
assert_eq!(output.commitment().amount, 2);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
test!(
|
||||
spend_max_outputs,
|
||||
(
|
||||
|_, mut builder: Builder, addr| async move {
|
||||
builder.add_payment(addr, 1000000000000);
|
||||
(builder.build().unwrap(), ())
|
||||
},
|
||||
|_, tx: Transaction, mut scanner: Scanner, _| async move {
|
||||
let mut outputs = scanner.scan_transaction(&tx).not_locked();
|
||||
outputs.sort_by(|x, y| x.commitment().amount.cmp(&y.commitment().amount));
|
||||
assert_eq!(outputs[0].commitment().amount, 1000000000000);
|
||||
outputs
|
||||
},
|
||||
),
|
||||
(
|
||||
|protocol: Protocol, rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
||||
add_inputs(protocol, &rpc, outputs, &mut builder).await;
|
||||
|
||||
for i in 0 .. 15 {
|
||||
builder.add_payment(addr, i + 1);
|
||||
}
|
||||
(builder.build().unwrap(), ())
|
||||
},
|
||||
|_, tx: Transaction, mut scanner: Scanner, _| async move {
|
||||
let mut scanned_tx = scanner.scan_transaction(&tx).not_locked();
|
||||
|
||||
let mut output_amounts = HashSet::new();
|
||||
for i in 0 .. 15 {
|
||||
output_amounts.insert((i + 1) as u64);
|
||||
}
|
||||
for _ in 0 .. 15 {
|
||||
let output = scanned_tx.swap_remove(0);
|
||||
let amount = output.commitment().amount;
|
||||
assert!(output_amounts.contains(&amount));
|
||||
output_amounts.remove(&amount);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
test!(
|
||||
spend_max_outputs_to_subaddresses,
|
||||
(
|
||||
|_, mut builder: Builder, addr| async move {
|
||||
builder.add_payment(addr, 1000000000000);
|
||||
(builder.build().unwrap(), ())
|
||||
},
|
||||
|_, tx: Transaction, mut scanner: Scanner, _| async move {
|
||||
let mut outputs = scanner.scan_transaction(&tx).not_locked();
|
||||
outputs.sort_by(|x, y| x.commitment().amount.cmp(&y.commitment().amount));
|
||||
assert_eq!(outputs[0].commitment().amount, 1000000000000);
|
||||
outputs
|
||||
},
|
||||
),
|
||||
(
|
||||
|protocol: Protocol, rpc, mut builder: Builder, _, outputs: Vec<ReceivedOutput>| async move {
|
||||
add_inputs(protocol, &rpc, outputs, &mut builder).await;
|
||||
|
||||
let view = runner::random_address().1;
|
||||
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
|
||||
|
||||
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)),
|
||||
(i + 1) as u64,
|
||||
);
|
||||
subaddresses.push(subaddress);
|
||||
}
|
||||
|
||||
(builder.build().unwrap(), (scanner, subaddresses))
|
||||
},
|
||||
|_, 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 output_amounts_by_subaddress = HashMap::new();
|
||||
for i in 0 .. 15 {
|
||||
output_amounts_by_subaddress.insert((i + 1) as u64, state.1[i]);
|
||||
}
|
||||
for _ in 0 .. 15 {
|
||||
let output = scanned_tx.swap_remove(0);
|
||||
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);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user