monero-serai: make it clear that not providing a change address is fingerprintable (#472)

* Make it clear not providing a change address is fingerprintable

When no change address is provided, all change is shunted to the
fee. This PR makes it clear to the caller that it is fingerprintable
when the caller does this.

* Review comments
This commit is contained in:
Justin Berman
2023-12-08 04:42:02 -08:00
committed by GitHub
parent 16b22dd105
commit 397fca748f
8 changed files with 91 additions and 44 deletions

View File

@@ -213,13 +213,13 @@ macro_rules! test {
let builder = SignableTransactionBuilder::new(
protocol,
rpc.get_fee(protocol, FeePriority::Low).await.unwrap(),
Some(Change::new(
Change::new(
&ViewPair::new(
&random_scalar(&mut OsRng) * ED25519_BASEPOINT_TABLE,
Zeroizing::new(random_scalar(&mut OsRng))
),
false
)),
),
);
let sign = |tx: SignableTransaction| {
@@ -298,7 +298,11 @@ macro_rules! test {
rpc.publish_transaction(&signed).await.unwrap();
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);
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,
// the change is added to the fee
check_weight_and_fee(&tx, fee_rate);
}
#[allow(unused_assignments)]
{
let scanner =

View File

@@ -111,7 +111,7 @@ test!(
let mut builder = SignableTransactionBuilder::new(
protocol,
rpc.get_fee(protocol, FeePriority::Low).await.unwrap(),
Some(Change::new(&change_view, false)),
Change::new(&change_view, false),
);
add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
@@ -273,3 +273,44 @@ test!(
},
),
);
test!(
spend_one_input_to_two_outputs_no_change,
(
|_, 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, rpc: Rpc<_>, _, addr, outputs: Vec<ReceivedOutput>| async move {
use monero_serai::wallet::FeePriority;
let mut builder = SignableTransactionBuilder::new(
protocol,
rpc.get_fee(protocol, FeePriority::Low).await.unwrap(),
Change::fingerprintable(None),
);
add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
builder.add_payment(addr, 10000);
builder.add_payment(addr, 50000);
(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, 10000);
assert_eq!(outputs[1].commitment().amount, 50000);
// The remainder should get shunted to fee, which is fingerprintable
assert_eq!(tx.rct_signatures.base.fee, 1000000000000 - 10000 - 50000);
},
),
);