Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged

Conversation

j-berman
Copy link
Contributor

@j-berman j-berman commented Dec 7, 2023

When a signable transaction is constructed without a change address specified, all change is added to the tx fee (this helps Serai avoid accumulating dust outputs that spam the chain). This PR makes it clearer to the caller that it is fingerprintable when the caller does not provide a change address, since the caller must explicitly initialize the change param with Change::fingerprintable(None) (as is done in the added test). A tx created this way is fingerprintable because the fee is distinguishable from a fee created with wallet2.

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.
Copy link
Member

@kayabaNerve kayabaNerve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for resolving this fingerprint :)

}
change.address.is_subaddress()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will an InternalPayment::Change exist if change.address is none?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't, I went with your suggestion in the other comment to not use the Option for InternalPayment::Change

@@ -624,7 +627,7 @@ impl SignableTransaction {
// regarding it's view key
payment = if !modified_change_ecdh {
if let InternalPayment::Change(change, amount) = &payment {
InternalPayment::Payment((change.address, *amount))
InternalPayment::Payment((change.address.unwrap(), *amount))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prior comment asked about this, here unwraps.

We could also make InternalPayment::Change not internally use the Change struct, avoiding the Option entirely and offering compile-time safety.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also make InternalPayment::Change not internally use the Change struct, avoiding the Option entirely and offering compile-time safety.

I thought of doing this when working on it too and decided not to. I like it though. Done in the latest.

@@ -298,7 +298,10 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind having the comment explain why?

" as if there's no change, any would-be change will be shunted to the fee" or so?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -376,6 +376,12 @@ impl Monero {
})
.collect::<Vec<_>>();

let change = Change::fingerprintable(if change.is_some() {
Some(change.clone().unwrap().into())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if let Some(change) = change {}, to avoid the unwrap

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did that suggestion, then the compiler warned it's equivalent to Option::map and suggested change.as_ref().map(|change| change.clone().into()). See the latest. I was wondering what the cleanest way to write this was, cool.

@kayabaNerve kayabaNerve merged commit 397fca7 into serai-dex:develop Dec 8, 2023
15 checks passed
@kayabaNerve
Copy link
Member

Thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants