-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[Feat] Introduce ECDSA verification opcodes #2913
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
Conversation
Signed-off-by: Raymond Chu <14917648+raychu86@users.noreply.github.com>
vicsn
left a comment
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.
Looks excellent, from my humble perspective. 🎆
Can you update the docstring for ConsensusVersion::V11?
| } | ||
|
|
||
| /// Unary operator for converting to raw bits. | ||
| pub trait ToBitsRaw: ToBits { |
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.
Inactionable rant: long term it might make more sense to have trait ToBits: ToBitsRaw, as I understand that to_bits serialization could call to_bits_raw internally. But then again, we don't want to enable raw serialization for all types, so I guess the current approach is fine and helpful.
| #[inline] | ||
| fn write_bits_raw_le(&self, vec: &mut Vec<Self::Boolean>) { | ||
| // The slice is order-preserving, meaning the first circuit in is the first circuit bits out. | ||
| for elem in self.iter() { |
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.
Note: the element-wise little endian serialization is bound to lead to confusion so we should really document this well on the SDK sode.
If you agree, do you want to note this explicitly in the PR description so it'll make its way into the release notes?
CC @Roee-87 just FYI
| (16, _) => bail!("'hash_many.psd4' is not yet implemented"), | ||
| (17, _) => bail!("'hash_many.psd8' is not yet implemented"), | ||
| (18.., _) => bail!("Invalid 'hash' variant: {}", $variant), | ||
| (18, _) => $q($N::hash_to_group_bhp256(&bits_raw()))?.into(), |
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.
Double checking: are we sure we don't have to guard this by Address | Group like we do for some other match arms? (Meaning the previous code would have a bug)
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.
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.
hash_to_group* for the BHP and Pedersen variants is the uncompressed version. If you look at their corresponding hash.* (without to_group), you'll see that it calls the uncompressed hash and then casts it to a field.
We guard for Poseidon because its standard version outputs a field. hash_to_group_psd encodes with Elligator. I could not tell you what security properties this affords.
| } | ||
|
|
||
| #[test] | ||
| fn test_ecdsa_signature_vector_1() { |
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.
Is there any canonical set of test vectors which we can use, for ECDSA and/or Ethereum's usage?
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.
Still waiting on test vectors from partners, however we should pull in any standard library of ECDSA test vectors if they are available.
| 31, 32 | ||
| ); | ||
| // Implement for `[U8<N>, SIZE]` for sizes 1 through 256. | ||
| seq_macro::seq!(S in 1..=256 { |
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.
That's a lot of code generation. Which will increase even more if we have a larger array size. The use of seq! feels like an antipattern here, albeit a pretty one.
What about we have just a single impl<N: Network> From<impl Iterator<Item = $element<N>>> for Plaintext<N>? To not have to think about differing guarantees we could ensure we don't surpass the maximum array length in there.
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.
You could also use ExactSizeIterator
Signed-off-by: Raymond Chu <14917648+raychu86@users.noreply.github.com>
|
|
||
| /// Returns `true` if the program contains an array type with a size that exceeds the given maximum. | ||
| pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool { | ||
| self.mappings.values().any(|mapping| mapping.exceeds_max_array_size(max_array_size)) |
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.
Can mapping keys also be arrays? MapKey contains a generic PlaintextType so maybe we should just check.
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 believe that is already checked here: https://github.com/ProvableHQ/snarkVM/blob/feat/ecdsa/synthesizer/program/src/mapping/mod.rs#L59
|
|
||
| #[cfg(feature = "test")] | ||
| #[test] | ||
| fn test_ecdsa_migration() { |
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'm a proponent of these sorts of tests going in a test_v* file. It helps us easily look back on our test coverage for each consensus version.
Motivation
This PR introduces native ECDSA signature verification to on-chain (
finalize) execution to improve interoperability and broaden supported signature formats. With on-chain verification, programs can trust externally signed messages and approvals without off-chain helpers, enabling cross-chain messaging, asset flows, multisig/governance, etc. The initial scope focuses on verification with a simple canonical representation and deterministic, consensus-safe behavior, while leaving room to add other schemes and encodings over time.The list of new
ecdsa.verify.<variant>instructions are as follows:ecdsa.verify.keccak256ecdsa.verify.keccak256.rawecdsa.verify.keccak256.ethecdsa.verify.keccak384ecdsa.verify.keccak384.rawecdsa.verify.keccak384.ethecdsa.verify.keccak512ecdsa.verify.keccak512.rawecdsa.verify.keccak512.ethecdsa.verify.sha3_256ecdsa.verify.sha3_256.rawecdsa.verify.sha3_256.ethecdsa.verify.sha3_384ecdsa.verify.sha3_384.rawecdsa.verify.sha3_384.ethecdsa.verify.sha3_512ecdsa.verify.sha3_512.rawecdsa.verify.sha3_512.ethThe variant types allow the users to select the underlying hash function used, the serialization type (with
.raw), and if the address being verified is a 20-byte Ethereum address. The.rawvariants indicate if the message should be serialized in it's raw bit form or with Aleo specific variant bits; this is necessary to ensure compatibility with external signing libraries. By default the.ethvariants will serialize the message in it's raw form, so there is no need for an additional.eth.rawsuffixed variant.A few additional features are also included:
.rawserialization variants.rawto the end.sign.verify.rawis also introduced toIncreasing array size from 32 to 256.
Test Plan
Local Devnet
TODO
Questions for Auditors
ToFieldsRawimplementation safe?