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 23c936b6a27f..912c23e77df4 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -119,6 +119,11 @@ void build_constraints(Builder& builder, acir_format const& constraint_system, b create_blake2s_constraints(builder, constraint); } + // Add blake3 constraints + for (const auto& constraint : constraint_system.blake3_constraints) { + create_blake3_constraints(builder, constraint); + } + // Add keccak constraints for (const auto& constraint : constraint_system.keccak_constraints) { create_keccak_constraints(builder, constraint); 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 6dadfa7bd6ef..a2f198e2f7fc 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -3,6 +3,7 @@ #include "barretenberg/dsl/types.hpp" #include "barretenberg/serialize/msgpack.hpp" #include "blake2s_constraint.hpp" +#include "blake3_constraint.hpp" #include "block_constraint.hpp" #include "ecdsa_secp256k1.hpp" #include "ecdsa_secp256r1.hpp" @@ -30,6 +31,7 @@ struct acir_format { std::vector ecdsa_k1_constraints; std::vector ecdsa_r1_constraints; std::vector blake2s_constraints; + std::vector blake3_constraints; std::vector keccak_constraints; std::vector keccak_var_constraints; std::vector keccak_permutations; @@ -56,6 +58,7 @@ struct acir_format { ecdsa_k1_constraints, ecdsa_r1_constraints, blake2s_constraints, + blake3_constraints, keccak_constraints, keccak_var_constraints, keccak_permutations, 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 a3ffaedd7207..4b6fdcd52a5a 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 @@ -37,6 +37,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -144,6 +145,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -209,6 +211,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -297,6 +300,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -404,6 +408,7 @@ TEST_F(AcirFormatTests, TestVarKeccak) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = { keccak }, .keccak_permutations = {}, @@ -442,6 +447,7 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = { keccak_permutation }, 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 20da55ce6c3e..9b6b36d7e065 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 @@ -3,6 +3,7 @@ #include "barretenberg/common/container.hpp" #include "barretenberg/common/throw_or_abort.hpp" #include "barretenberg/dsl/acir_format/blake2s_constraint.hpp" +#include "barretenberg/dsl/acir_format/blake3_constraint.hpp" #include "barretenberg/dsl/acir_format/block_constraint.hpp" #include "barretenberg/dsl/acir_format/ecdsa_secp256k1.hpp" #include "barretenberg/dsl/acir_format/keccak_constraint.hpp" @@ -113,6 +114,17 @@ void handle_blackbox_func_call(Circuit::Opcode::BlackBoxFuncCall const& arg, aci }), .result = map(arg.outputs, [](auto& e) { return e.value; }), }); + } else if constexpr (std::is_same_v) { + af.blake3_constraints.push_back(Blake3Constraint{ + .inputs = map(arg.inputs, + [](auto& e) { + return Blake3Input{ + .witness = e.witness.value, + .num_bits = e.num_bits, + }; + }), + .result = map(arg.outputs, [](auto& e) { return e.value; }), + }); } else if constexpr (std::is_same_v) { af.schnorr_constraints.push_back(SchnorrConstraint{ .message = map(arg.message, [](auto& e) { return e.witness.value; }), diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.cpp new file mode 100644 index 000000000000..7348ee25d0da --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.cpp @@ -0,0 +1,44 @@ +#include "blake3_constraint.hpp" +#include "round.hpp" + +namespace acir_format { + +template void create_blake3_constraints(Builder& builder, const Blake3Constraint& constraint) +{ + using byte_array_ct = proof_system::plonk::stdlib::byte_array; + using field_ct = proof_system::plonk::stdlib::field_t; + + // Create byte array struct + byte_array_ct arr(&builder); + + // Get the witness assignment for each witness index + // Write the witness assignment to the byte_array + for (const auto& witness_index_num_bits : constraint.inputs) { + auto witness_index = witness_index_num_bits.witness; + auto num_bits = witness_index_num_bits.num_bits; + + // XXX: The implementation requires us to truncate the element to the nearest byte and not bit + auto num_bytes = round_to_nearest_byte(num_bits); + + field_ct element = field_ct::from_witness_index(&builder, witness_index); + byte_array_ct element_bytes(element, num_bytes); + + arr.write(element_bytes); + } + + byte_array_ct output_bytes = proof_system::plonk::stdlib::blake3s(arr); + + // Convert byte array to vector of field_t + auto bytes = output_bytes.bytes(); + + for (size_t i = 0; i < bytes.size(); ++i) { + builder.assert_equal(bytes[i].normalize().witness_index, constraint.result[i]); + } +} + +template void create_blake3_constraints(UltraCircuitBuilder& builder, + const Blake3Constraint& constraint); +template void create_blake3_constraints(GoblinUltraCircuitBuilder& builder, + const Blake3Constraint& constraint); + +} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.hpp new file mode 100644 index 000000000000..2fe421fb16ce --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/blake3_constraint.hpp @@ -0,0 +1,29 @@ +#pragma once +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include +#include + +namespace acir_format { + +struct Blake3Input { + uint32_t witness; + uint32_t num_bits; + + // For serialization, update with any new fields + MSGPACK_FIELDS(witness, num_bits); + friend bool operator==(Blake3Input const& lhs, Blake3Input const& rhs) = default; +}; + +struct Blake3Constraint { + std::vector inputs; + std::vector result; + + // For serialization, update with any new fields + MSGPACK_FIELDS(inputs, result); + friend bool operator==(Blake3Constraint const& lhs, Blake3Constraint const& rhs) = default; +}; + +template void create_blake3_constraints(Builder& builder, const Blake3Constraint& constraint); + +} // namespace acir_format 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 649c751edc55..0473907838a2 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 @@ -118,6 +118,7 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, 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 fb8f99288ee9..4dfdff06fb2c 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 @@ -97,6 +97,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .ecdsa_k1_constraints = { ecdsa_k1_constraint }, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -138,6 +139,7 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .ecdsa_k1_constraints = { ecdsa_k1_constraint }, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -174,6 +176,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .ecdsa_k1_constraints = { ecdsa_k1_constraint }, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, 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 0e6fe54509af..deb95e656b5a 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 @@ -131,6 +131,7 @@ TEST(ECDSASecp256r1, test_hardcoded) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = { ecdsa_r1_constraint }, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -173,6 +174,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = { ecdsa_r1_constraint }, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -213,6 +215,7 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = { ecdsa_r1_constraint }, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -248,6 +251,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = { ecdsa_r1_constraint }, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, 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 700ea0b70b01..63a60cda21ab 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 @@ -90,6 +90,7 @@ Builder create_inner_circuit() .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, @@ -248,6 +249,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) .ecdsa_k1_constraints = {}, .ecdsa_r1_constraints = {}, .blake2s_constraints = {}, + .blake3_constraints = {}, .keccak_constraints = {}, .keccak_var_constraints = {}, .keccak_permutations = {}, 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 b0c8baf17d17..4aa912073c84 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -70,6 +70,15 @@ struct BlackBoxFuncCall { static Blake2s bincodeDeserialize(std::vector); }; + struct Blake3 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Blake3&, const Blake3&); + std::vector bincodeSerialize() const; + static Blake3 bincodeDeserialize(std::vector); + }; + struct SchnorrVerify { Circuit::FunctionInput public_key_x; Circuit::FunctionInput public_key_y; @@ -182,6 +191,7 @@ struct BlackBoxFuncCall { RANGE, SHA256, Blake2s, + Blake3, SchnorrVerify, PedersenCommitment, PedersenHash, @@ -2045,6 +2055,58 @@ Circuit::BlackBoxFuncCall::Blake2s serde::Deserializable BlackBoxFuncCall::Blake3::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxFuncCall::Blake3 BlackBoxFuncCall::Blake3::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::Blake3& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::Blake3 serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxFuncCall::Blake3 obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + inline bool operator==(const BlackBoxFuncCall::SchnorrVerify& lhs, const BlackBoxFuncCall::SchnorrVerify& rhs) { if (!(lhs.public_key_x == rhs.public_key_x)) { diff --git a/noir/Cargo.lock b/noir/Cargo.lock index f1fb11aea6af..82d4030f100a 100644 --- a/noir/Cargo.lock +++ b/noir/Cargo.lock @@ -58,6 +58,7 @@ version = "0.38.0" dependencies = [ "acir", "blake2", + "blake3", "k256", "p256", "sha2", @@ -344,6 +345,12 @@ dependencies = [ "rand", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + [[package]] name = "arrayvec" version = "0.7.4" @@ -575,6 +582,19 @@ dependencies = [ "digest", ] +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -963,6 +983,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "core-foundation-sys" version = "0.8.4" diff --git a/noir/acvm-repo/acir/codegen/acir.cpp b/noir/acvm-repo/acir/codegen/acir.cpp index 29981e66b35f..27dc427227f1 100644 --- a/noir/acvm-repo/acir/codegen/acir.cpp +++ b/noir/acvm-repo/acir/codegen/acir.cpp @@ -70,6 +70,15 @@ namespace Circuit { static Blake2s bincodeDeserialize(std::vector); }; + struct Blake3 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Blake3&, const Blake3&); + std::vector bincodeSerialize() const; + static Blake3 bincodeDeserialize(std::vector); + }; + struct SchnorrVerify { Circuit::FunctionInput public_key_x; Circuit::FunctionInput public_key_y; @@ -177,7 +186,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; @@ -1830,6 +1839,47 @@ Circuit::BlackBoxFuncCall::Blake2s serde::Deserializable BlackBoxFuncCall::Blake3::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::Blake3 BlackBoxFuncCall::Blake3::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::Blake3 &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::Blake3 serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::Blake3 obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const BlackBoxFuncCall::SchnorrVerify &lhs, const BlackBoxFuncCall::SchnorrVerify &rhs) { 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 47de9eaa9c27..6e45a9a2c21e 100644 --- a/noir/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/noir/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -19,6 +19,8 @@ pub enum BlackBoxFunc { SHA256, /// Calculates the Blake2s hash of the inputs. Blake2s, + /// Calculates the Blake3 hash of the inputs. + Blake3, /// Verifies a Schnorr signature over a curve which is "pairing friendly" with the curve on which the ACIR circuit is defined. /// /// The exact curve which this signature uses will vary based on the curve being used by ACIR. @@ -57,6 +59,7 @@ impl BlackBoxFunc { BlackBoxFunc::SHA256 => "sha256", BlackBoxFunc::SchnorrVerify => "schnorr_verify", BlackBoxFunc::Blake2s => "blake2s", + BlackBoxFunc::Blake3 => "blake3", BlackBoxFunc::PedersenCommitment => "pedersen_commitment", BlackBoxFunc::PedersenHash => "pedersen_hash", BlackBoxFunc::EcdsaSecp256k1 => "ecdsa_secp256k1", @@ -75,6 +78,7 @@ impl BlackBoxFunc { "sha256" => Some(BlackBoxFunc::SHA256), "schnorr_verify" => Some(BlackBoxFunc::SchnorrVerify), "blake2s" => Some(BlackBoxFunc::Blake2s), + "blake3" => Some(BlackBoxFunc::Blake3), "pedersen_commitment" => Some(BlackBoxFunc::PedersenCommitment), "pedersen_hash" => Some(BlackBoxFunc::PedersenHash), "ecdsa_secp256k1" => Some(BlackBoxFunc::EcdsaSecp256k1), 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 3775310ec3b9..fea12f9c08a5 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 @@ -39,6 +39,10 @@ pub enum BlackBoxFuncCall { inputs: Vec, outputs: Vec, }, + Blake3 { + inputs: Vec, + outputs: Vec, + }, SchnorrVerify { public_key_x: FunctionInput, public_key_y: FunctionInput, @@ -125,6 +129,7 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::RANGE { .. } => BlackBoxFunc::RANGE, BlackBoxFuncCall::SHA256 { .. } => BlackBoxFunc::SHA256, BlackBoxFuncCall::Blake2s { .. } => BlackBoxFunc::Blake2s, + BlackBoxFuncCall::Blake3 { .. } => BlackBoxFunc::Blake3, BlackBoxFuncCall::SchnorrVerify { .. } => BlackBoxFunc::SchnorrVerify, BlackBoxFuncCall::PedersenCommitment { .. } => BlackBoxFunc::PedersenCommitment, BlackBoxFuncCall::PedersenHash { .. } => BlackBoxFunc::PedersenHash, @@ -146,6 +151,7 @@ impl BlackBoxFuncCall { match self { BlackBoxFuncCall::SHA256 { inputs, .. } | BlackBoxFuncCall::Blake2s { inputs, .. } + | BlackBoxFuncCall::Blake3 { inputs, .. } | BlackBoxFuncCall::Keccak256 { inputs, .. } | BlackBoxFuncCall::Keccakf1600 { inputs, .. } | BlackBoxFuncCall::PedersenCommitment { inputs, .. } @@ -236,6 +242,7 @@ impl BlackBoxFuncCall { match self { BlackBoxFuncCall::SHA256 { outputs, .. } | BlackBoxFuncCall::Blake2s { outputs, .. } + | BlackBoxFuncCall::Blake3 { outputs, .. } | BlackBoxFuncCall::Keccak256 { outputs, .. } | BlackBoxFuncCall::Keccakf1600 { outputs, .. } | BlackBoxFuncCall::RecursiveAggregation { diff --git a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs index 4d2a49c330ba..003124f8b63a 100644 --- a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -117,7 +117,8 @@ pub(super) fn transform_internal( output_aggregation_object: outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Blake2s { outputs, .. } => { + | acir::circuit::opcodes::BlackBoxFuncCall::Blake2s { outputs, .. } + | acir::circuit::opcodes::BlackBoxFuncCall::Blake3 { outputs, .. } => { for witness in outputs { transformer.mark_solvable(*witness); } diff --git a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 0ae68bf0eb2d..c36596235a26 100644 --- a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -3,7 +3,7 @@ use acir::{ native_types::{Witness, WitnessMap}, FieldElement, }; -use acvm_blackbox_solver::{blake2s, keccak256, sha256}; +use acvm_blackbox_solver::{blake2s, blake3, keccak256, sha256}; use self::{hash::keccakf1600, pedersen::pedersen_hash}; @@ -83,6 +83,14 @@ pub(crate) fn solve( blake2s, bb_func.get_black_box_func(), ), + BlackBoxFuncCall::Blake3 { inputs, outputs } => solve_generic_256_hash_opcode( + initial_witness, + inputs, + None, + outputs, + blake3, + bb_func.get_black_box_func(), + ), BlackBoxFuncCall::Keccak256 { inputs, outputs } => solve_generic_256_hash_opcode( initial_witness, inputs, diff --git a/noir/acvm-repo/blackbox_solver/Cargo.toml b/noir/acvm-repo/blackbox_solver/Cargo.toml index be2a58417f46..258321d8ef42 100644 --- a/noir/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/acvm-repo/blackbox_solver/Cargo.toml @@ -17,6 +17,7 @@ acir.workspace = true thiserror.workspace = true blake2 = "0.10.6" +blake3 = "1.5.0" sha2 = "0.10.6" sha3 = "0.10.6" k256 = { version = "0.11.0", features = [ diff --git a/noir/acvm-repo/blackbox_solver/src/lib.rs b/noir/acvm-repo/blackbox_solver/src/lib.rs index cf2cf295f7a4..ede648ef75ee 100644 --- a/noir/acvm-repo/blackbox_solver/src/lib.rs +++ b/noir/acvm-repo/blackbox_solver/src/lib.rs @@ -59,6 +59,10 @@ pub fn blake2s(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::Blake2s, err)) } +pub fn blake3(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { + Ok(blake3::hash(inputs).into()) +} + pub fn keccak256(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { generic_hash_256::(inputs) .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::Keccak256, err)) 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 076039efbbfb..c9f7ee51e977 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 @@ -155,6 +155,7 @@ impl GeneratedAcir { BlackBoxFunc::Blake2s => { BlackBoxFuncCall::Blake2s { inputs: inputs[0].clone(), outputs } } + BlackBoxFunc::Blake3 => BlackBoxFuncCall::Blake3 { inputs: inputs[0].clone(), outputs }, BlackBoxFunc::SchnorrVerify => { BlackBoxFuncCall::SchnorrVerify { public_key_x: inputs[0][0], @@ -572,6 +573,7 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s + | BlackBoxFunc::Blake3 | BlackBoxFunc::PedersenCommitment | BlackBoxFunc::PedersenHash => None, @@ -602,7 +604,10 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { // or the operation. BlackBoxFunc::AND | BlackBoxFunc::XOR => Some(1), // 32 byte hash algorithms - BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s => Some(32), + BlackBoxFunc::Keccak256 + | BlackBoxFunc::SHA256 + | BlackBoxFunc::Blake2s + | BlackBoxFunc::Blake3 => Some(32), BlackBoxFunc::Keccakf1600 => Some(25), // Pedersen commitment returns a point BlackBoxFunc::PedersenCommitment => Some(2), 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 96028709a0ee..f77e84d99e09 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::Blake3 => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake3), BlackBoxFunc::Keccakf1600 => SimplifyResult::None, //TODO(Guillaume) BlackBoxFunc::Keccak256 => { match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) { diff --git a/noir/noir_stdlib/src/hash.nr b/noir/noir_stdlib/src/hash.nr index ad7e4f2e28f1..5933209d9bc0 100644 --- a/noir/noir_stdlib/src/hash.nr +++ b/noir/noir_stdlib/src/hash.nr @@ -7,6 +7,9 @@ pub fn sha256(_input: [u8; N]) -> [u8; 32] {} #[foreign(blake2s)] pub fn blake2s(_input: [u8; N]) -> [u8; 32] {} +#[foreign(blake3)] +pub fn blake3(_input: [u8; N]) -> [u8; 32] {} + struct PedersenPoint { x : Field, y : Field, diff --git a/noir/test_programs/execution_success/blake3/Nargo.toml b/noir/test_programs/execution_success/blake3/Nargo.toml new file mode 100644 index 000000000000..29f6ad5f11c9 --- /dev/null +++ b/noir/test_programs/execution_success/blake3/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "blake3" +type = "bin" +authors = [""] +compiler_version = ">=0.22.0" + +[dependencies] \ No newline at end of file diff --git a/noir/test_programs/execution_success/blake3/Prover.toml b/noir/test_programs/execution_success/blake3/Prover.toml new file mode 100644 index 000000000000..c807701479b6 --- /dev/null +++ b/noir/test_programs/execution_success/blake3/Prover.toml @@ -0,0 +1,37 @@ +# hello as bytes +# https://connor4312.github.io/blake3/index.html +x = [104, 101, 108, 108, 111] +result = [ + 0xea, + 0x8f, + 0x16, + 0x3d, + 0xb3, + 0x86, + 0x82, + 0x92, + 0x5e, + 0x44, + 0x91, + 0xc5, + 0xe5, + 0x8d, + 0x4b, + 0xb3, + 0x50, + 0x6e, + 0xf8, + 0xc1, + 0x4e, + 0xb7, + 0x8a, + 0x86, + 0xe9, + 0x08, + 0xc5, + 0x62, + 0x4a, + 0x67, + 0x20, + 0x0f, +] diff --git a/noir/test_programs/execution_success/blake3/src/main.nr b/noir/test_programs/execution_success/blake3/src/main.nr new file mode 100644 index 000000000000..3bfea6c5f95e --- /dev/null +++ b/noir/test_programs/execution_success/blake3/src/main.nr @@ -0,0 +1,6 @@ +use dep::std; + +fn main(x: [u8; 5], result: [u8; 32]) { + let digest = std::hash::blake3(x); + assert(digest == result); +} diff --git a/noir/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs b/noir/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs index 09c9596fb5a8..fd8cf6021259 100644 --- a/noir/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs +++ b/noir/tooling/backend_interface/test-binaries/mock_backend/src/info_cmd.rs @@ -14,6 +14,7 @@ const INFO_RESPONSE: &str = r#"{ "range", "sha256", "blake2s", + "blake3", "keccak256", "schnorr_verify", "pedersen", diff --git a/yarn-project/noir-compiler/src/__snapshots__/index.test.ts.snap b/yarn-project/noir-compiler/src/__snapshots__/index.test.ts.snap index 1a99b10fe690..a68f82ec1258 100644 --- a/yarn-project/noir-compiler/src/__snapshots__/index.test.ts.snap +++ b/yarn-project/noir-compiler/src/__snapshots__/index.test.ts.snap @@ -7,7 +7,7 @@ exports[`noir-compiler using nargo compiles the test contract 1`] = ` "events": [], "functions": [ { - "bytecode": "H4sIAAAAAAAA/62QUQqEMAxEY7ew10maxCZ/e5Ut1vufQFRsoX7rQJiZnyG8CAABLk3HfZv3vrX8gbt6/zXHZ6Lpxa0wbDHOIjWnSkx/TF5MUbTMRkZquiRjriaWvXhGJ+FKqzqvbSy+9xeOXHsOA/+TaRwY7+UbOCacAQAA", + "bytecode": "H4sIAAAAAAAA/62QQQ6EMAwDQ7eHfU7SJDS57Ve2ovz/BQgQrVTOYCmyfbGiiQAQ4NJ03Ld571vLH7ir919zfCaaXtwKwxbjLFJzqsT0x+TFFEXLbGSkpksy5mpi2YtndBKutKrz2sbie3/hyLXnMPA/mcaB8Q4eX+1anAEAAA==", "functionType": "secret", "isInternal": false, "name": "constructor", @@ -205,7 +205,7 @@ exports[`noir-compiler using wasm binary compiles the test contract 1`] = ` "events": [], "functions": [ { - "bytecode": "H4sIAAAAAAAA/62QUQqEMAxEY7ew10maxCZ/e5Ut1vufQFRsoX7rQJiZnyG8CAABLk3HfZv3vrX8gbt6/zXHZ6Lpxa0wbDHOIjWnSkx/TF5MUbTMRkZquiRjriaWvXhGJ+FKqzqvbSy+9xeOXHsOA/+TaRwY7+UbOCacAQAA", + "bytecode": "H4sIAAAAAAAA/62QQQ6EMAwDQ7eHfU7SJDS57Ve2ovz/BQgQrVTOYCmyfbGiiQAQ4NJ03Ld571vLH7ir919zfCaaXtwKwxbjLFJzqsT0x+TFFEXLbGSkpksy5mpi2YtndBKutKrz2sbie3/hyLXnMPA/mcaB8Q4eX+1anAEAAA==", "functionType": "secret", "isInternal": false, "name": "constructor",