Conversation
How to use the Graphite Merge QueueAdd the label merge-ready to this PR to add it to the merge queue. You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
36a5314 to
1b3cc02
Compare
|
There is no support for keyed MAC mode (Blake2s-MAC). I suppose this is OK? I would check with Jim. |
1b3cc02 to
6acf60e
Compare
| //! - **Variable output length**: Fixed at 256 bits (32 bytes). | ||
| //! | ||
| //! These exclusions are appropriate for a ZK circuit focused on hash verification | ||
| //! rather than general-purpose hashing. For details, see RFC_7693_COMPLIANCE_REPORT.md. |
There was a problem hiding this comment.
Referencing a Markdown document not in the repo.
| // Initialize hash state with Blake2s-256 parameters | ||
| // h[0] = IV[0] ^ 0x01010020 (param block: digest_length=32, fanout=1, depth=1) | ||
| let init_state = [ | ||
| builder.add_constant(Word((IV[0] ^ 0x01010020) as u64)), |
There was a problem hiding this comment.
IMO this is a perfect candidate for add_constant_64
There was a problem hiding this comment.
should I extract it as a function in this PR?
| builder.shl(byte_wire, (byte_idx * 8) as u32) | ||
| }; | ||
|
|
||
| word = builder.bor(word, shifted); |
There was a problem hiding this comment.
I am almost sure this can be a XOR instead (the bit ranges are disjoint). XOR is allegedly cheaper.
|
|
||
| // Finalization flag: apply mask to set appropriately | ||
| let flag_value = builder.add_constant(Word(0xFFFFFFFF)); | ||
| let last_flag = builder.band(is_final_block, flag_value); |
There was a problem hiding this comment.
Using select here is more future proof. The same thing applies to all band usages above. The rewritten form should be something like:
builder.select(is_final_block, flag_value, zero);| let counter_if_final = builder.band(is_final_block, length); | ||
| let counter_if_not_final = builder.band(builder.bnot(is_final_block), block_counter); | ||
| let t_lo = builder.bxor(counter_if_final, counter_if_not_final); | ||
| let t_hi = zero; |
There was a problem hiding this comment.
This implicitly assumes less than 4 GiB hashed data, which is reasonable for a circuit, but needs to be mentioned in the comments.
| let global_byte_idx = byte_offset + byte_idx; | ||
| if global_byte_idx < max_bytes { | ||
| // Get the byte wire (or zero if beyond message length) | ||
| let byte_wire = if global_byte_idx < message.len() { |
There was a problem hiding this comment.
There should be a similar constraint but against length via icmp_ult, as RFC7693 requires the m array to be zeroed at the end of the final block, and this is currently not enforced. If someone tries to hash data with nonzero bytes in message beyond length they'll be in for a surprise.
inb4: I know that populate_message zeroes this bytes out; we are talking a potential soundness bug here.
onesk
left a comment
There was a problem hiding this comment.
Message handling needs to be improved, otherwise this seems to follow RFC7693 to the very letter.
6acf60e to
3be5eca
Compare
| let byte_wire = if global_byte_idx < message.len() { | ||
| message[global_byte_idx] | ||
| } else { | ||
| builder.add_constant(Word(0)) | ||
| }; |
There was a problem hiding this comment.
The condition global_byte_idx < message.len() will always evaluate to true because message.len() equals max_bytes (from line 296 where the message vector is created with exactly max_bytes elements). This means the else branch is unreachable dead code.
The condition should check against max_bytes instead:
let byte_wire = if global_byte_idx < max_bytes {
message[global_byte_idx]
} else {
builder.add_constant(Word(0))
};This would properly handle the case where padding with zeros is needed beyond the message array bounds.
| let byte_wire = if global_byte_idx < message.len() { | |
| message[global_byte_idx] | |
| } else { | |
| builder.add_constant(Word(0)) | |
| }; | |
| let byte_wire = if global_byte_idx < max_bytes { | |
| message[global_byte_idx] | |
| } else { | |
| builder.add_constant(Word(0)) | |
| }; |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
| cargo run --release --example blake2s -- --message-len 64\n\ | ||
| \n\ | ||
| Test with maximum message length:\n\ | ||
| cargo run --release --example blake2s -- --max-message-len 256 --message-len 256", |
There was a problem hiding this comment.
The parameter name in the help text (--max-message-len) doesn't match the actual parameter defined in the Params struct (--max-bytes). This inconsistency will cause confusion for users trying to follow the examples. Consider updating the help text to use --max-bytes to match the actual parameter name:
cargo run --release --example blake2s -- --max-bytes 256 --message-len 256
| cargo run --release --example blake2s -- --max-message-len 256 --message-len 256", | |
| cargo run --release --example blake2s -- --max-bytes 256 --message-len 256", |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
3be5eca to
64ef5da
Compare
64ef5da to
77a6fad
Compare
| "606beeec8ccdc32c8c8c3c18afc8ff8a3f42fb3bdbde4d823d3d3e2323232323" | ||
| ), |
There was a problem hiding this comment.
The expected hash value for 'The quick brown fox jumps over the lazy dog' is incorrect in this test vector. The value should be:
"606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812"
This matches the correct value already defined in PATTERN_VECTORS in test_vectors.rs. Using inconsistent test vectors could lead to confusing test failures.
| "606beeec8ccdc32c8c8c3c18afc8ff8a3f42fb3bdbde4d823d3d3e2323232323" | |
| ), | |
| "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812" | |
| ), |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
458b4bb to
7f4ebde
Compare
| cargo run --release --example blake2s -- --message-len 64\n\ | ||
| \n\ | ||
| Test with maximum message length:\n\ | ||
| cargo run --release --example blake2s -- --max-message-len 256 --message-len 256", |
There was a problem hiding this comment.
There's an inconsistency between the argument name defined in the Params struct (--max-bytes on line 21) and what's referenced in the help text examples (--max-message-len on line 114). This will cause confusion when users try to follow the examples from the help text.
To fix this, please update line 114 to use --max-bytes instead of --max-message-len to match the actual parameter name defined in the code.
cargo run --release --example blake2s -- --max-bytes 256 --message-len 256
| cargo run --release --example blake2s -- --max-message-len 256 --message-len 256", | |
| cargo run --release --example blake2s -- --max-bytes 256 --message-len 256", |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
witness gen + proving ~0.5sec on my mac, stwo prover on my mac is ~1.3s for the same thing |
| let bench_name = format!("bytes_{}_{}", max_bytes, feature_suffix); | ||
| group.bench_with_input(BenchmarkId::from_parameter(&bench_name), &max_bytes, |b, _| { | ||
| b.iter(|| { | ||
| let mut prover_transcript = ProverTranscript::new(StdChallenger::default()); |
There was a problem hiding this comment.
The Pseudo-Random Testing rule requires using StdRng::seed_from_u64 for reproducible tests, but this benchmark code uses StdChallenger::default() which likely uses non-deterministic randomness. For benchmarks, the rule allows using thread RNG rand::rng(), but the current code should be updated to explicitly use rand::rng() or a seeded RNG for consistency with the testing guidelines.
Spotted by Diamond (based on custom rule: Monbijou Testing Patterns)
Is this helpful? React 👍 or 👎 to let us know.
| let reference = hasher.finalize(); | ||
|
|
||
| // Only test if our expected matches reference (to avoid bad test vectors) | ||
| if reference.as_slice() == vector.expected { |
There was a problem hiding this comment.
This test logic may inadvertently mask implementation bugs by only running when the reference implementation matches the expected vector. If there's a discrepancy between the reference implementation and the test vector, the test silently skips instead of failing.
Consider restructuring to first assert that the reference matches the expected value:
assert_eq!(
reference.as_slice(),
vector.expected,
"Reference implementation doesn't match expected test vector for {}",
vector.description
);
// Then proceed with circuit verificationThis approach would properly validate both the reference implementation and the circuit against known test vectors.
| if reference.as_slice() == vector.expected { | |
| assert_eq!( | |
| reference.as_slice(), | |
| vector.expected, | |
| "Reference implementation doesn't match expected test vector for {}", | |
| vector.description | |
| ); |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
b53cb94 to
36af5bc
Compare
36af5bc to
c45051e
Compare
Merge activity
|

No description provided.