diff --git a/barretenberg/cpp/scripts/audit/audit_scopes/bigfield_audit_scope.md b/barretenberg/cpp/scripts/audit/audit_scopes/bigfield_audit_scope.md new file mode 100644 index 000000000000..68586a4a688d --- /dev/null +++ b/barretenberg/cpp/scripts/audit/audit_scopes/bigfield_audit_scope.md @@ -0,0 +1,58 @@ +# External Audit Scope: Bigfield + +Repository: https://github.com/AztecProtocol/aztec-packages + +Commit hash: [d0ee94134b6cf290cf93cccf30354278d2bdff59](https://github.com/AztecProtocol/aztec-packages/tree/d0ee94134b6cf290cf93cccf30354278d2bdff59) + +## Files to Audit + +Note: Paths relative to `aztec-packages/barretenberg/cpp/src/barretenberg` + +1. `stdlib/primitives/bigfield/bigfield.hpp` +2. `stdlib/primitives/bigfield/bigfield_impl.hpp` +3. `stdlib/primitives/bigfield/constants.hpp` + +Relations: (wasn't explicitly in the SoW but was still audited) + +4. `relations/non_native_field_relations.hpp` + +## Summary of Module + +The `bigfield` module implements non-native field arithmetic inside a circuit. It enables arithmetic operations on field elements from a different (larger) field than the native circuit field, which is essential for operations like + +- Recursive verification of BN254-based proofs inside BN254 circuits, and +- ECDSA verification where we need to work with secp256k1/r1 field elements inside BN254-based circuits. + +**Representation**: Each `bigfield` element is represented using: + +- 4 binary basis limbs of 68 bits each (total 272 bits) +- A prime basis limb (the value mod native field modulus) +- Maximum value tracking for each limb to enable lazy reduction + +The value is: `limb[0] + limb[1] * 2^68 + limb[2] * 2^136 + limb[3] * 2^204` + +**Operations**: Implements full field arithmetic (+, -, \*, /) with: + +- Lazy reduction to minimize expensive range checks +- Chinese Remainder Theorem (CRT) for efficient multiplication verification +- Optimized gate usage (4 gates for addition, custom gates for multiplication) + +**CRT-based Multiplication**: To verify `a * b = r mod p`: + +- Checks equation holds mod 2^272 (binary basis) via schoolbook multiplication +- Checks equation holds mod native field (prime basis) via single multiplication gate +- Ensures both sides are less than CRT modulus `M = 2^272 * n` + +**Range Tracking**: The module tracks maximum values of limbs to: + +- Determine when reduction is needed before overflow +- Compute appropriate range constraints for quotient/carry values +- Enable batching multiple operations before reduction + +Please refer to the [bigfield README](https://github.com/AztecProtocol/aztec-packages/blob/d0ee94134b6cf290cf93cccf30354278d2bdff59/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/README.md) for detailed specification of the multiplication, addition, subtraction, and division algorithms. + +> Note: The README uses LaTeX notation which doesn't render well on GitHub; you might need to use Markdown preview in VS Code to render the file. + +## Test Files + +1. `stdlib/primitives/bigfield/bigfield.test.cpp` diff --git a/barretenberg/cpp/scripts/audit/audit_scopes/biggroup_audit_scope.md b/barretenberg/cpp/scripts/audit/audit_scopes/biggroup_audit_scope.md index 1737bda5b808..84dab7e37df4 100644 --- a/barretenberg/cpp/scripts/audit/audit_scopes/biggroup_audit_scope.md +++ b/barretenberg/cpp/scripts/audit/audit_scopes/biggroup_audit_scope.md @@ -1,6 +1,7 @@ # External Audit Scope: Biggroup Repository: https://github.com/AztecProtocol/aztec-packages + Commit hash: [553c5eb82901955c638b943065acd3e47fc918c0](https://github.com/AztecProtocol/aztec-packages/tree/553c5eb82901955c638b943065acd3e47fc918c0) ## Files to Audit @@ -13,24 +14,20 @@ The following files are to be audited, located in the `stdlib/primitives/biggrou 4. `stdlib/primitives/biggroup/biggroup_tables.hpp` 5. `stdlib/primitives/biggroup/biggroup_secp256k1.hpp` 6. `stdlib/primitives/biggroup/biggroup_edgecase_handling.hpp` - -Update: Fixed lookup tables are implemented in `stdlib_circuit_builders/plookup_tables/non_native_group_generator.cpp` which must be added to the scope. - 7. `stdlib_circuit_builders/plookup_tables/non_native_group_generator.cpp` 8. `stdlib_circuit_builders/plookup_tables/non_native_group_generator.hpp` ## Brief Summary of Module -The biggroup module implements elliptic-curve operations using UltraHonk arithmetisation in barretenberg. This is specifically implemented to work for three curves[^1]: bn254, secp256k1 and secp256r1. +The biggroup module implements elliptic-curve operations using UltraHonk arithmetisation in barretenberg. This is specifically implemented to work for three curves: bn254, secp256k1 and secp256r1 (see Note 1). -Please refer to the [biggroup README](https://github.com/AztecProtocol/aztec-packages/blob/553c5eb82901955c638b943065acd3e47fc918c0/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/README.md) for details on the specification and implementation details.[^2] +Please refer to the [biggroup README](https://github.com/AztecProtocol/aztec-packages/blob/553c5eb82901955c638b943065acd3e47fc918c0/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/README.md) for details on the specification and implementation details (see Note 2). + +> Note 1: Our implementation can _technically_ work for other curves as well (so long as the base and scalar fields of the curve can be represented with our bigfield module) but we have not tested it for other curves. +> +> Note 2: The README uses Latex notation which doesn't render well on Github, you might need to use Markdown preview in VS code to render the file. ## Test Files + 1. `stdlib/primitives/biggroup/biggroup.test.cpp` 2. `stdlib/primitives/biggroup/biggroup_secp256k1.test.cpp` - - - -[^1]: Our implementation can _technically_ work for other curves as well (so long as the base and scalar fields of the curve can be represented with our bigfield module) but we have not tested it for other curves. - -[^2]: The README uses Latex notation which doesn't render well on Github, you might need to use Markdown preview in VS code to render the file. diff --git a/barretenberg/cpp/scripts/audit/audit_scopes/eccvm_builder_prover_audit_scope.md b/barretenberg/cpp/scripts/audit/audit_scopes/eccvm_builder_prover_audit_scope.md deleted file mode 100644 index 2706c534722c..000000000000 --- a/barretenberg/cpp/scripts/audit/audit_scopes/eccvm_builder_prover_audit_scope.md +++ /dev/null @@ -1,34 +0,0 @@ -# ECCVM Builder/Prover Audit Scope - -Repository: https://github.com/AztecProtocol/aztec-packages -Commit hash: TBD - -## Files to Audit -Note: Paths relative to `aztec-packages/barretenberg/cpp/src/barretenberg` - -### ECCVM Core -1. `eccvm/eccvm_builder_types.hpp` -2. `eccvm/eccvm_prover.cpp` -3. `eccvm/eccvm_prover.hpp` - -### Op Queue -4. `op_queue/eccvm_row_tracker.hpp` - -### Stdlib Goblin Components -5. `stdlib/primitives/bigfield/goblin_field.hpp` -6. `stdlib/primitives/biggroup/biggroup_goblin.hpp` -7. `stdlib/primitives/biggroup/biggroup_goblin_impl.hpp` - -## Summary of Module - -The ECCVM builder/prover module implements the circuit construction and proof generation for the Elliptic Curve Virtual Machine (ECCVM). The ECCVM is used in Goblin to handle non-native elliptic curve operations efficiently by deferring them to a separate VM. The builder types define the circuit structure and the prover generates proofs of ECCVM execution. The op queue row tracker manages the accumulation of elliptic curve operations, while the stdlib Goblin components (goblin_field and biggroup_goblin) provide circuit-friendly implementations of field arithmetic and group operations specifically optimized for Goblin's deferred execution model. - -## Test Files -1. `eccvm/eccvm.test.cpp` -2. `stdlib/primitives/bigfield/bigfield_goblin.test.cpp` -3. `stdlib/primitives/biggroup/biggroup_goblin.test.cpp` - -## Security Mechanisms -- ECCVM circuit constraints ensure correctness of elliptic curve operations -- Op queue tracking maintains operation ordering and consistency -- Goblin field and biggroup components provide secure deferred execution of EC operations diff --git a/barretenberg/cpp/scripts/audit/audit_scopes/logic_scope_doc.md b/barretenberg/cpp/scripts/audit/audit_scopes/logic_scope_doc.md new file mode 100644 index 000000000000..4c053d90b222 --- /dev/null +++ b/barretenberg/cpp/scripts/audit/audit_scopes/logic_scope_doc.md @@ -0,0 +1,40 @@ +# External Audit Scope: Logic + +Repository: https://github.com/AztecProtocol/aztec-packages + +Commit hash: TBD + +## Files to Audit + +Note: Paths relative to `aztec-packages/barretenberg/cpp/src/barretenberg` + +1. `stdlib/primitives/logic/logic.hpp` +2. `stdlib/primitives/logic/logic.cpp` +3. `stdlib_circuit_builders/plookup_tables/uint.hpp` (lookup tables) + +## Summary of Module + +The `logic` module provides circuit-friendly implementations of bitwise logical operations (XOR and AND) over variable-length unsigned integers using plookup tables. + +Main function: `create_logic_constraint(a, b, num_bits, is_xor_gate)` + +- Computes `a XOR b` or `a AND b` for inputs up to `num_bits` in length +- Supports inputs up to 252 bits (grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH) + +The implementation: + +- Decomposes inputs into 32-bit chunks +- Performs lookups against `UINT32_XOR` or `UINT32_AND` multi-tables for each chunk +- The lookup operation implicitly enforces 32-bit range constraints on each chunk +- For non-32-bit-aligned inputs, the final chunk is explicitly range-constrained to the remaining bits +- Input values are reconstructed from chunks and verified via `assert_equal` +- If both inputs are constants, the operation is computed natively without circuit constraints +- If one input is constant, it is converted to a witness before processing + +## Test Files + +1. `stdlib/primitives/logic/logic.test.cpp` + +## Dependencies + +- Plookup read: `stdlib/primitives/plookup/plookup.hpp` diff --git a/barretenberg/cpp/scripts/audit/audit_scopes/ultra_mega_builder_audit_scope.md b/barretenberg/cpp/scripts/audit/audit_scopes/ultra_mega_builder_audit_scope.md index 55074f8d8ec1..9d75be60bf9b 100644 --- a/barretenberg/cpp/scripts/audit/audit_scopes/ultra_mega_builder_audit_scope.md +++ b/barretenberg/cpp/scripts/audit/audit_scopes/ultra_mega_builder_audit_scope.md @@ -1,16 +1,17 @@ # External Audit Scope: Ultra + MegaCircuitBuilder Repository: https://github.com/AztecProtocol/aztec-packages -Commit hash: To be added in January + +Commit hash: [6d14241271ad07c72937498b66f28df630662c2c](https://github.com/AztecProtocol/aztec-packages/tree/6d14241271ad07c72937498b66f28df630662c2c) + Status: Planned, [Luke, Raju] ## Files to Audit Note: Paths relative to `aztec-packages/barretenberg/cpp/src/barretenberg` - - ### Circuit Builders + 1. `stdlib_circuit_builders/circuit_builder_base.hpp` 2. `stdlib_circuit_builders/circuit_builder_base_impl.hpp` 3. `stdlib_circuit_builders/ultra_circuit_builder.hpp` @@ -23,6 +24,7 @@ Note: Paths relative to `aztec-packages/barretenberg/cpp/src/barretenberg` 10. `honk/execution_trace/gate_data.hpp` ### Relations (Ultra) + 11. `relations/ultra_arithmetic_relation.hpp` 12. `relations/permutation_relation.hpp` 13. `relations/logderiv_lookup_relation.hpp` @@ -34,10 +36,12 @@ Note: Paths relative to `aztec-packages/barretenberg/cpp/src/barretenberg` 19. `relations/poseidon2_internal_relation.hpp` ### Relations (Mega-only) + 20. `relations/ecc_op_queue_relation.hpp` 21. `relations/databus_lookup_relation.hpp` ### Lookup infrastructure + 22. `stdlib_circuit_builders/plookup_tables/plookup_tables.hpp` 23. `stdlib_circuit_builders/plookup_tables/plookup_tables.cpp` 24. `stdlib_circuit_builders/plookup_tables/types.hpp` @@ -48,21 +52,34 @@ Note: Paths relative to `aztec-packages/barretenberg/cpp/src/barretenberg` ### ECC Op Queue The following is "joint" functionality for the ECCVM and the Mega circuit builder (called `UltraOps`. In this audit, we only care about how things are represented in the Mega circuit builder. + 28. `op_queue/ecc_op_queue.hpp` 29. `op_queue/ecc_ops_table.hpp` (especially the `UltraEccOpsTable` class and its methods) +### Stdlib Goblin Components + +We represent bn254 group elements in the Mega circuit builder using "Goblinized" representations. Particularly, the `goblin_field` represents bn254 base field elements (x, y coordinates), and the `biggroup_goblin` represents bn254 group elements. + +30. `stdlib/primitives/bigfield/goblin_field.hpp` +31. `stdlib/primitives/biggroup/biggroup_goblin.hpp` +32. `stdlib/primitives/biggroup/biggroup_goblin_impl.hpp` + ### Databus + Within this audit, it is important to make sure that the databus "correctly links up" with the Mega circuit builder. Therefore, the following file is also in the scope of the audit. -30. `stdlib_circuit_builders/databus.hpp` + +33. `stdlib_circuit_builders/databus.hpp` ### ACIR Format -31. `dsl/acir_format/range_constraint.hpp` + +34. `dsl/acir_format/range_constraint.hpp` ## Brief Summary of Module The Ultra/MegaCircuitBuilder module implements the core circuit construction infrastructure for Barretenberg's proving system. **Class Hierarchy:** + ``` CircuitBuilderBase └── UltraCircuitBuilder_ @@ -78,6 +95,7 @@ CircuitBuilderBase ## Test Files ### Circuit Builder Tests + 1. `circuit_checker/ultra_circuit_builder_basic.test.cpp` 2. `circuit_checker/ultra_circuit_builder_arithmetic.test.cpp` 3. `circuit_checker/ultra_circuit_builder_elliptic.test.cpp` @@ -89,9 +107,11 @@ CircuitBuilderBase 9. `circuit_checker/mega_circuit_builder.test.cpp` ### Relation Tests + 10. `relations/ultra_relation_consistency.test.cpp` ### Test Utilities + 1. `circuit_checker/circuit_checker.hpp` 2. `circuit_checker/ultra_circuit_checker.hpp` 3. `circuit_checker/ultra_circuit_checker.cpp` @@ -99,10 +119,13 @@ CircuitBuilderBase ## Security Mechanisms ### SMT (Satisfiability Modulo Theories) Verification + 1. `smt_verification/circuit/ultra_circuit.test.cpp` ## Misc. Tests (NOT part of the audit, but might be helpful to situation) + The full prove-verify testing package is more extensive than the mere `circuit_checker` tests. Therefore, the following tests might be helpful as reference points. + 1. `ultra_honk/lookup.test.cpp` 2. `ultra_honk/permutation.test.cpp` 3. `ultra_honk/rom_ram.test.cpp` diff --git a/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh b/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh index 1e54a714e0df..7764f2510242 100755 --- a/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh +++ b/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh @@ -13,7 +13,7 @@ cd .. # - Generate a hash for versioning: sha256sum bb-chonk-inputs.tar.gz # - Upload the compressed results: aws s3 cp bb-chonk-inputs.tar.gz s3://aztec-ci-artifacts/protocol/bb-chonk-inputs-[hash(0:8)].tar.gz # Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0 -pinned_short_hash="db8f42e3" +pinned_short_hash="0d7388db" pinned_chonk_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-chonk-inputs-${pinned_short_hash}.tar.gz" script_path="$(cd "$(dirname "${BASH_SOURCE[0]}")/scripts" && pwd)/$(basename "${BASH_SOURCE[0]}")" diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp index 1c79489b5f9f..a014a1e5261a 100644 --- a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp @@ -1266,6 +1266,47 @@ inline void StaticAnalyzer_::remove_unnecessary_sha256_plook } } +/** + * @brief This method removes false positive cases from keccak lookup tables. + * Tables which are enumerated in keccak_plookup_tables are used by keccak lookup constraints. Some lookup-gate outputs + * are auxiliary (e.g. MSB) and may appear in only one gate but this is not dangerous. So we remove these variables. + * @tparam FF + * @tparam CircuitBuilder + * @param table_id + * @param gate_index + */ +template +inline void StaticAnalyzer_::remove_unnecessary_keccak_plookup_variables(BasicTableId& table_id, + size_t gate_index) +{ + auto find_position = [&](uint32_t real_variable_index) { + return variables_in_one_gate.contains(real_variable_index); + }; + + std::unordered_set keccak_plookup_tables{ + BasicTableId::KECCAK_INPUT, BasicTableId::KECCAK_OUTPUT, BasicTableId::KECCAK_CHI, BasicTableId::KECCAK_THETA, + BasicTableId::KECCAK_RHO, BasicTableId::KECCAK_RHO_1, BasicTableId::KECCAK_RHO_2, BasicTableId::KECCAK_RHO_3, + BasicTableId::KECCAK_RHO_4, BasicTableId::KECCAK_RHO_5, BasicTableId::KECCAK_RHO_6, BasicTableId::KECCAK_RHO_7, + BasicTableId::KECCAK_RHO_8, BasicTableId::KECCAK_RHO_9 + }; + + auto& lookup_block = circuit_builder.blocks.lookup; + + if (keccak_plookup_tables.contains(table_id)) { + uint32_t real_out_idx = this->to_real(lookup_block.w_o()[gate_index]); + uint32_t real_right_idx = this->to_real(lookup_block.w_r()[gate_index]); + if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) { + bool find_out = find_position(real_out_idx); + auto q_c = lookup_block.q_c()[gate_index]; + if (q_c.is_zero()) { + if (find_out) { + variables_in_one_gate.erase(real_out_idx); + } + } + } + } +} + /** * @brief this method removes false cases in lookup table for a given gate. * it uses all functions above for lookup tables to remove all variables that appear in one gate, @@ -1294,6 +1335,8 @@ inline void StaticAnalyzer_::process_current_plookup_gate(si this->remove_unnecessary_aes_plookup_variables(table_id, gate_index); // false cases for sha256 this->remove_unnecessary_sha256_plookup_variables(table_id, gate_index); + // false cases for keccak + this->remove_unnecessary_keccak_plookup_variables(table_id, gate_index); // if the amount of unique elements from columns of plookup tables = 1, it means that // variable from this column aren't used and we can remove it. if (column_1.size() == 1) { diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp index e82edf3e2441..5c81fb0174a2 100644 --- a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp @@ -142,6 +142,7 @@ template class StaticAnalyzer_ { void remove_unnecessary_range_constrains_variables(); void remove_unnecessary_aes_plookup_variables(bb::plookup::BasicTableId& table_id, size_t gate_index); void remove_unnecessary_sha256_plookup_variables(bb::plookup::BasicTableId& table_id, size_t gate_index); + void remove_unnecessary_keccak_plookup_variables(bb::plookup::BasicTableId& table_id, size_t gate_index); void remove_record_witness_variables(); std::unordered_set get_variables_in_one_gate(); diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_keccak.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_keccak.test.cpp new file mode 100644 index 000000000000..10702ef509d4 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_keccak.test.cpp @@ -0,0 +1,53 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" + +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/stdlib/hash/keccak/keccak.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include +#include + +using namespace bb; +using namespace bb::stdlib; +using namespace cdg; + +using Builder = UltraCircuitBuilder; +using field_ct = field_t; +using witness_ct = witness_t; + +/** + * @brief Fix witness for an array of field elements + * + * Static analyzer prints variables that only appear in one gate. By fixing witnesses, + * we ensure variables appear in at least 2 gates, filtering out false positives. + */ +template void fix_field_array(std::array& arr) +{ + for (auto& elem : arr) { + elem.fix_witness(); + } +} + +TEST(boomerang_stdlib_keccak, test_graph_for_keccakf1600) +{ + Builder builder; + + // 25-lane input state as witnesses + std::array::NUM_KECCAK_LANES> state; + for (size_t i = 0; i < state.size(); ++i) { + state[i] = witness_ct(&builder, static_cast(i + 1)); + } + fix_field_array(state); + + auto out_state = keccak::permutation_opcode(state, &builder); + fix_field_array(out_state); + + // Analyze graph structure + StaticAnalyzer graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); + + auto variables_in_one_gate = graph.get_variables_in_one_gate(); + EXPECT_EQ(variables_in_one_gate.size(), 0); +} diff --git a/barretenberg/cpp/src/barretenberg/common/assert.hpp b/barretenberg/cpp/src/barretenberg/common/assert.hpp index 7041dc2c1aaf..5fcb7434273f 100644 --- a/barretenberg/cpp/src/barretenberg/common/assert.hpp +++ b/barretenberg/cpp/src/barretenberg/common/assert.hpp @@ -55,16 +55,6 @@ struct AssertGuard { #define BB_ASSERT_DEBUG(expression, ...) BB_ASSERT(expression, __VA_ARGS__) #endif // NDEBUG -#ifdef __wasm__ -#define BB_ASSERT(expression, ...) DONT_EVALUATE((expression)) - -#define BB_ASSERT_EQ(actual, expected, ...) DONT_EVALUATE((actual) == (expected)) -#define BB_ASSERT_NEQ(actual, expected, ...) DONT_EVALUATE((actual) != (expected)) -#define BB_ASSERT_GT(left, right, ...) DONT_EVALUATE((left) > (right)) -#define BB_ASSERT_GTE(left, right, ...) DONT_EVALUATE((left) >= (right)) -#define BB_ASSERT_LT(left, right, ...) DONT_EVALUATE((left) < (right)) -#define BB_ASSERT_LTE(left, right, ...) DONT_EVALUATE((left) <= (right)) -#else #ifdef FUZZING_DISABLE_WARNINGS #define BB_ASSERT(expression, ...) \ do { \ @@ -179,7 +169,18 @@ struct AssertGuard { bb::assert_failure(oss.str()); \ } \ } while (0) -#endif // __wasm__ + +// BB_ASSERT_NO_WASM: Use this for asserts that are too expensive to run in WASM +// (e.g., asserts inside hot loops or with expensive computations) +#ifdef __wasm__ +#define BB_ASSERT_NO_WASM(expression, ...) DONT_EVALUATE((expression)) +#define BB_ASSERT_EQ_NO_WASM(actual, expected, ...) DONT_EVALUATE((actual) == (expected)) +#define BB_ASSERT_LT_NO_WASM(left, right, ...) DONT_EVALUATE((left) < (right)) +#else +#define BB_ASSERT_NO_WASM(expression, ...) BB_ASSERT(expression, __VA_ARGS__) +#define BB_ASSERT_EQ_NO_WASM(actual, expected, ...) BB_ASSERT_EQ(actual, expected, __VA_ARGS__) +#define BB_ASSERT_LT_NO_WASM(left, right, ...) BB_ASSERT_LT(left, right, __VA_ARGS__) +#endif // These are used in tests. #ifdef BB_NO_EXCEPTIONS diff --git a/barretenberg/cpp/src/barretenberg/common/google_bb_bench.hpp b/barretenberg/cpp/src/barretenberg/common/google_bb_bench.hpp index a1da56ad982f..520726ca08c5 100644 --- a/barretenberg/cpp/src/barretenberg/common/google_bb_bench.hpp +++ b/barretenberg/cpp/src/barretenberg/common/google_bb_bench.hpp @@ -2,7 +2,7 @@ #pragma once #include -#ifdef __wasm__ +#if defined(__wasm__) && !defined(ENABLE_WASM_BENCH) namespace bb { struct GoogleBbBenchReporter { GoogleBbBenchReporter(::benchmark::State& state) diff --git a/barretenberg/cpp/src/barretenberg/common/ref_array.hpp b/barretenberg/cpp/src/barretenberg/common/ref_array.hpp index 79e929c94108..326a51818d56 100644 --- a/barretenberg/cpp/src/barretenberg/common/ref_array.hpp +++ b/barretenberg/cpp/src/barretenberg/common/ref_array.hpp @@ -89,7 +89,7 @@ template class RefArray { T& operator*() const { - BB_ASSERT_LT(pos, N); + BB_ASSERT_LT_NO_WASM(pos, N); return (*array)[pos]; } diff --git a/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2-impl.hpp b/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2-impl.hpp index a5dcdbd306a4..479f291872ee 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2-impl.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2-impl.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== @@ -154,4 +154,4 @@ static BLAKE2_INLINE void secure_zero_memory(void* v, size_t n) } // namespace bb::crypto -#endif \ No newline at end of file +#endif diff --git a/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.cpp b/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.cpp index 1aaea491b2f1..dfbeaa9c4ed9 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.cpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.hpp b/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.hpp index e3f043871102..7de4178cafc9 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.test.cpp b/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.test.cpp index 0ef048a6295d..fb62e2885994 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.test.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/blake2s/blake2s.test.cpp @@ -1,3 +1,9 @@ +// === AUDIT STATUS === +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} +// external_1: { status: not started, auditors: [], commit: } +// external_2: { status: not started, auditors: [], commit: } +// ===================== + #include "blake2s.hpp" #include @@ -386,4 +392,4 @@ TEST(misc_blake2s, test_vectors) std::vector input(v.input.begin(), v.input.end()); EXPECT_EQ(crypto::blake2s(input), v.output); } -} \ No newline at end of file +} diff --git a/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3-impl.hpp b/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3-impl.hpp index d94330b16bca..010e688e70c4 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3-impl.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3-impl.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== @@ -83,4 +83,4 @@ constexpr void store_cv_words(out_array& bytes_out, key_array& cv_words) #include "blake3s.tcc" -#endif \ No newline at end of file +#endif diff --git a/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.hpp b/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.hpp index a21b3a006da5..f426d81a0316 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.tcc b/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.tcc index c5f57198e715..d140fe9a7350 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.tcc +++ b/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.tcc @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.test.cpp b/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.test.cpp index 8f76fff8e476..2adbb452f816 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.test.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/blake3s/blake3s.test.cpp @@ -1,3 +1,9 @@ +// === AUDIT STATUS === +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} +// external_1: { status: not started, auditors: [], commit: } +// external_2: { status: not started, auditors: [], commit: } +// ===================== + #include "blake3s.hpp" #include diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp index c6109b06a86f..562e22016322 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp @@ -622,21 +622,13 @@ void add_blackbox_func_call_to_acir_format(Acir::Opcode::BlackBoxFuncCall const& af.original_opcode_indices.sha256_compression.push_back(opcode_index); } else if constexpr (std::is_same_v) { af.blake2s_constraints.push_back(Blake2sConstraint{ - .inputs = transform::map(arg.inputs, - [&](auto& e) { - return Blake2sInput{ - .blackbox_input = parse_input(e), - .num_bits = 8, - }; - }), + .inputs = transform::map(arg.inputs, to_witness_or_constant), .result = transform::map(*arg.outputs, to_witness), }); af.original_opcode_indices.blake2s_constraints.push_back(opcode_index); } else if constexpr (std::is_same_v) { af.blake3_constraints.push_back(Blake3Constraint{ - .inputs = transform::map( - arg.inputs, - [&](auto& e) { return Blake3Input{ .blackbox_input = parse_input(e), .num_bits = 8 }; }), + .inputs = transform::map(arg.inputs, to_witness_or_constant), .result = transform::map(*arg.outputs, to_witness), }); af.original_opcode_indices.blake3_constraints.push_back(opcode_index); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/aes128_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/aes128_constraint.cpp index 626781749c4d..7f97ad2642a1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/aes128_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/aes128_constraint.cpp @@ -48,7 +48,7 @@ template void create_aes128_constraints(Builder& builder, con return converted; }; - const size_t padding_size = 16 - constraint.inputs.size() % 16; + const size_t padding_size = 16 - (constraint.inputs.size() % 16); // Perform the conversions from array of bytes to field elements std::vector converted_inputs; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/aes128_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/aes128_constraint.hpp index 03483b6c1d81..2263d15e1220 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/aes128_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/aes128_constraint.hpp @@ -13,13 +13,6 @@ namespace acir_format { -struct AES128Input { - uint32_t witness; - uint32_t num_bits; - - friend bool operator==(AES128Input const& lhs, AES128Input const& rhs) = default; -}; - struct AES128Constraint { std::vector> inputs; std::array, 16> iv; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.cpp index 82d8457848d0..05b1104d277c 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.cpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== @@ -21,8 +21,7 @@ template void create_blake2s_constraints(Builder& builder, co // Build input byte array by appending constrained byte_arrays byte_array_ct arr = byte_array_ct::constant_padding(&builder, 0); // Start with empty array - for (const auto& witness_index_num_bits : constraint.inputs) { - auto witness_index = witness_index_num_bits.blackbox_input; + for (const auto& witness_index : constraint.inputs) { field_ct element = to_field_ct(witness_index, builder); // byte_array_ct(field, num_bytes) constructor adds range constraints for each byte. Note that num_bytes = diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.hpp index a1deabe7c060..27cdf7ef839d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== @@ -12,15 +12,8 @@ namespace acir_format { -struct Blake2sInput { - WitnessOrConstant blackbox_input; - uint32_t num_bits; - - friend bool operator==(Blake2sInput const& lhs, Blake2sInput const& rhs) = default; -}; - struct Blake2sConstraint { - std::vector inputs; + std::vector> inputs; std::array result; friend bool operator==(Blake2sConstraint const& lhs, Blake2sConstraint const& rhs) = default; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.test.cpp index 83a36f6dd686..81270e8ce91d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake2s_constraint.test.cpp @@ -1,3 +1,9 @@ +// === AUDIT STATUS === +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} +// external_1: { status: not started, auditors: [], commit: } +// external_2: { status: not started, auditors: [], commit: } +// ===================== + #include "blake2s_constraint.hpp" #include "acir_format.hpp" #include "barretenberg/crypto/blake2s/blake2s.hpp" @@ -39,10 +45,9 @@ template class Blake2sTestingFunctions case InvalidWitness::Target::Input: { // Tamper with the first input element if constexpr (IsInputConstant) { - constraint.inputs[0].blackbox_input = - WitnessOrConstant::from_constant(constraint.inputs[0].blackbox_input.value + bb::fr(1)); + constraint.inputs[0] = WitnessOrConstant::from_constant(constraint.inputs[0].value + bb::fr(1)); } else { - witness_values[constraint.inputs[0].blackbox_input.index] += bb::fr(1); + witness_values[constraint.inputs[0].index] += bb::fr(1); } break; } @@ -89,8 +94,7 @@ template class Blake2sTestingFunctions // Create the constraint blake2s_constraint.inputs.reserve(input_state.size()); for (const auto& state : construct_state(input_state, IsInputConstant)) { - Blake2sInput input{ .blackbox_input = state, .num_bits = 8 }; - blake2s_constraint.inputs.push_back(input); + blake2s_constraint.inputs.push_back(state); } // Add output state to witness diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.cpp index 14b0b0f3c321..e95e3855ad76 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.cpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== @@ -19,8 +19,7 @@ template void create_blake3_constraints(Builder& builder, con // Build input byte array by appending constrained byte_arrays byte_array_ct arr = byte_array_ct::constant_padding(&builder, 0); // Start with empty array - for (const auto& witness_index_num_bits : constraint.inputs) { - auto witness_index = witness_index_num_bits.blackbox_input; + for (const auto& witness_index : constraint.inputs) { field_ct element = to_field_ct(witness_index, builder); // byte_array_ct(field, num_bytes) constructor adds range constraints for each byte. Note that num_bytes = diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.hpp index 929178b0d8f8..7f10113cb3ab 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== @@ -12,15 +12,8 @@ namespace acir_format { -struct Blake3Input { - WitnessOrConstant blackbox_input; - uint32_t num_bits; - - friend bool operator==(Blake3Input const& lhs, Blake3Input const& rhs) = default; -}; - struct Blake3Constraint { - std::vector inputs; + std::vector> inputs; std::array result; friend bool operator==(Blake3Constraint const& lhs, Blake3Constraint const& rhs) = default; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.test.cpp index 3bddcb5a9e55..629ac4906914 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.test.cpp @@ -1,3 +1,9 @@ +// === AUDIT STATUS === +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} +// external_1: { status: not started, auditors: [], commit: } +// external_2: { status: not started, auditors: [], commit: } +// ===================== + #include "blake3_constraint.hpp" #include "acir_format.hpp" #include "barretenberg/crypto/blake3s/blake3s.hpp" @@ -39,10 +45,9 @@ template class Blake3TestingFunctions case InvalidWitness::Target::Input: { // Tamper with the first input element if constexpr (IsInputConstant) { - constraint.inputs[0].blackbox_input = - WitnessOrConstant::from_constant(constraint.inputs[0].blackbox_input.value + bb::fr(1)); + constraint.inputs[0] = WitnessOrConstant::from_constant(constraint.inputs[0].value + bb::fr(1)); } else { - witness_values[constraint.inputs[0].blackbox_input.index] += bb::fr(1); + witness_values[constraint.inputs[0].index] += bb::fr(1); } break; } @@ -89,8 +94,7 @@ template class Blake3TestingFunctions // Create the constraint blake3_constraint.inputs.reserve(input_state.size()); for (const auto& state : construct_state(input_state, IsInputConstant)) { - Blake3Input input{ .blackbox_input = state, .num_bits = 8 }; - blake3_constraint.inputs.push_back(input); + blake3_constraint.inputs.push_back(state); } // Add output state to witness diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp index 03d617779909..0e88d8489cbd 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp @@ -27,7 +27,7 @@ template inline constexpr size_t BIG_QUAD = 2 + ZERO_GATE + M template inline constexpr size_t LOGIC_XOR_32 = 6 + ZERO_GATE + MEGA_OFFSET; template inline constexpr size_t LOGIC_AND_32 = 6 + ZERO_GATE + MEGA_OFFSET; template inline constexpr size_t RANGE_32 = 2744 + ZERO_GATE + MEGA_OFFSET; -template inline constexpr size_t SHA256_COMPRESSION = 6679 + ZERO_GATE + MEGA_OFFSET; +template inline constexpr size_t SHA256_COMPRESSION = 6695 + ZERO_GATE + MEGA_OFFSET; template inline constexpr size_t AES128_ENCRYPTION = 1432 + ZERO_GATE + MEGA_OFFSET; // The mega offset works differently for ECDSA opcodes because of the use of ROM tables, which use indices that diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/hypernova_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/hypernova_recursion_constraint.test.cpp index d978be39c7b4..d646b5292efc 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/hypernova_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/hypernova_recursion_constraint.test.cpp @@ -765,7 +765,7 @@ TEST_F(HypernovaRecursionConstraintTest, FailsOnAcirQueueSizeMismatch) ProgramMetadata metadata{ .ivc = ivc }; EXPECT_THROW_WITH_MESSAGE(acir_format::create_circuit(program, metadata), - "mismatch between ACIR constraints"); + "mismatch in number of recursive verifications"); } /** diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/opcode_gate_count.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/opcode_gate_count.test.cpp index 016e42636129..279b34ee4253 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/opcode_gate_count.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/opcode_gate_count.test.cpp @@ -398,10 +398,7 @@ TYPED_TEST(OpcodeGateCountTests, Blake2s) { Blake2sConstraint blake2s_constraint; - blake2s_constraint.inputs.push_back(Blake2sInput{ - .blackbox_input = WitnessOrConstant::from_index(0), - .num_bits = 8, - }); + blake2s_constraint.inputs.push_back(WitnessOrConstant::from_index(0)); for (size_t i = 0; i < 32; ++i) { blake2s_constraint.result[i] = static_cast(i + 1); @@ -428,10 +425,7 @@ TYPED_TEST(OpcodeGateCountTests, Blake3) { Blake3Constraint blake3_constraint; - blake3_constraint.inputs.push_back(Blake3Input{ - .blackbox_input = WitnessOrConstant::from_index(0), - .num_bits = 8, - }); + blake3_constraint.inputs.push_back(WitnessOrConstant::from_index(0)); for (size_t i = 0; i < 32; ++i) { blake3_constraint.result[i] = static_cast(i + 1); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index ea267a67b2e8..366e8372d022 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -30,18 +30,14 @@ HonkRecursionConstraintsOutput create_recursion_constraints( bool has_chonk_recursion_constraints = !chonk_recursion_data.first.empty(); // Schema invariants: validate constraint type combinations for MegaBuilder - if (has_honk_recursion_constraints && has_hn_recursion_constraints) { - throw_or_abort( - "create_recursion_constraints: invalid circuit - both honk and HN recursion constraints present"); - } - if (has_avm_recursion_constraints) { - throw_or_abort("create_recursion_constraints: invalid circuit - AVM recursion constraints not supported with " - "MegaBuilder"); - } - if (has_chonk_recursion_constraints) { - throw_or_abort("create_recursion_constraints: invalid circuit - Chonk recursion constraints not supported with " - "MegaBuilder"); - } + BB_ASSERT(!(has_honk_recursion_constraints && has_hn_recursion_constraints), + "create_recursion_constraints: invalid circuit - both honk and HN recursion constraints present"); + BB_ASSERT( + !has_avm_recursion_constraints, + "create_recursion_constraints: invalid circuit - AVM recursion constraints not supported with MegaBuilder"); + BB_ASSERT(!has_chonk_recursion_constraints, + "create_recursion_constraints: invalid circuit - Chonk recursion constraints not supported with " + "MegaBuilder"); HonkRecursionConstraintsOutput output; @@ -88,14 +84,11 @@ HonkRecursionConstraintsOutput create_recursion_constraints bool has_chonk_recursion_constraints = !chonk_recursion_data.first.empty(); // Schema invariants: validate constraint type combinations for UltraBuilder - if (has_hn_recursion_constraints) { - throw_or_abort( - "create_recursion_constraints: invalid circuit - HN recursion constraints not supported with UltraBuilder"); - } - if (has_chonk_recursion_constraints && has_honk_recursion_constraints) { - throw_or_abort( - "create_recursion_constraints: invalid circuit - both honk and chonk recursion constraints present"); - } + BB_ASSERT( + !has_hn_recursion_constraints, + "create_recursion_constraints: invalid circuit - HN recursion constraints not supported with UltraBuilder"); + BB_ASSERT(!(has_chonk_recursion_constraints && has_honk_recursion_constraints), + "create_recursion_constraints: invalid circuit - both honk and chonk recursion constraints present"); if (has_chonk_recursion_constraints && has_avm_recursion_constraints) { vinfo("WARNING: both chonk and avm recursion constraints are present. While we support this combination, we " "expect to see it only in a mock circuit."); @@ -166,18 +159,17 @@ void process_hn_recursion_constraints( using StdlibFF = Chonk::RecursiveFlavor::FF; // Validate hn_recursion_data constraints/indices size match - if (hn_recursion_data.first.size() != hn_recursion_data.second.size()) { - throw_or_abort("process_hn_recursion_constraints: hn_recursion_data constraints/indices size mismatch"); - } + BB_ASSERT_EQ(hn_recursion_data.first.size(), + hn_recursion_data.second.size(), + "process_hn_recursion_constraints: hn_recursion_data constraints/indices size mismatch"); // Lambda template to handle both Chonk and Chonk with the same code auto process_with_ivc = [&](const std::shared_ptr& ivc) { // We expect the length of the internal verification queue to match the number of ivc recursion constraints - if (hn_recursion_data.first.size() != ivc->verification_queue.size()) { - throw_or_abort("process_hn_recursion_constraints: mismatch between ACIR constraints (" + - std::to_string(hn_recursion_data.first.size()) + ") and IVC verification queue (" + - std::to_string(ivc->verification_queue.size()) + ")"); - } + BB_ASSERT_EQ(hn_recursion_data.first.size(), + ivc->verification_queue.size(), + "process_hn_recursion_constraints: mismatch in number of recursive verifications during kernel " + "creation!"); // If no witness is provided, populate the VK and public inputs in the recursion constraint with dummy values so // that the present kernel circuit is constructed correctly. (Used for constructing VKs without witnesses). @@ -204,47 +196,38 @@ void process_hn_recursion_constraints( ivc->instantiate_stdlib_verification_queue(builder, stdlib_vk_and_hashs); // Verify stdlib queue size matches after instantiation (invariant check) - if (ivc->stdlib_verification_queue.size() != hn_recursion_data.first.size()) { - throw_or_abort("process_hn_recursion_constraints: stdlib_verification_queue size (" + - std::to_string(ivc->stdlib_verification_queue.size()) + ") != ACIR constraints size (" + - std::to_string(hn_recursion_data.first.size()) + ") after instantiation"); - } + BB_ASSERT_EQ(ivc->stdlib_verification_queue.size(), + hn_recursion_data.first.size(), + "process_hn_recursion_constraints: stdlib_verification_queue size mismatch after instantiation"); // Validate constraints against stdlib verification queue entries for (auto [constraint, queue_entry] : zip_view(hn_recursion_data.first, ivc->stdlib_verification_queue)) { // Validate ACIR constraint proof_type matches IVC queue type - if (proof_type_to_chonk_queue_type(constraint.proof_type) != queue_entry.type) { - throw_or_abort("process_hn_recursion_constraints: ACIR constraint proof_type does not match IVC queue " - "type"); - } + BB_ASSERT(proof_type_to_chonk_queue_type(constraint.proof_type) == queue_entry.type, + "process_hn_recursion_constraints: ACIR constraint proof_type does not match IVC queue type"); // HN recursion constraints from Noir always have empty public_inputs - the public inputs are handled // entirely by the IVC (KernelIO/AppIO). If this changes in the future, we need to implement binding // between ACIR public inputs and proof public inputs. - if (!constraint.public_inputs.empty()) { - throw_or_abort( - "process_hn_recursion_constraints: unexpected non-empty public_inputs in HN constraint - " - "Noir HN constraints should have empty public_inputs (public inputs are handled by IVC IO)"); - } + BB_ASSERT(constraint.public_inputs.empty(), + "process_hn_recursion_constraints: unexpected non-empty public_inputs in HN constraint - " + "Noir HN constraints should have empty public_inputs (public inputs are handled by IVC IO)"); // Validate public input layout: IO region size must match VK's num_public_inputs size_t expected_io_size = queue_entry.is_kernel ? IVCType::KernelIO::PUBLIC_INPUTS_SIZE : IVCType::AppIO::PUBLIC_INPUTS_SIZE; size_t vk_num_public_inputs = static_cast(uint64_t(queue_entry.honk_vk_and_hash->vk->num_public_inputs.get_value())); - if (expected_io_size != vk_num_public_inputs) { - throw_or_abort("process_hn_recursion_constraints: IO size mismatch - expected " + - std::to_string(expected_io_size) + " but VK has num_public_inputs " + - std::to_string(vk_num_public_inputs)); - } + BB_ASSERT_EQ(expected_io_size, + vk_num_public_inputs, + "process_hn_recursion_constraints: IO size mismatch with VK num_public_inputs"); // Sanity check: proof vector should have at least num_public_inputs elements // (HN proofs store public inputs at the start of the proof vector) - if (queue_entry.proof.size() < vk_num_public_inputs) { - throw_or_abort("process_hn_recursion_constraints: proof vector size (" + - std::to_string(queue_entry.proof.size()) + ") smaller than num_public_inputs (" + - std::to_string(vk_num_public_inputs) + ") - malformed proof"); - } + BB_ASSERT_GTE(queue_entry.proof.size(), + vk_num_public_inputs, + "process_hn_recursion_constraints: proof vector smaller than num_public_inputs - malformed " + "proof"); } // Complete the kernel circuit with all required recursive verifications, databus consistency checks etc. diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.cpp index 3ffffb1bc4a1..6c1821e1ac18 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.cpp @@ -18,13 +18,9 @@ void create_sha256_compression_constraints(Builder& builder, const Sha256Compres std::array hash_inputs; // previous (or initial) hash state std::array inputs; // message block to compress - // Get the witness assignment for each witness index - // AUDITTODO: We do not range-check the inputs here, assuming lookup tables in sha256_block - // provide implicit 32-bit constraints. However, analysis shows this assumption is incomplete: - // - inputs[0] is NEVER lookup-constrained - // - hash_values[3] and hash_values[7] are used in arithmetic before being lookup-constrained - // These values are only weakly bounded (~35 bits) by add_normalize overflow constraints. - // See AUDITTODO in stdlib/hash/sha256/sha256.cpp for details and recommended fix. + // Get the witness assignment for each witness index. + // It is assumed that the caller (Noir) separately constrains all 24 inputs (8 hash state + 16 message words) to 32 + // bits, e.g. via instantiating them as u32 types. for (auto [input, witness_or_constant] : zip_view(inputs, constraint.inputs)) { input = to_field_ct(witness_or_constant, builder); } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/test_class.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/test_class.hpp index 1e971dabb12b..63bc86d0cbe9 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/test_class.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/test_class.hpp @@ -316,7 +316,7 @@ template std::vector constraint_to_acir_ } else if constexpr (std::is_same_v) { std::vector inputs; for (const auto& input : constraint.inputs) { - inputs.push_back(witness_or_constant_to_function_input(input.blackbox_input)); + inputs.push_back(witness_or_constant_to_function_input(input)); } auto outputs = std::make_shared>(); for (size_t i = 0; i < 32; ++i) { @@ -330,7 +330,7 @@ template std::vector constraint_to_acir_ } else if constexpr (std::is_same_v) { std::vector inputs; for (const auto& input : constraint.inputs) { - inputs.push_back(witness_or_constant_to_function_input(input.blackbox_input)); + inputs.push_back(witness_or_constant_to_function_input(input)); } auto outputs = std::make_shared>(); for (size_t i = 0; i < 32; ++i) { diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/group.hpp b/barretenberg/cpp/src/barretenberg/ecc/groups/group.hpp index b9b161807662..e60de391c108 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/group.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/group.hpp @@ -96,7 +96,7 @@ template class group { if (num_generators > 0) { BB_ASSERT(starting_index <= static_cast(UINT32_MAX), "derive_generators: starting_index exceeds uint32 range"); - BB_ASSERT(num_generators <= (static_cast(UINT32_MAX) - starting_index + 1), + BB_ASSERT(num_generators - 1 <= static_cast(UINT32_MAX) - starting_index, "derive_generators: starting_index + num_generators exceeds uint32 range"); } diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp index 7df91a9caa96..6e2512a83a06 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp @@ -376,7 +376,7 @@ template class Polynomial { */ void set_if_valid_index(size_t index, const Fr& value) { - BB_ASSERT(value.is_zero() || is_valid_set_index(index)); + BB_ASSERT_NO_WASM(value.is_zero() || is_valid_set_index(index)); if (is_valid_set_index(index)) { at(index) = value; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.cpp index 2383b206d46a..863ba138c1c4 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.cpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.hpp index 1dc6e20ae5a1..de1b64558f11 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.test.cpp index 57c24f2f51ce..294b783a295a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake2s.test.cpp @@ -1,3 +1,9 @@ +// === AUDIT STATUS === +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} +// external_1: { status: not started, auditors: [], commit: } +// external_2: { status: not started, auditors: [], commit: } +// ===================== + #include "barretenberg/crypto/blake2s/blake2s.hpp" #include "barretenberg/circuit_checker/circuit_checker.hpp" #include "barretenberg/flavor/ultra_flavor.hpp" diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake_util.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake_util.hpp index f2d8e8971b8d..4c66e9d5ac02 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake_util.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake2s/blake_util.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.cpp index a96a16f75531..66059954b0ad 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.cpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.hpp index 57c40bead15b..1dd4068c3510 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.test.cpp index 557fe3dbd8ec..7c164f813566 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/blake3s/blake3s.test.cpp @@ -1,3 +1,9 @@ +// === AUDIT STATUS === +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} +// external_1: { status: not started, auditors: [], commit: } +// external_2: { status: not started, auditors: [], commit: } +// ===================== + #include "barretenberg/crypto/blake3s/blake3s.hpp" #include "barretenberg/circuit_checker/circuit_checker.hpp" #include "barretenberg/common/assert.hpp" diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp index 24d03511c697..a2bdb514e421 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp @@ -24,6 +24,7 @@ #include "sha256.hpp" #include "barretenberg/stdlib/primitives/field/field.hpp" +#include "barretenberg/stdlib/primitives/field/field_utils.hpp" #include "barretenberg/stdlib/primitives/plookup/plookup.hpp" #include "barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.hpp" #include "barretenberg/stdlib_circuit_builders/plookup_tables/sha256.hpp" @@ -66,6 +67,30 @@ SHA256::sparse_witness_limbs SHA256::convert_witness(const fie return result; } +/** + * @brief Apply an implicit 32-bit range constraint by performing a lookup on the input. + * + * @details This is more efficient in the context of SHA-256 operations than explicit 32-bit range constraints since the + * lookup table is already in use. We use the SHA256_MAJ_INPUT MultiTable since it results in only 3 lookup gates per + * lookup. + * + * @note The result of the lookup is not used, but the accumulator outputs are marked as intentionally unused to + * avoid false positives in the boomerang value detection analysis. + * + * @param input The field element to constrain to 32 bits. + */ +template +void SHA256::apply_32_bit_range_constraint_via_lookup(const field_t& input) +{ + auto lookup_data = plookup_read::get_lookup_accumulators(MultiTableId::SHA256_MAJ_INPUT, input); + // Mark all accumulator outputs as intentionally unused (they exist only for the range constraint side-effect) + for (auto& col : lookup_data.columns) { + for (auto& elem : col) { + mark_witness_as_used(elem); + } + } +} + /** * @brief Extend the 16-word message block to 64 words per SHA-256 specification. * @@ -159,20 +184,8 @@ std::array, 64> SHA256::extend_witness(const std::arra field_pt w_out_raw_inv_pow_two = w_out_raw * inv_pow_two; field_pt w_out_inv_pow_two = w_out * inv_pow_two; field_pt divisor = w_out_raw_inv_pow_two - w_out_inv_pow_two; - // AUDITTODO: The 3-bit constraint is currently necessary due to unconstrained inputs. - // - // w_out_raw = xor_result + w[i-16] + w[i-7], where: - // - xor_result: 32-bit (from SHA256_WITNESS_OUTPUT lookup) - // - w[i-16]: At i=16, this is input[0] which is NEVER lookup-constrained - // - w[i-7]: At i=16..20, this is input[9..13] which are used BEFORE being converted - // - // If all three inputs were 32-bit constrained, max sum = 3*(2^32-1), so divisor <= 2 - // and a 2-bit constraint would suffice. However, with unconstrained inputs (~35 bits - // per the add_normalize overflow slack), divisor could exceed 7 and reject the proof. - // - // This constraint implicitly enforces input bounds - if we add explicit 32-bit input - // constraints (see AUDITTODO in sha256_block), this could be tightened to 2 bits. - divisor.create_range_constraint(3); + // Sum of four 32-bit values: divisor ≤ 3. + divisor.create_range_constraint(/*num_bits=*/2); } w_sparse[i] = sparse_witness_limbs(w_out); @@ -240,7 +253,7 @@ SHA256::sparse_value SHA256::map_into_maj_sparse_form(const fi * @param e Input/output: e.normal read, e.sparse populated as side effect * @param f Input: must have .sparse already populated * @param g Input: must have .sparse already populated - * @return Σ₁(e) + Ch(e,f,g) as a constrained 32-bit field element + * @return Σ₁(e) + Ch(e,f,g) (lookup-constrained to ≤ 2*(2^32-1)) */ template field_t SHA256::choose_with_sigma1(sparse_value& e, const sparse_value& f, const sparse_value& g) @@ -285,7 +298,7 @@ field_t SHA256::choose_with_sigma1(sparse_value& e, const spar * @param a Input/output: a.normal read, a.sparse populated as side effect * @param b Input: must have .sparse already populated * @param c Input: must have .sparse already populated - * @return Σ₀(a) + Maj(a,b,c) as a constrained 32-bit field element + * @return Σ₀(a) + Maj(a,b,c) (lookup-constrained to ≤ 2*(2^32-1)) */ template field_t SHA256::majority_with_sigma0(sparse_value& a, const sparse_value& b, const sparse_value& c) @@ -319,14 +332,17 @@ field_t SHA256::majority_with_sigma0(sparse_value& a, const sp /** * @brief Compute (a + b) mod 2^32 with circuit constraints. * - * Used throughout SHA-256 to add 32-bit values and reduce modulo 2^32. - * Constrains: result = a + b - overflow * 2^32, where overflow is range-checked. + * Constrains: result = a + b - overflow * 2^32, where overflow is range-checked to overflow_bits. + * + * @param overflow_bits Number of bits for overflow range constraint. Must accommodate max(a + b) / 2^32. * - * @warning result is not explicitly range-constrained here because it is typically used downstream in lookup tables - * (e.g. in choose_with_sigma1, majority_with_sigma0) which implicitly validates the 32-bit range. + * @warning Marked `unsafe` since result is not explicitly range-constrained herein because it is typically used + * downstream in lookup tables which implicitly impose the 32-bit range. */ template -field_t SHA256::add_normalize(const field_t& a, const field_t& b) +field_t SHA256::add_normalize_unsafe(const field_t& a, + const field_t& b, + size_t overflow_bits) { using field_pt = field_t; using witness_pt = witness_t; @@ -344,22 +360,7 @@ field_t SHA256::add_normalize(const field_t& a, const field_pt overflow = witness_pt(ctx, overflow_value); field_pt result = a.add_two(b, overflow * field_pt(ctx, -fr(1ULL << 32ULL))); - // AUDITTODO: The 3-bit constraint is necessary. Analysis of call sites: - // - // Compression loop (lines ~439-450): - // ch, maj outputs: max = 2(2^32-1) each (lookup output digits are 0-2, see sha256.hpp:79) - // temp1 = ch + h.normal + (w[i] + K[i]) (max = 2(2^32-1) + (2^32-1) + 2(2^32-1) = 5(2^32-1)) - // add_normalize(d.normal, temp1): max sum = (2^32-1) + 5(2^32-1) = 6(2^32-1), overflow <= 5 - // add_normalize(temp1, maj): max sum = 5(2^32-1) + 2(2^32-1) = 7(2^32-1), overflow <= 6 - // => Requires 3 bits (to represent overflow values 0-7) - // - // Final output (lines ~456-463): - // add_normalize(X.normal, h_init[i]): both 32-bit, max sum = 2(2^32-1), overflow <= 1 - // => Could use 1 bit, but we use 3 for uniformity - // - // The 3-bit constraint is correct and necessary for the compression loop. - // Consider adding argument overflow_bits to customize constraint size and make it more explicit. - overflow.create_range_constraint(3); + overflow.create_range_constraint(overflow_bits); return result; } @@ -369,6 +370,9 @@ field_t SHA256::add_normalize(const field_t& a, const * This is the only public entry point for the stdlib SHA-256 implementation. We implement only the compression function * (rather than a full hash) because this is all that is required in DSL. * + * @note It is assumed that all 24 inputs (8 hash state + 16 message words) are 32-bit constrained externally so that + * the input has a unique representation. + * * @param h_init The 8-word (256-bit) initial hash state. For the first block of a message, * this should be the standard SHA-256 IV. For subsequent blocks, this is the * output of the previous compression. @@ -381,33 +385,6 @@ std::array, 8> SHA256::sha256_block(const std::array; - // AUDITTODO: Input range constraints are not explicitly enforced here. Analysis shows: - // - // - h_init[1,2,5,6] are immediately lookup-constrained (32-bit) via map_into_*_sparse_form - // - h_init[0,4] are lookup-constrained in round 0 via choose/majority functions - // - h_init[3,7] are used in round 0 arithmetic BEFORE being lookup-constrained (they cycle - // through working variables and get constrained in later rounds) - // - input[0] is NEVER lookup-constrained (only used as w[i-16] and in round 0, both additions) - // - input[1..8] are lookup-constrained during extend_witness as w[i-15] (at i=16..23) - // - input[9..13] are used as w[i-7] at i=16..20 BEFORE being constrained (converted later - // as w[i-15] at i=24..28) - // - input[14..15] are lookup-constrained during extend_witness as w[i-2] (at i=16..17) - // - // The overflow constraints in extend_witness (3-bit divisor) and add_normalize (3-bit overflow) - // provide weak implicit bounds. If unconstrained inputs exceed ~35 bits, these constraints - // will reject the proof. This is safe (rejects invalid proofs) but not ideal. - // - // This is not practically exploitable (finding inputs that produce a specific hash still - // requires ~2^208 work), but deviates from the SHA-256 spec which assumes 32-bit words. - // - // Potential fix: Use lookups (cheaper than create_range_constraint, ~1 gate vs multiple): - // - For h_init[3], h_init[7]: convert immediately via map_into_*_sparse_form instead of - // wrapping in sparse_value(). The lookup constrains the input as a side effect. - // - For input[0]: add a lookup in extend_witness via convert_witness() or SHA256_WITNESS_INPUT. - // - For input[9..13]: reorder extend_witness to convert these before use, or add explicit lookups. - // - // After fixing, the extend_witness divisor constraint could be tightened to 2 bits. - /** * Initialize round variables with previous block output. * Note: We delay converting `a` and `e` into their respective sparse forms because it's done as part of the @@ -437,36 +414,44 @@ std::array, 8> SHA256::sha256_block(const std::array output; - output[0] = add_normalize(a.normal, h_init[0]); - output[1] = add_normalize(b.normal, h_init[1]); - output[2] = add_normalize(c.normal, h_init[2]); - output[3] = add_normalize(d.normal, h_init[3]); - output[4] = add_normalize(e.normal, h_init[4]); - output[5] = add_normalize(f.normal, h_init[5]); - output[6] = add_normalize(g.normal, h_init[6]); - output[7] = add_normalize(h.normal, h_init[7]); - - // The final add_normalize outputs are not consumed by lookup tables, so they must be - // explicitly range-constrained. (Within the compression loop, lookup tables provide - // implicit 32-bit constraints on add_normalize outputs.) - for (size_t i = 0; i < 8; i++) { - output[i].create_range_constraint(32); + output[0] = add_normalize_unsafe(a.normal, h_init[0], /*overflow_bits=*/1); + output[1] = add_normalize_unsafe(b.normal, h_init[1], /*overflow_bits=*/1); + output[2] = add_normalize_unsafe(c.normal, h_init[2], /*overflow_bits=*/1); + output[3] = add_normalize_unsafe(d.normal, h_init[3], /*overflow_bits=*/1); + output[4] = add_normalize_unsafe(e.normal, h_init[4], /*overflow_bits=*/1); + output[5] = add_normalize_unsafe(f.normal, h_init[5], /*overflow_bits=*/1); + output[6] = add_normalize_unsafe(g.normal, h_init[6], /*overflow_bits=*/1); + output[7] = add_normalize_unsafe(h.normal, h_init[7], /*overflow_bits=*/1); + + // The final add_normalize outputs are not consumed by lookup tables, so they must be explicitly range-constrained. + // (Within the compression loop, lookup tables provide implicit 32-bit constraints on add_normalize outputs.) + for (const auto& val : output) { + val.create_range_constraint(32); } return output; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp index f35fd68e83a9..7f02d5d3fa0d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp @@ -138,13 +138,15 @@ template class SHA256 { static sparse_witness_limbs convert_witness(const field_ct& input); + static void apply_32_bit_range_constraint_via_lookup(const field_ct& input); + static field_ct choose_with_sigma1(sparse_value& e, const sparse_value& f, const sparse_value& g); static field_ct majority_with_sigma0(sparse_value& a, const sparse_value& b, const sparse_value& c); static sparse_value map_into_choose_sparse_form(const field_ct& input); static sparse_value map_into_maj_sparse_form(const field_ct& input); - static field_ct add_normalize(const field_ct& a, const field_ct& b); + static field_ct add_normalize_unsafe(const field_ct& a, const field_ct& b, size_t overflow_bits); public: static std::array sha256_block(const std::array& h_init, diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp index 613ca213510b..56f05a12802c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.test.cpp @@ -87,7 +87,7 @@ TYPED_TEST(Sha256Test, BlockNistVectorOne) EXPECT_EQ(circuit_val, EXPECTED[i]) << "Circuit mismatch at index " << i; } - check_circuit_and_gate_count(builder, 6679); + check_circuit_and_gate_count(builder, 6695); EXPECT_EQ(builder.get_tables_size(), 35992); } @@ -165,7 +165,7 @@ TYPED_TEST(Sha256Test, BlockNistVectorTwo) EXPECT_EQ(circuit_val, EXPECTED[i]) << "Circuit mismatch at index " << i; } - check_circuit_and_gate_count(builder, 10611); + check_circuit_and_gate_count(builder, 10633); EXPECT_EQ(builder.get_tables_size(), 35992); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp index dcf9d2d73f94..61081cbce2c7 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Luke, Raju], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Luke, Raju], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/blake2s.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/blake2s.hpp index e5a5b6ee233e..854657f221e9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/blake2s.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/blake2s.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Nishat], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Nishat], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.cpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.cpp index 7d093d40b7e0..774ec2f634dd 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.cpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Luke, Raju], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Luke, Raju], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // ===================== diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.hpp index 8cfe2202026d..171b8fb565f7 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.hpp @@ -1,5 +1,5 @@ // === AUDIT STATUS === -// internal: { status: Complete, auditors: [Luke, Raju], commit: 4a956ceb179c2fe855e4f1fd78f2594e7fc3f5ea} +// internal: { status: Complete, auditors: [Luke, Raju], commit: 8fb8b041d4c9179f62da56a9c7bbf22c40db46cc} // external_1: { status: not started, auditors: [], commit: } // external_2: { status: not started, auditors: [], commit: } // =====================