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 9d3542c0092..93ced6a19b3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -448,6 +448,15 @@ struct BlackBoxOp { static Blake2s bincodeDeserialize(std::vector); }; + struct Blake3 { + Circuit::HeapVector message; + Circuit::HeapArray output; + + friend bool operator==(const Blake3&, const Blake3&); + std::vector bincodeSerialize() const; + static Blake3 bincodeDeserialize(std::vector); + }; + struct Keccak256 { Circuit::HeapVector message; Circuit::HeapArray output; @@ -547,6 +556,7 @@ struct BlackBoxOp { std::variant namespace Circuit { +inline bool operator==(const BlackBoxOp::Blake3& lhs, const BlackBoxOp::Blake3& rhs) +{ + if (!(lhs.message == rhs.message)) { + return false; + } + if (!(lhs.output == rhs.output)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::Blake3::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::Blake3 BlackBoxOp::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::BlackBoxOp::Blake3& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Blake3 serde::Deserializable::deserialize(Deserializer& deserializer) +{ + Circuit::BlackBoxOp::Blake3 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + inline bool operator==(const BlackBoxOp::Keccak256& lhs, const BlackBoxOp::Keccak256& rhs) { if (!(lhs.message == rhs.message)) { diff --git a/noir/acvm-repo/acir/codegen/acir.cpp b/noir/acvm-repo/acir/codegen/acir.cpp index d0b920e057c..7d9d293a776 100644 --- a/noir/acvm-repo/acir/codegen/acir.cpp +++ b/noir/acvm-repo/acir/codegen/acir.cpp @@ -428,6 +428,15 @@ namespace Circuit { static Blake2s bincodeDeserialize(std::vector); }; + struct Blake3 { + Circuit::HeapVector message; + Circuit::HeapArray output; + + friend bool operator==(const Blake3&, const Blake3&); + std::vector bincodeSerialize() const; + static Blake3 bincodeDeserialize(std::vector); + }; + struct Keccak256 { Circuit::HeapVector message; Circuit::HeapArray output; @@ -525,7 +534,7 @@ namespace Circuit { static EmbeddedCurveDouble bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); std::vector bincodeSerialize() const; @@ -2595,6 +2604,47 @@ Circuit::BlackBoxOp::Blake2s serde::Deserializable return obj; } +namespace Circuit { + + inline bool operator==(const BlackBoxOp::Blake3 &lhs, const BlackBoxOp::Blake3 &rhs) { + if (!(lhs.message == rhs.message)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::Blake3::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::Blake3 BlackBoxOp::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::BlackBoxOp::Blake3 &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Blake3 serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::Blake3 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const BlackBoxOp::Keccak256 &lhs, const BlackBoxOp::Keccak256 &rhs) { diff --git a/noir/acvm-repo/brillig/src/black_box.rs b/noir/acvm-repo/brillig/src/black_box.rs index 4c755f8eae2..2286539e4c1 100644 --- a/noir/acvm-repo/brillig/src/black_box.rs +++ b/noir/acvm-repo/brillig/src/black_box.rs @@ -9,6 +9,8 @@ pub enum BlackBoxOp { Sha256 { message: HeapVector, output: HeapArray }, /// Calculates the Blake2s hash of the inputs. Blake2s { message: HeapVector, output: HeapArray }, + /// Calculates the Blake3 hash of the inputs. + Blake3 { message: HeapVector, output: HeapArray }, /// Calculates the Keccak256 hash of the inputs. Keccak256 { message: HeapVector, output: HeapArray }, /// Verifies a ECDSA signature over the secp256k1 curve. @@ -42,7 +44,13 @@ pub enum BlackBoxOp { /// Performs scalar multiplication over the embedded curve. FixedBaseScalarMul { low: RegisterIndex, high: RegisterIndex, result: HeapArray }, /// Performs addtion over the embedded curve. - EmbeddedCurveAdd { input1_x: RegisterIndex, input1_y: RegisterIndex, input2_x: RegisterIndex, input2_y: RegisterIndex, result: HeapArray }, + EmbeddedCurveAdd { + input1_x: RegisterIndex, + input1_y: RegisterIndex, + input2_x: RegisterIndex, + input2_y: RegisterIndex, + result: HeapArray, + }, /// Performs point doubling over the embedded curve. EmbeddedCurveDouble { input1_x: RegisterIndex, input1_y: RegisterIndex, result: HeapArray }, } diff --git a/noir/acvm-repo/brillig_vm/src/black_box.rs b/noir/acvm-repo/brillig_vm/src/black_box.rs index 012d527e44e..a6e904c2902 100644 --- a/noir/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/acvm-repo/brillig_vm/src/black_box.rs @@ -1,7 +1,7 @@ use acir::brillig::{BlackBoxOp, HeapArray, HeapVector, Value}; use acir::{BlackBoxFunc, FieldElement}; use acvm_blackbox_solver::{ - blake2s, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccak256, sha256, + blake2s, blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccak256, sha256, BlackBoxFunctionSolver, BlackBoxResolutionError, }; @@ -58,6 +58,12 @@ pub(crate) fn evaluate_black_box( memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); Ok(()) } + BlackBoxOp::Blake3 { message, output } => { + let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let bytes = blake3(message.as_slice())?; + memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + Ok(()) + } BlackBoxOp::Keccak256 { message, output } => { let message = to_u8_vec(read_heap_vector(memory, registers, message)); let bytes = keccak256(message.as_slice())?; @@ -187,6 +193,7 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { match op { BlackBoxOp::Sha256 { .. } => BlackBoxFunc::SHA256, BlackBoxOp::Blake2s { .. } => BlackBoxFunc::Blake2s, + BlackBoxOp::Blake3 { .. } => BlackBoxFunc::Blake3, BlackBoxOp::Keccak256 { .. } => BlackBoxFunc::Keccak256, BlackBoxOp::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1, BlackBoxOp::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1, diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 8235cbf6340..5a5f9694534 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -41,6 +41,19 @@ pub(crate) fn convert_black_box_call( unreachable!("ICE: Blake2s expects one array argument and one array result") } } + BlackBoxFunc::Blake3 => { + if let ([message], [BrilligVariable::BrilligArray(result_array)]) = + (function_arguments, function_results) + { + let message_vector = convert_array_or_vector(brillig_context, message, bb_func); + brillig_context.black_box_op_instruction(BlackBoxOp::Blake3 { + message: message_vector.to_heap_vector(), + output: result_array.to_heap_array(), + }); + } else { + unreachable!("ICE: Blake3 expects one array argument and one array result") + } + } BlackBoxFunc::Keccak256 => { if let ( [message, BrilligVariable::Simple(array_size)], @@ -171,8 +184,7 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::EmbeddedCurveAdd => { if let ( - [BrilligVariable::Simple(input1_x), BrilligVariable::Simple(input1_y), - BrilligVariable::Simple(input2_x), BrilligVariable::Simple(input2_y)], + [BrilligVariable::Simple(input1_x), BrilligVariable::Simple(input1_y), BrilligVariable::Simple(input2_x), BrilligVariable::Simple(input2_y)], [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { @@ -188,7 +200,7 @@ pub(crate) fn convert_black_box_call( "ICE: EmbeddedCurveAdd expects four register arguments and one array result" ) } - } + } BlackBoxFunc::EmbeddedCurveDouble => { if let ( [BrilligVariable::Simple(input1_x), BrilligVariable::Simple(input1_y)], @@ -218,9 +230,6 @@ pub(crate) fn convert_black_box_call( BlackBoxFunc::RecursiveAggregation => unimplemented!( "ICE: `BlackBoxFunc::RecursiveAggregation` is not implemented by the Brillig VM" ), - BlackBoxFunc::Blake3 => { - unimplemented!("ICE: `BlackBoxFunc::Blake3` is not implemented by the Brillig VM") - } BlackBoxFunc::Keccakf1600 => { unimplemented!("ICE: `BlackBoxFunc::Keccakf1600` is not implemented by the Brillig VM") } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index 0d35d948694..6add4c97e32 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -353,6 +353,9 @@ impl DebugShow { BlackBoxOp::Blake2s { message, output } => { debug_println!(self.enable_debug_trace, " BLAKE2S {} -> {}", message, output); } + BlackBoxOp::Blake3 { message, output } => { + debug_println!(self.enable_debug_trace, " BLAKE3 {} -> {}", message, output); + } BlackBoxOp::EcdsaSecp256k1 { hashed_msg, public_key_x, diff --git a/noir/test_programs/execution_success/brillig_blake3/Nargo.toml b/noir/test_programs/execution_success/brillig_blake3/Nargo.toml new file mode 100644 index 00000000000..879476dbdcf --- /dev/null +++ b/noir/test_programs/execution_success/brillig_blake3/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "brillig_blake3" +type = "bin" +authors = [""] +compiler_version = ">=0.22.0" + +[dependencies] diff --git a/noir/test_programs/execution_success/brillig_blake3/Prover.toml b/noir/test_programs/execution_success/brillig_blake3/Prover.toml new file mode 100644 index 00000000000..c807701479b --- /dev/null +++ b/noir/test_programs/execution_success/brillig_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/brillig_blake3/src/main.nr b/noir/test_programs/execution_success/brillig_blake3/src/main.nr new file mode 100644 index 00000000000..05a5b31f936 --- /dev/null +++ b/noir/test_programs/execution_success/brillig_blake3/src/main.nr @@ -0,0 +1,6 @@ +use dep::std; + +unconstrained fn main(x: [u8; 5], result: [u8; 32]) { + let digest = std::hash::blake3(x); + assert(digest == result); +}