From 891de6347e73a00c25c25a2f0d68ffde2b51ef68 Mon Sep 17 00:00:00 2001 From: guipublic Date: Mon, 18 Dec 2023 12:21:58 +0000 Subject: [PATCH 1/9] keccak in noir using a permutation opcode --- barretenberg/cpp/src/barretenberg/bb/main.cpp | 2 +- .../dsl/acir_format/acir_format.cpp | 3 + .../dsl/acir_format/acir_format.hpp | 2 + .../dsl/acir_format/acir_format.test.cpp | 49 +++++++ .../acir_format/acir_to_constraint_buf.hpp | 5 + .../dsl/acir_format/keccak_constraint.cpp | 20 +++ .../dsl/acir_format/keccak_constraint.hpp | 10 ++ .../dsl/acir_format/serde/acir.hpp | 62 +++++++++ .../stdlib/hash/keccak/keccak.cpp | 127 ++++++++++++++++++ .../stdlib/hash/keccak/keccak.hpp | 11 ++ .../stdlib/hash/keccak/keccak.test.cpp | 43 ++++++ .../acir/src/circuit/black_box_functions.rs | 4 + noir/acvm-repo/acir/src/circuit/mod.rs | 59 ++++++++ .../opcodes/black_box_function_call.rs | 7 + .../acvm/src/compiler/transformers/mod.rs | 1 + noir/acvm-repo/acvm/src/pwg/blackbox/hash.rs | 79 +++++++++++ noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs | 20 ++- .../ssa/acir_gen/acir_ir/generated_acir.rs | 6 + .../src/ssa/ir/instruction/call.rs | 1 + noir/noir_stdlib/src/hash/keccak.nr | 89 ++++++++++++ .../execution_success/keccak256/src/main.nr | 3 + noir/tooling/backend_interface/src/lib.rs | 1 + 22 files changed, 601 insertions(+), 3 deletions(-) create mode 100644 noir/noir_stdlib/src/hash/keccak.nr diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 0cc46d9dd24e..e91809288605 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -337,7 +337,7 @@ void acvm_info(const std::string& output_path) "width" : 3 }, "opcodes_supported" : ["arithmetic", "directive", "brillig", "memory_init", "memory_op"], - "black_box_functions_supported" : ["and", "xor", "range", "sha256", "blake2s", "keccak256", "schnorr_verify", "pedersen", "pedersen_hash", "hash_to_field_128_security", "ecdsa_secp256k1", "ecdsa_secp256r1", "fixed_base_scalar_mul", "recursive_aggregation"] + "black_box_functions_supported" : ["and", "xor", "range", "sha256", "blake2s", "keccak256", "keccak_f1600", "schnorr_verify", "pedersen", "pedersen_hash", "hash_to_field_128_security", "ecdsa_secp256k1", "ecdsa_secp256r1", "fixed_base_scalar_mul", "recursive_aggregation"] })"; size_t length = strlen(jsonData); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 6d94ba7b766e..d0d36119f071 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -78,6 +78,9 @@ void build_constraints(Builder& builder, acir_format const& constraint_system, b for (const auto& constraint : constraint_system.keccak_var_constraints) { create_keccak_var_constraints(builder, constraint); } + for (const auto& constraint : constraint_system.keccak_permutations) { + create_keccak_permutations(builder, constraint); + } // Add pedersen constraints for (const auto& constraint : constraint_system.pedersen_constraints) { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index 15f2ecbcdd83..488496e6d116 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -33,6 +33,7 @@ struct acir_format { std::vector blake2s_constraints; std::vector keccak_constraints; std::vector keccak_var_constraints; + std::vector keccak_permutations; std::vector pedersen_constraints; std::vector pedersen_hash_constraints; std::vector hash_to_field_constraints; @@ -58,6 +59,7 @@ struct acir_format { blake2s_constraints, keccak_constraints, keccak_var_constraints, + keccak_permutations, pedersen_constraints, pedersen_hash_constraints, hash_to_field_constraints, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index b0711582deb7..ea36f0df58d1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -39,6 +39,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .hash_to_field_constraints = {}, @@ -146,6 +147,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .hash_to_field_constraints = {}, @@ -211,6 +213,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .hash_to_field_constraints = {}, @@ -299,6 +302,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .hash_to_field_constraints = {}, @@ -406,6 +410,7 @@ TEST_F(AcirFormatTests, TestVarKeccak) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = { keccak }, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .hash_to_field_constraints = {}, @@ -424,4 +429,48 @@ TEST_F(AcirFormatTests, TestVarKeccak) EXPECT_EQ(verifier.verify_proof(proof), true); } +TEST_F(AcirFormatTests, TestKeccakPermutation) +{ + Keccakf1600 + keccak_permutation{ + .state = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }, + .result = { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }, + }; + + acir_format constraint_system{ .varnum = 51, + .public_inputs = {}, + .logic_constraints = {}, + .range_constraints = {}, + .sha256_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_k1_constraints = {}, + .ecdsa_r1_constraints = {}, + .blake2s_constraints = {}, + .keccak_constraints = {}, + .keccak_var_constraints = {}, + .keccak_permutations = { keccak_permutation }, + .pedersen_constraints = {}, + .pedersen_hash_constraints = {}, + .hash_to_field_constraints = {}, + .fixed_base_scalar_mul_constraints = {}, + .recursion_constraints = {}, + .constraints = {}, + .block_constraints = {} }; + + WitnessVector witness{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }; + + auto builder = create_circuit_with_witness(constraint_system, witness); + + auto composer = Composer(); + auto prover = composer.create_ultra_with_keccak_prover(builder); + auto proof = prover.construct_proof(); + + auto verifier = composer.create_ultra_with_keccak_verifier(builder); + + EXPECT_EQ(verifier.verify_proof(proof), true); +} + } // namespace acir_format::tests diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp index 0c2f63a18170..286a495f2498 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp @@ -192,6 +192,11 @@ void handle_blackbox_func_call(Circuit::Opcode::BlackBoxFuncCall const& arg, aci .result = map(arg.outputs, [](auto& e) { return e.value; }), .var_message_size = arg.var_message_size.witness.value, }); + } else if constexpr (std::is_same_v) { + af.keccak_permutations.push_back(Keccakf1600{ + .state = map(arg.inputs, [](auto& e) { return e.witness.value; }), + .result = map(arg.outputs, [](auto& e) { return e.value; }), + }); } else if constexpr (std::is_same_v) { auto c = RecursionConstraint{ .key = map(arg.verification_key, [](auto& e) { return e.witness.value; }), diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.cpp index 748c3e927e1d..192d53cc1cc7 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.cpp @@ -68,4 +68,24 @@ void create_keccak_var_constraints(Builder& builder, const KeccakVarConstraint& } } +void create_keccak_permutations(Builder& builder, const Keccakf1600& constraint) +{ + // Create the array containing the permuted state + std::array::NUM_KECCAK_LANES> state; + + // Get the witness assignment for each witness index + // Write the witness assignment to the byte_array + for (size_t i = 0; i < constraint.state.size(); ++i) { + info(constraint.state[i]); + state[i] = field_ct::from_witness_index(&builder, constraint.state[i]); + } + + std::array output_state = + proof_system::plonk::stdlib::keccak::permutation_opcode(state, &builder); + + for (size_t i = 0; i < output_state.size(); ++i) { + builder.assert_equal(output_state[i].normalize().witness_index, constraint.result[i]); + } +} + } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.hpp index fa2af398678c..3c6edc2837f5 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/keccak_constraint.hpp @@ -15,6 +15,15 @@ struct HashInput { friend bool operator==(HashInput const& lhs, HashInput const& rhs) = default; }; +struct Keccakf1600 { + std::vector state; + std::vector result; + + // For serialization, update with any new fields + MSGPACK_FIELDS(state, result); + friend bool operator==(Keccakf1600 const& lhs, Keccakf1600 const& rhs) = default; +}; + struct KeccakConstraint { std::vector inputs; std::vector result; @@ -36,5 +45,6 @@ struct KeccakVarConstraint { void create_keccak_constraints(Builder& builder, const KeccakConstraint& constraint); void create_keccak_var_constraints(Builder& builder, const KeccakVarConstraint& constraint); +void create_keccak_permutations(Builder& builder, const Keccakf1600& constraint); } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp index dc1e8e148219..d8f698f8fbbf 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -164,6 +164,15 @@ struct BlackBoxFuncCall { static Keccak256VariableLength bincodeDeserialize(std::vector); }; + struct Keccakf1600 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Keccakf1600&, const Keccakf1600&); + std::vector bincodeSerialize() const; + static Keccakf1600 bincodeDeserialize(std::vector); + }; + struct RecursiveAggregation { std::vector verification_key; std::vector proof; @@ -191,6 +200,7 @@ struct BlackBoxFuncCall { FixedBaseScalarMul, Keccak256, Keccak256VariableLength, + Keccakf1600, RecursiveAggregation> value; @@ -2594,6 +2604,58 @@ Circuit::BlackBoxFuncCall::Keccak256VariableLength serde::Deserializable< namespace Circuit { +inline bool operator==(const BlackBoxFuncCall::Keccakf1600& lhs, const BlackBoxFuncCall::Keccakf1600& rhs) +{ + if (!(lhs.inputs == rhs.inputs)) { + return false; + } + if (!(lhs.outputs == rhs.outputs)) { + return false; + } + return true; +} + +inline std::vector BlackBoxFuncCall::Keccakf1600::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxFuncCall::Keccakf1600 BlackBoxFuncCall::Keccakf1600::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::Keccakf1600& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::Keccakf1600 serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxFuncCall::Keccakf1600 obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + inline bool operator==(const BlackBoxFuncCall::RecursiveAggregation& lhs, const BlackBoxFuncCall::RecursiveAggregation& rhs) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp index 2ff044e13fe0..fe9fb2794653 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp @@ -1,6 +1,7 @@ #include "keccak.hpp" #include "barretenberg/common/constexpr_utils.hpp" #include "barretenberg/numeric/bitop/sparse_form.hpp" +#include "barretenberg/stdlib/primitives/logic/logic.hpp" #include "barretenberg/stdlib/primitives/uint/uint.hpp" namespace proof_system::plonk { namespace stdlib { @@ -721,6 +722,92 @@ std::vector> keccak::format_input_lanes(byte_array_ct& return lanes; } +// Returns the keccak f1600 permutation of the input state +// We first convert the state into 'extended' representation, along with the 'twisted' state +// and then we call keccakf1600() with this keccak 'internal state' +// Finally, we convert back the state from the extented representation +template +std::array, keccak::NUM_KECCAK_LANES> keccak::permutation_opcode( + std::array, NUM_KECCAK_LANES> state, Builder* ctx) +{ + std::vector> converted_buffer(NUM_KECCAK_LANES); + std::vector> msb_buffer(NUM_KECCAK_LANES); + // populate keccak_state, convert our 64-bit lanes into an extended base-11 representation + keccak_state internal; + internal.context = ctx; + for (size_t i = 0; i < state.size(); ++i) { + const auto accumulators = plookup_read::get_lookup_accumulators(KECCAK_FORMAT_INPUT, state[i]); + internal.state[i] = accumulators[ColumnIdx::C2][0]; + internal.state_msb[i] = accumulators[ColumnIdx::C3][accumulators[ColumnIdx::C3].size() - 1]; + } + compute_twisted_state(internal); + keccakf1600(internal); + // we convert back to the normal lanes + return extended_2_normal(internal); +} + +// This function is similar to sponge_absorb() +// but it uses permutation_opcode() instead of calling directly keccakf1600(). +// As a result, this function is less efficient and should only be used to test permutation_opcode() +template +void keccak::sponge_absorb_with_permutation_opcode(keccak_state& internal, + std::vector>& input_buffer, + const size_t input_size) +{ + // populate keccak_state + const size_t num_blocks = input_size / (BLOCK_SIZE / 8); + for (size_t i = 0; i < num_blocks; ++i) { + if (i == 0) { + for (size_t j = 0; j < LIMBS_PER_BLOCK; ++j) { + internal.state[j] = input_buffer[j]; + } + for (size_t j = LIMBS_PER_BLOCK; j < NUM_KECCAK_LANES; ++j) { + internal.state[j] = witness_ct::create_constant_witness(internal.context, 0); + } + } else { + for (size_t j = 0; j < LIMBS_PER_BLOCK; ++j) { + internal.state[j] = stdlib::logic::create_logic_constraint( + internal.state[j], input_buffer[i * LIMBS_PER_BLOCK + j], 64, true); + } + } + internal.state = permutation_opcode(internal.state, internal.context); + } +} + +// This function computes the keccak hash, like the hash() function +// but it uses permutation_opcode() instead of calling directly keccakf1600(). +// As a result, this function is less efficient and should only be used to test permutation_opcode() +template +stdlib::byte_array keccak::hash_using_permutation_opcode(byte_array_ct& input, + const uint32_ct& num_bytes) +{ + auto ctx = input.get_context(); + + ASSERT(uint256_t(num_bytes.get_value()) == input.size()); + + if (ctx == nullptr) { + // if buffer is constant compute hash and return w/o creating constraints + byte_array_ct output(nullptr, 32); + const std::vector result = hash_native(input.get_value()); + for (size_t i = 0; i < 32; ++i) { + output.set_byte(i, result[i]); + } + return output; + } + + // convert the input byte array into 64-bit keccak lanes (+ apply padding) + auto formatted_slices = format_input_lanes(input, num_bytes); + + keccak_state internal; + internal.context = ctx; + uint32_ct num_blocks_with_data = (num_bytes + BLOCK_SIZE) / BLOCK_SIZE; + sponge_absorb_with_permutation_opcode(internal, formatted_slices, formatted_slices.size()); + + auto result = sponge_squeeze_for_permutation_opcode(internal.state, ctx); + + return result; +} + template stdlib::byte_array keccak::hash(byte_array_ct& input, const uint32_ct& num_bytes) { @@ -762,6 +849,46 @@ stdlib::byte_array keccak::hash(byte_array_ct& input, const ui return result; } +// Convert the 'extended' representation of the internal Keccak state into the usual array of 64 bits lanes +template +std::array, keccak::NUM_KECCAK_LANES> keccak::extended_2_normal( + keccak_state& internal) +{ + std::array, NUM_KECCAK_LANES> conversion; + + // Each hash limb represents a little-endian integer. Need to reverse bytes before we write into the output array + for (size_t i = 0; i < internal.state.size(); ++i) { + field_ct output_limb = plookup_read::read_from_1_to_2_table(KECCAK_FORMAT_OUTPUT, internal.state[i]); + conversion[i] = output_limb; + } + + return conversion; +} + +// This function is the same as sponge_squeeze, except that it does not convert +// from extented representation and assumes the input has already being converted +template +stdlib::byte_array keccak::sponge_squeeze_for_permutation_opcode( + std::array, NUM_KECCAK_LANES> lanes, Builder* context) +{ + byte_array_ct result(context); + + // Each hash limb represents a little-endian integer. Need to reverse bytes before we write into the output array + for (size_t i = 0; i < 4; ++i) { + byte_array_ct limb_bytes(lanes[i], 8); + byte_array_ct little_endian_limb_bytes(context, 8); + little_endian_limb_bytes.set_byte(0, limb_bytes[7]); + little_endian_limb_bytes.set_byte(1, limb_bytes[6]); + little_endian_limb_bytes.set_byte(2, limb_bytes[5]); + little_endian_limb_bytes.set_byte(3, limb_bytes[4]); + little_endian_limb_bytes.set_byte(4, limb_bytes[3]); + little_endian_limb_bytes.set_byte(5, limb_bytes[2]); + little_endian_limb_bytes.set_byte(6, limb_bytes[1]); + little_endian_limb_bytes.set_byte(7, limb_bytes[0]); + result.write(little_endian_limb_bytes); + } + return result; +} INSTANTIATE_STDLIB_ULTRA_TYPE(keccak) } // namespace stdlib } // namespace proof_system::plonk diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp index 1790040731ed..5719938ebad1 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp @@ -189,6 +189,17 @@ template class keccak { memcpy((void*)&output[0], (void*)&hash_result.word64s[0], 32); return output; } + + // exposing keccak f1600 permutation + static byte_array_ct hash_using_permutation_opcode(byte_array_ct& input, const uint32_ct& num_bytes); + static std::array permutation_opcode(std::array state, + Builder* context); + static void sponge_absorb_with_permutation_opcode(keccak_state& internal, + std::vector& input_buffer, + const size_t input_size); + static std::array extended_2_normal(keccak_state& internal); + static byte_array_ct sponge_squeeze_for_permutation_opcode(std::array lanes, + Builder* context); }; EXTERN_STDLIB_ULTRA_TYPE(keccak) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.test.cpp index dd5ab49884e1..852488f5a6cd 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.test.cpp @@ -269,3 +269,46 @@ TEST(stdlib_keccak, test_variable_length_nonzero_input_greater_than_byte_array_s bool proof_result = builder.check_circuit(); EXPECT_EQ(proof_result, true); } + +TEST(stdlib_keccak, test_permutation_opcode_single_block) +{ + Builder builder = Builder(); + std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; + std::vector input_v(input.begin(), input.end()); + + byte_array input_arr(&builder, input_v); + byte_array output = + stdlib::keccak::hash_using_permutation_opcode(input_arr, static_cast(input.size())); + + std::vector expected = stdlib::keccak::hash_native(input_v); + + EXPECT_EQ(output.get_value(), expected); + + builder.print_num_gates(); + + bool proof_result = builder.check_circuit(); + EXPECT_EQ(proof_result, true); +} + +TEST(stdlib_keccak, test_permutation_opcode_double_block) +{ + Builder builder = Builder(); + std::string input = ""; + for (size_t i = 0; i < 200; ++i) { + input += "a"; + } + std::vector input_v(input.begin(), input.end()); + + byte_array input_arr(&builder, input_v); + byte_array output = + stdlib::keccak::hash_using_permutation_opcode(input_arr, static_cast(input.size())); + + std::vector expected = stdlib::keccak::hash_native(input_v); + + EXPECT_EQ(output.get_value(), expected); + + builder.print_num_gates(); + + bool proof_result = builder.check_circuit(); + EXPECT_EQ(proof_result, true); +} diff --git a/noir/acvm-repo/acir/src/circuit/black_box_functions.rs b/noir/acvm-repo/acir/src/circuit/black_box_functions.rs index 9129f44008c2..bc8ee962d4a9 100644 --- a/noir/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/noir/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -47,6 +47,8 @@ pub enum BlackBoxFunc { FixedBaseScalarMul, /// Calculates the Keccak256 hash of the inputs. Keccak256, + /// Keccak Permutation function of 1600 width + Keccakf1600, /// Compute a recursive aggregation object when verifying a proof inside another circuit. /// This outputted aggregation object will then be either checked in a top-level verifier or aggregated upon again. RecursiveAggregation, @@ -73,6 +75,7 @@ impl BlackBoxFunc { BlackBoxFunc::XOR => "xor", BlackBoxFunc::RANGE => "range", BlackBoxFunc::Keccak256 => "keccak256", + BlackBoxFunc::Keccakf1600 => "keccak_f1600", BlackBoxFunc::RecursiveAggregation => "recursive_aggregation", BlackBoxFunc::EcdsaSecp256r1 => "ecdsa_secp256r1", } @@ -92,6 +95,7 @@ impl BlackBoxFunc { "xor" => Some(BlackBoxFunc::XOR), "range" => Some(BlackBoxFunc::RANGE), "keccak256" => Some(BlackBoxFunc::Keccak256), + "keccakf1600" => Some(BlackBoxFunc::Keccakf1600), "recursive_aggregation" => Some(BlackBoxFunc::RecursiveAggregation), _ => None, } diff --git a/noir/acvm-repo/acir/src/circuit/mod.rs b/noir/acvm-repo/acir/src/circuit/mod.rs index 99ab389e31e2..40df1c177543 100644 --- a/noir/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/acvm-repo/acir/src/circuit/mod.rs @@ -250,6 +250,64 @@ mod tests { input: FunctionInput { witness: Witness(1), num_bits: 8 }, }) } + fn perm_opcode() -> Opcode { + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::Keccakf1600 { + inputs: vec![ + FunctionInput { witness: Witness(1), num_bits: 64 }, + FunctionInput { witness: Witness(2), num_bits: 64 }, + FunctionInput { witness: Witness(3), num_bits: 64 }, + FunctionInput { witness: Witness(4), num_bits: 64 }, + FunctionInput { witness: Witness(5), num_bits: 64 }, + FunctionInput { witness: Witness(6), num_bits: 64 }, + FunctionInput { witness: Witness(7), num_bits: 64 }, + FunctionInput { witness: Witness(8), num_bits: 64 }, + FunctionInput { witness: Witness(9), num_bits: 64 }, + FunctionInput { witness: Witness(10), num_bits: 64 }, + FunctionInput { witness: Witness(11), num_bits: 64 }, + FunctionInput { witness: Witness(12), num_bits: 64 }, + FunctionInput { witness: Witness(13), num_bits: 64 }, + FunctionInput { witness: Witness(14), num_bits: 64 }, + FunctionInput { witness: Witness(15), num_bits: 64 }, + FunctionInput { witness: Witness(16), num_bits: 64 }, + FunctionInput { witness: Witness(17), num_bits: 64 }, + FunctionInput { witness: Witness(18), num_bits: 64 }, + FunctionInput { witness: Witness(19), num_bits: 64 }, + FunctionInput { witness: Witness(20), num_bits: 64 }, + FunctionInput { witness: Witness(21), num_bits: 64 }, + FunctionInput { witness: Witness(22), num_bits: 64 }, + FunctionInput { witness: Witness(23), num_bits: 64 }, + FunctionInput { witness: Witness(24), num_bits: 64 }, + FunctionInput { witness: Witness(25), num_bits: 64 }, + ], + outputs: vec![ + Witness(26), + Witness(27), + Witness(28), + Witness(29), + Witness(30), + Witness(31), + Witness(32), + Witness(33), + Witness(34), + Witness(35), + Witness(36), + Witness(37), + Witness(38), + Witness(39), + Witness(40), + Witness(41), + Witness(42), + Witness(43), + Witness(44), + Witness(45), + Witness(46), + Witness(47), + Witness(48), + Witness(49), + Witness(50), + ], + }) + } #[test] fn serialization_roundtrip() { @@ -284,6 +342,7 @@ mod tests { }), range_opcode(), and_opcode(), + perm_opcode(), ], private_parameters: BTreeSet::new(), public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])), diff --git a/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 708219138362..29a7d384b8fd 100644 --- a/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -94,6 +94,10 @@ pub enum BlackBoxFuncCall { var_message_size: FunctionInput, outputs: Vec, }, + Keccakf1600 { + inputs: Vec, + outputs: Vec, + }, RecursiveAggregation { verification_key: Vec, proof: Vec, @@ -136,6 +140,7 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul, BlackBoxFuncCall::Keccak256 { .. } => BlackBoxFunc::Keccak256, BlackBoxFuncCall::Keccak256VariableLength { .. } => BlackBoxFunc::Keccak256, + BlackBoxFuncCall::Keccakf1600 { .. } => BlackBoxFunc::Keccakf1600, BlackBoxFuncCall::RecursiveAggregation { .. } => BlackBoxFunc::RecursiveAggregation, } } @@ -149,6 +154,7 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::SHA256 { inputs, .. } | BlackBoxFuncCall::Blake2s { inputs, .. } | BlackBoxFuncCall::Keccak256 { inputs, .. } + | BlackBoxFuncCall::Keccakf1600 { inputs, .. } | BlackBoxFuncCall::PedersenCommitment { inputs, .. } | BlackBoxFuncCall::PedersenHash { inputs, .. } | BlackBoxFuncCall::HashToField128Security { inputs, .. } => inputs.to_vec(), @@ -239,6 +245,7 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::SHA256 { outputs, .. } | BlackBoxFuncCall::Blake2s { outputs, .. } | BlackBoxFuncCall::Keccak256 { outputs, .. } + | BlackBoxFuncCall::Keccakf1600 { outputs, .. } | BlackBoxFuncCall::RecursiveAggregation { output_aggregation_object: outputs, .. } => outputs.to_vec(), diff --git a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs index d827b7596662..0cc68d580dd2 100644 --- a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -117,6 +117,7 @@ pub(super) fn transform_internal( outputs, .. } + | acir::circuit::opcodes::BlackBoxFuncCall::Keccakf1600 { outputs, .. } | acir::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation { output_aggregation_object: outputs, .. diff --git a/noir/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/noir/acvm-repo/acvm/src/pwg/blackbox/hash.rs index 80665a743c44..15f368ea7c5f 100644 --- a/noir/acvm-repo/acvm/src/pwg/blackbox/hash.rs +++ b/noir/acvm-repo/acvm/src/pwg/blackbox/hash.rs @@ -101,3 +101,82 @@ fn write_digest_to_outputs( Ok(()) } + +const ROUNDS: usize = 24; + +const RC: [u64; ROUNDS] = [ + 1u64, + 0x8082u64, + 0x800000000000808au64, + 0x8000000080008000u64, + 0x808bu64, + 0x80000001u64, + 0x8000000080008081u64, + 0x8000000000008009u64, + 0x8au64, + 0x88u64, + 0x80008009u64, + 0x8000000au64, + 0x8000808bu64, + 0x800000000000008bu64, + 0x8000000000008089u64, + 0x8000000000008003u64, + 0x8000000000008002u64, + 0x8000000000000080u64, + 0x800au64, + 0x800000008000000au64, + 0x8000000080008081u64, + 0x8000000000008080u64, + 0x80000001u64, + 0x8000000080008008u64, +]; + +const RHO: [u32; 24] = + [1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44]; + +const PI: [usize; 24] = + [10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1]; + +const KECCAK_LANES: usize = 25; + +pub(crate) fn keccakf1600(state: &mut [u64; KECCAK_LANES]) { + for rc in RC { + let mut array: [u64; 5] = [0; 5]; + + // Theta + for x in 0..5 { + for y_count in 0..5 { + let y = y_count * 5; + array[x] ^= state[x + y]; + } + } + + for x in 0..5 { + for y_count in 0..5 { + let y = y_count * 5; + state[y + x] ^= array[(x + 4) % 5] ^ array[(x + 1) % 5].rotate_left(1); + } + } + + // Rho and pi + let mut last = state[1]; + for x in 0..24 { + array[0] = state[PI[x]]; + state[PI[x]] = last.rotate_left(RHO[x]); + last = array[0]; + } + + // Chi + for y_step in 0..5 { + let y = y_step * 5; + array[..5].copy_from_slice(&state[y..(5 + y)]); + + for x in 0..5 { + state[y + x] = array[x] ^ ((!array[(x + 1) % 5]) & (array[(x + 2) % 5])); + } + } + + // Iota + state[0] ^= rc; + } +} diff --git a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 7e8ab8b948cd..6e123642ab04 100644 --- a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -5,10 +5,10 @@ use acir::{ }; use acvm_blackbox_solver::{blake2s, keccak256, sha256}; -use self::pedersen::pedersen_hash; +use self::{hash::keccakf1600, pedersen::pedersen_hash}; use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError}; -use crate::BlackBoxFunctionSolver; +use crate::{pwg::witness_to_value, BlackBoxFunctionSolver}; mod fixed_base_scalar_mul; mod hash; @@ -101,6 +101,22 @@ pub(crate) fn solve( bb_func.get_black_box_func(), ) } + BlackBoxFuncCall::Keccakf1600 { inputs, outputs } => { + let mut state = [0; 25]; + for (i, input) in inputs.iter().enumerate() { + let witness = input.witness; + let num_bits = input.num_bits as usize; + assert_eq!(num_bits, 64); + let witness_assignment = witness_to_value(initial_witness, witness)?; + let lane = witness_assignment.try_to_u64(); + state[i] = lane.unwrap(); + } + keccakf1600(&mut state); + for (output_witness, value) in outputs.iter().zip(state.into_iter()) { + insert_value(output_witness, FieldElement::from(value as u128), initial_witness)?; + } + Ok(()) + } BlackBoxFuncCall::HashToField128Security { inputs, output } => { solve_hash_to_field(initial_witness, inputs, output) } diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index f29d3c9ec050..781efe057bbe 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -226,6 +226,9 @@ impl GeneratedAcir { outputs, } } + BlackBoxFunc::Keccakf1600 => { + BlackBoxFuncCall::Keccakf1600 { inputs: inputs[0].clone(), outputs } + } BlackBoxFunc::RecursiveAggregation => { let has_previous_aggregation = self.opcodes.iter().any(|op| { matches!( @@ -577,6 +580,8 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { | BlackBoxFunc::PedersenHash | BlackBoxFunc::HashToField128Security => None, + BlackBoxFunc::Keccakf1600 => Some(25), + // Can only apply a range constraint to one // witness at a time. BlackBoxFunc::RANGE => Some(1), @@ -603,6 +608,7 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { BlackBoxFunc::AND | BlackBoxFunc::XOR => Some(1), // 32 byte hash algorithms BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s => Some(32), + BlackBoxFunc::Keccakf1600 => Some(25), // Hash to field returns a field element BlackBoxFunc::HashToField128Security => Some(1), // Pedersen commitment returns a point diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index b07e2df7bd32..98ecbc3d2e62 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -393,6 +393,7 @@ fn simplify_black_box_func( _ => SimplifyResult::None, } } + BlackBoxFunc::Keccakf1600 => SimplifyResult::None, //TODO BlackBoxFunc::HashToField128Security => match dfg.get_array_constant(arguments[0]) { Some((input, _)) if array_is_constant(dfg, &input) => { let input_bytes: Vec = to_u8_vec(dfg, input); diff --git a/noir/noir_stdlib/src/hash/keccak.nr b/noir/noir_stdlib/src/hash/keccak.nr new file mode 100644 index 000000000000..1a8801ac9fd1 --- /dev/null +++ b/noir/noir_stdlib/src/hash/keccak.nr @@ -0,0 +1,89 @@ +global LIMBS_PER_BLOCK = 17; //BLOCK_SIZE / 8; +global NUM_KECCAK_LANES = 25; + +fn hash(input: [u8; N], input_length: u64) -> [u8; 32] +{ + // no var keccak for now + assert(N == input_length); + + //1. format_input_lanes + let max_blocks = (N as u64 + BLOCK_SIZE) / BLOCK_SIZE; + let max_blocks_length = (BLOCK_SIZE * (max_blocks)); //maximum number of bytes to hash + assert(max_blocks_length < 1000); + let mut block_bytes = [0; 1000]; //should be max_blocks_length + for i in 0..N { + block_bytes[i] = input[i]; + } + let byte_difference = max_blocks_length - N; + block_bytes[N] = 1; + block_bytes[max_blocks_length - 1] = 0x80; + + + // keccak lanes interpret memory as little-endian integers, + // means we need to swap our byte ordering... + let num_limbs = max_blocks * LIMBS_PER_BLOCK; //max_blocks_length / WORD_SIZE; + for i in 0..num_limbs{ + let mut temp = [0;8]; + + for j in 0..8 { + temp[j] = block_bytes[8*i+j]; + } + for j in 0..8 { + block_bytes[8*i+j] = temp[7-j]; + } + } + let byte_size = max_blocks_length; + assert(num_limbs < 1000); + let mut sliced_buffer = [0;1000]; + // populate a vector of 64-bit limbs from our byte array + for i in 0..num_limbs { + let mut sliced = 0; + if ((i as u64) * WORD_SIZE + WORD_SIZE > byte_size) { + let slice_size = byte_size - (i * WORD_SIZE); + let byte_shift = (WORD_SIZE - slice_size) * 8; + let mut v = 1; + for k in 0..slice_size { + sliced += v*(block_bytes[i * WORD_SIZE+7-k] as Field); + v = v*256; + } + let w = 1 << byte_shift; + sliced *= w as Field; + }else { + let mut v = 1; + for k in 0..WORD_SIZE { + sliced += v*(block_bytes[i * WORD_SIZE+7-k] as Field); + v = v*256; + } + } + sliced_buffer[i] =sliced as u64; + } + + //2. sponge_absorb + let num_blocks = max_blocks; + let mut state : [u64;NUM_KECCAK_LANES]= [0; NUM_KECCAK_LANES]; + for i in 0..num_blocks { + if (i == 0) { + for j in 0..LIMBS_PER_BLOCK { + state[j] = sliced_buffer[j]; + } + } else { + for j in 0..LIMBS_PER_BLOCK { + state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j]; + } + } + state = std::hash::keccak_f1600(state); + } + + //3. sponge_squeeze + let mut result = [0; 32]; + + // Each hash limb represents a little-endian integer. Need to reverse bytes before we write into the output array + for i in 0..4 { + let lane = state[i] as Field; + let lane_le = lane.to_le_bytes(8); + for j in 0..8 { + result[8*i+j] = lane_le[7-j]; + } + } + result +} diff --git a/noir/test_programs/execution_success/keccak256/src/main.nr b/noir/test_programs/execution_success/keccak256/src/main.nr index ff2167694d63..bc33fe8eb850 100644 --- a/noir/test_programs/execution_success/keccak256/src/main.nr +++ b/noir/test_programs/execution_success/keccak256/src/main.nr @@ -7,6 +7,9 @@ fn main(x: Field, result: [u8; 32]) { // The padding is taken care of by the program let digest = std::hash::keccak256([x as u8], 1); assert(digest == result); + let digest_with_permutation = std::hash::keccak256::hash([x as u8], 1); + assert(digest_with_permutation == result); + //#1399: variable meesage size let message_size = 4; let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); diff --git a/noir/tooling/backend_interface/src/lib.rs b/noir/tooling/backend_interface/src/lib.rs index 36ebe5ebb917..0a3fb7358033 100644 --- a/noir/tooling/backend_interface/src/lib.rs +++ b/noir/tooling/backend_interface/src/lib.rs @@ -176,6 +176,7 @@ impl BackendOpcodeSupport { "xor".to_string(), "range".to_string(), "keccak256".to_string(), + "keccakf1600".to_string(), "recursive_aggregation".to_string(), "ecdsa_secp256r1".to_string(), ]), From b1a3c7c483a321c3fb823f5487206477b249e843 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 5 Jan 2024 14:54:43 +0000 Subject: [PATCH 2/9] Update barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp --- barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp index fe9fb2794653..bbc2159c7560 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp @@ -866,7 +866,7 @@ std::array, keccak::NUM_KECCAK_LANES> keccak: } // This function is the same as sponge_squeeze, except that it does not convert -// from extented representation and assumes the input has already being converted +// from extended representation and assumes the input has already being converted template stdlib::byte_array keccak::sponge_squeeze_for_permutation_opcode( std::array, NUM_KECCAK_LANES> lanes, Builder* context) From 7e08ca5659132e6877f77064f8514520a3755ae0 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 5 Jan 2024 15:24:12 +0000 Subject: [PATCH 3/9] update acir.cpp --- noir/acvm-repo/acir/codegen/acir.cpp | 52 +++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/noir/acvm-repo/acir/codegen/acir.cpp b/noir/acvm-repo/acir/codegen/acir.cpp index 185329407527..29981e66b35f 100644 --- a/noir/acvm-repo/acir/codegen/acir.cpp +++ b/noir/acvm-repo/acir/codegen/acir.cpp @@ -155,6 +155,15 @@ namespace Circuit { static Keccak256VariableLength bincodeDeserialize(std::vector); }; + struct Keccakf1600 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Keccakf1600&, const Keccakf1600&); + std::vector bincodeSerialize() const; + static Keccakf1600 bincodeDeserialize(std::vector); + }; + struct RecursiveAggregation { std::vector verification_key; std::vector proof; @@ -168,7 +177,7 @@ namespace Circuit { static RecursiveAggregation bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; @@ -2188,6 +2197,47 @@ Circuit::BlackBoxFuncCall::Keccak256VariableLength serde::Deserializable BlackBoxFuncCall::Keccakf1600::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::Keccakf1600 BlackBoxFuncCall::Keccakf1600::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Keccakf1600 &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::Keccakf1600 serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::Keccakf1600 obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const BlackBoxFuncCall::RecursiveAggregation &lhs, const BlackBoxFuncCall::RecursiveAggregation &rhs) { From 452c6788930581e03d8336562f51d76cc769a764 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 5 Jan 2024 15:31:32 +0000 Subject: [PATCH 4/9] remove hash_to_field --- .../cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 47d2c9d59650..a3ffaedd7207 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -447,7 +447,6 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .keccak_permutations = { keccak_permutation }, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, - .hash_to_field_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .recursion_constraints = {}, .constraints = {}, From d99e9d2e2168540cea38a1f1204e99f3285526b8 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 5 Jan 2024 15:32:58 +0000 Subject: [PATCH 5/9] remove HashToFieldSecurity variant --- .../noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 4b067e269064..076039efbbfb 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -604,8 +604,6 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { // 32 byte hash algorithms BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s => Some(32), BlackBoxFunc::Keccakf1600 => Some(25), - // Hash to field returns a field element - BlackBoxFunc::HashToField128Security => Some(1), // Pedersen commitment returns a point BlackBoxFunc::PedersenCommitment => Some(2), // Pedersen hash returns a field From 5e4789686ddde25ae2157c6cfa81f0a37b4e3ebc Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 5 Jan 2024 15:48:43 +0000 Subject: [PATCH 6/9] Add keccak_permutations to test files --- .../barretenberg/dsl/acir_format/block_constraint.test.cpp | 1 + .../src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp | 3 +++ .../src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp | 4 ++++ .../dsl/acir_format/recursion_constraint.test.cpp | 2 ++ 4 files changed, 10 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index a20de2c75db0..649c751edc55 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -120,6 +120,7 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 9bcbc89ec879..fb8f99288ee9 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -99,6 +99,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, @@ -139,6 +140,7 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, @@ -174,6 +176,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index 5d26a4b7a379..0e6fe54509af 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -133,6 +133,7 @@ TEST(ECDSASecp256r1, test_hardcoded) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, @@ -174,6 +175,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, @@ -213,6 +215,7 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, @@ -247,6 +250,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index c4decf04a3af..700ea0b70b01 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -92,6 +92,7 @@ Builder create_inner_circuit() .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, @@ -249,6 +250,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) .blake2s_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, + .keccak_permutations = {}, .pedersen_constraints = {}, .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, From 6d7fb8e5278d5eef5d7acdc4f8e1abc7f0ddac5b Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 5 Jan 2024 16:03:11 +0000 Subject: [PATCH 7/9] add back missing match case --- noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index cada0b97f93b..67610205d8c2 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -374,6 +374,7 @@ fn simplify_black_box_func( match bb_func { BlackBoxFunc::SHA256 => simplify_hash(dfg, arguments, acvm::blackbox_solver::sha256), BlackBoxFunc::Blake2s => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake2s), + BlackBoxFunc::Keccakf1600 => SimplifyResult::None, //TODO BlackBoxFunc::Keccak256 => { match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) { (Some((input, _)), Some(num_bytes)) if array_is_constant(dfg, &input) => { From b39217b18bf522a5b9780af362fd5297d64d969c Mon Sep 17 00:00:00 2001 From: kevaundray Date: Fri, 5 Jan 2024 16:05:06 +0000 Subject: [PATCH 8/9] Add guillaumes name to the todo --- noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 67610205d8c2..96028709a0ee 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -374,7 +374,7 @@ fn simplify_black_box_func( match bb_func { BlackBoxFunc::SHA256 => simplify_hash(dfg, arguments, acvm::blackbox_solver::sha256), BlackBoxFunc::Blake2s => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake2s), - BlackBoxFunc::Keccakf1600 => SimplifyResult::None, //TODO + BlackBoxFunc::Keccakf1600 => SimplifyResult::None, //TODO(Guillaume) BlackBoxFunc::Keccak256 => { match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) { (Some((input, _)), Some(num_bytes)) if array_is_constant(dfg, &input) => { From 9001e3b08a568a5bd579472ef172ad4f63b6dc4b Mon Sep 17 00:00:00 2001 From: TomAFrench Date: Wed, 22 May 2024 13:48:19 +0000 Subject: [PATCH 9/9] chore: fix merge --- .../dsl/acir_format/acir_format.cpp | 3 - .../dsl/acir_format/acir_format.test.cpp | 86 ------------------- 2 files changed, 89 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index a20976ffd4ac..3dc7fc15e6c9 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -79,9 +79,6 @@ void build_constraints(Builder& builder, for (const auto& constraint : constraint_system.keccak_permutations) { create_keccak_permutations(builder, constraint); } - for (const auto& constraint : constraint_system.keccak_permutations) { - create_keccak_permutations(builder, constraint); - } // Add pedersen constraints for (const auto& constraint : constraint_system.pedersen_constraints) { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index f3bb625205da..cf6df51ea90d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -476,92 +476,6 @@ TEST_F(AcirFormatTests, TestVarKeccak) EXPECT_EQ(verifier.verify_proof(proof), true); } -TEST_F(AcirFormatTests, TestKeccakPermutation) -{ - Keccakf1600 - keccak_permutation{ - .state = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }, - .result = { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }, - }; - - acir_format constraint_system{ .varnum = 51, - .public_inputs = {}, - .logic_constraints = {}, - .range_constraints = {}, - .sha256_constraints = {}, - .schnorr_constraints = {}, - .ecdsa_k1_constraints = {}, - .ecdsa_r1_constraints = {}, - .blake2s_constraints = {}, - .keccak_constraints = {}, - .keccak_var_constraints = {}, - .keccak_permutations = { keccak_permutation }, - .pedersen_constraints = {}, - .pedersen_hash_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .recursion_constraints = {}, - .constraints = {}, - .block_constraints = {} }; - - WitnessVector witness{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }; - - auto builder = create_circuit_with_witness(constraint_system, witness); - - auto composer = Composer(); - auto prover = composer.create_ultra_with_keccak_prover(builder); - auto proof = prover.construct_proof(); - - auto verifier = composer.create_ultra_with_keccak_verifier(builder); - - EXPECT_EQ(verifier.verify_proof(proof), true); -} - -TEST_F(AcirFormatTests, TestKeccakPermutation) -{ - Keccakf1600 - keccak_permutation{ - .state = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }, - .result = { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }, - }; - - acir_format constraint_system{ .varnum = 51, - .public_inputs = {}, - .logic_constraints = {}, - .range_constraints = {}, - .sha256_constraints = {}, - .schnorr_constraints = {}, - .ecdsa_k1_constraints = {}, - .ecdsa_r1_constraints = {}, - .blake2s_constraints = {}, - .keccak_constraints = {}, - .keccak_var_constraints = {}, - .keccak_permutations = { keccak_permutation }, - .pedersen_constraints = {}, - .pedersen_hash_constraints = {}, - .fixed_base_scalar_mul_constraints = {}, - .recursion_constraints = {}, - .constraints = {}, - .block_constraints = {} }; - - WitnessVector witness{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }; - - auto builder = create_circuit_with_witness(constraint_system, witness); - - auto composer = Composer(); - auto prover = composer.create_ultra_with_keccak_prover(builder); - auto proof = prover.construct_proof(); - - auto verifier = composer.create_ultra_with_keccak_verifier(builder); - - EXPECT_EQ(verifier.verify_proof(proof), true); -} - TEST_F(AcirFormatTests, TestKeccakPermutation) { Keccakf1600