-
Notifications
You must be signed in to change notification settings - Fork 986
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
Avoid duplicated signatures when building transactions #4230
base: main
Are you sure you want to change the base?
Changes from all commits
d4c6e33
aecd5af
650f0b8
7430933
fa5bdbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
- Improved the transaction building process to avoid duplicated sections. | ||
([\#4230](https://github.com/anoma/namada/pull/4230)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -431,7 +431,6 @@ pub enum Signer { | |
BorshSchema, | ||
Serialize, | ||
Deserialize, | ||
PartialEq, | ||
)] | ||
pub struct Authorization { | ||
/// The hash of the section being signed | ||
|
@@ -442,6 +441,41 @@ pub struct Authorization { | |
pub signatures: BTreeMap<u8, common::Signature>, | ||
} | ||
|
||
impl PartialEq for Authorization { | ||
fn eq(&self, other: &Self) -> bool { | ||
// Deconstruct the two instances to ensure we don't forget any new field | ||
let Authorization { | ||
targets, | ||
signer: _, | ||
signatures, | ||
} = self; | ||
let Authorization { | ||
targets: other_targets, | ||
signer: _, | ||
signatures: other_signatures, | ||
} = other; | ||
|
||
// Two authorizations are equal when they are computed over the same | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This definition could work. But it has the implication that if Also, given that this function defines a partial equivalence relation, I guess There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's correct. The representation of the signer being different in the duplicated authorizations I could see on mainnet was the drive to modify this trait implementation. My reasoning was that once we establish the equality of the signature's targets and of the signatures themselves we can safely consider the two |
||
// target(s) and the signatures match, regardless of how the signer is | ||
// expressed | ||
|
||
// Check equivalence of the targets ignoring the specific ordering and | ||
// duplicates (the PartialEq implementation of IndexSet ignores the | ||
// order) | ||
let unique_targets = | ||
HashSet::<&namada_account::Hash>::from_iter(targets.iter()); | ||
let unique_other_targets = | ||
HashSet::<&namada_account::Hash>::from_iter(other_targets.iter()); | ||
|
||
if unique_targets != unique_other_targets { | ||
return false; | ||
} | ||
|
||
// Check equivalence of the signatures | ||
signatures == other_signatures | ||
} | ||
} | ||
|
||
impl Authorization { | ||
/// Sign the given section hash with the given key and return a section | ||
pub fn new( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it matter that partially equivalent
Authorization
s might not have the same cryptographic hash? Or is this impossible? Could this sort of definition of partial equality confuse people in the future? (This definition seems to be calculated specifically to deal with our duplicate signatures issue (no?), but could be unintuitive in other cases/situations...)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe
PartialEq
should guarantee a byte-level match between the two instances but rather a logical equivalence (as I said in the previous question, when safety is critical one should explicitly rely on the digest of the expected instance).I do see your point on this implementation being potentially confusing in other situations: I could try to reintroduce the check on the signer with a more refined logic. My only concern is the case when a multisig signer is expressed as a set of keys in one instance and as an address in the other one: in this case there's no way I can resolve the equivalence without a query to storage (but there's a chance we always express multisig signers as a set of keys which would solve this issue).
An alternative could be to come up with a different function for this comparison and restore the previous implementation of
PartialEq
(net of ignoring the order of the set of keys which I think is a required update regardless).