From ab2c860c747d3051a1cb85ad6ce5fac2a68867f7 Mon Sep 17 00:00:00 2001 From: Ilyas Ridhuan Date: Sat, 25 Jan 2025 19:05:36 +0000 Subject: [PATCH] feat(avm)!: include length in bytecode hash (#11425) Looks like it's easier to constrain the length of bytecode in the avm if we just include it in the hash computation --- .../src/barretenberg/crypto/poseidon2/c_bind.cpp | 4 ++-- .../barretenberg/vm/avm/trace/bytecode_trace.cpp | 6 ++++-- .../vm2/simulation/lib/contract_crypto.cpp | 5 +++-- .../src/main.nr | 3 ++- .../src/contract/contract_class_id.test.ts | 2 +- .../src/contract/contract_class_id.ts | 2 +- .../src/__snapshots__/noir_test_gen.test.ts.snap | 16 ++++++++-------- .../contract_class_registered_event.test.ts | 2 +- 8 files changed, 22 insertions(+), 18 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/c_bind.cpp b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/c_bind.cpp index ef8415a340b..522c68a5463 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/c_bind.cpp @@ -53,8 +53,8 @@ WASM_EXPORT void poseidon2_hash_accumulate(fr::vec_in_buf inputs_buffer, fr::out std::vector to_hash; read(inputs_buffer, to_hash); const size_t numHashes = to_hash.size(); - fr result = 0; - size_t count = 0; + fr result = to_hash[0]; + size_t count = 1; while (count < numHashes) { result = crypto::Poseidon2::hash({ to_hash[count], result }); ++count; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp index e61f1109a09..36137f3c84a 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/trace/bytecode_trace.cpp @@ -80,8 +80,9 @@ std::vector AvmBytecodeTraceBuilder::encode_bytecode(const std::vector& contract_bytes) { + FF bytecode_length_in_bytes = FF(static_cast(contract_bytes.size())); std::vector contract_bytecode_fields = encode_bytecode(contract_bytes); - FF running_hash = FF::zero(); + FF running_hash = bytecode_length_in_bytes; for (auto& contract_bytecode_field : contract_bytecode_fields) { running_hash = poseidon2::hash({ contract_bytecode_field, running_hash }); } @@ -114,7 +115,8 @@ void AvmBytecodeTraceBuilder::build_bytecode_hash_columns() if (contract_bytecode.bytecode.size() == 0) { vinfo("Excluding non-existent contract from bytecode hash columns..."); } else { - FF running_hash = FF::zero(); + auto bytecode_length_in_bytes = FF(static_cast(contract_bytecode.bytecode.size())); + FF running_hash = bytecode_length_in_bytes; auto field_encoded_bytecode = encode_bytecode(contract_bytecode.bytecode); // This size is already based on the number of fields for (size_t i = 0; i < field_encoded_bytecode.size(); ++i) { diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/contract_crypto.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/contract_crypto.cpp index c923c27b43a..2a3e0acdd6f 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/contract_crypto.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/contract_crypto.cpp @@ -27,8 +27,9 @@ FF compute_public_bytecode_commitment(std::span bytecode) return contract_bytecode_fields; }; + FF bytecode_length_in_bytes = FF(static_cast(bytecode.size())); std::vector contract_bytecode_fields = encode_bytecode(bytecode); - FF running_hash = 0; + FF running_hash = bytecode_length_in_bytes; for (const auto& contract_bytecode_field : contract_bytecode_fields) { running_hash = poseidon2::hash({ contract_bytecode_field, running_hash }); } @@ -66,4 +67,4 @@ FF compute_contract_address(const ContractInstance& contract_instance) return (grumpkin::g1::affine_one * h_fq + contract_instance.public_keys.incoming_viewing_key).x; } -} // namespace bb::avm2::simulation \ No newline at end of file +} // namespace bb::avm2::simulation diff --git a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr index a237e9a0bd0..a5d99a95869 100644 --- a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr @@ -56,7 +56,8 @@ contract ContractClassRegisterer { (bytecode_length_in_bytes / 31) + (bytecode_length_in_bytes % 31 != 0) as u32; assert(bytecode_length_in_fields < MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS); - let mut computed_public_bytecode_commitment = 0; + // The first value in the running hash is the bytecode length in bytes + let mut computed_public_bytecode_commitment = packed_public_bytecode[0]; // We can hash up to MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS - 1, since the first element is the length for i in 0..(MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS - 1) { // While we are forced to run the hash MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS times in the circuit, diff --git a/yarn-project/circuits.js/src/contract/contract_class_id.test.ts b/yarn-project/circuits.js/src/contract/contract_class_id.test.ts index 323c8783352..2d1296854ca 100644 --- a/yarn-project/circuits.js/src/contract/contract_class_id.test.ts +++ b/yarn-project/circuits.js/src/contract/contract_class_id.test.ts @@ -25,7 +25,7 @@ describe('ContractClass', () => { }; expect(computeContractClassId(contractClass).toString()).toMatchInlineSnapshot( - `"0x2d5c712c483891d42e5bca539e8516fc52b5b024568ac71e4fe47c0c0157f851"`, + `"0x2c3a8b2ad29dd4000cb827e973737bcf57fc072aeaf93ceeef4b4b9eb086cf67"`, ); }); }); diff --git a/yarn-project/circuits.js/src/contract/contract_class_id.ts b/yarn-project/circuits.js/src/contract/contract_class_id.ts index ef401fe5691..181b9a70dca 100644 --- a/yarn-project/circuits.js/src/contract/contract_class_id.ts +++ b/yarn-project/circuits.js/src/contract/contract_class_id.ts @@ -66,5 +66,5 @@ export function computePublicBytecodeCommitment(packedBytecode: Buffer) { const bytecodeLength = Math.ceil(encodedBytecode[0].toNumber() / (Fr.SIZE_IN_BYTES - 1)); assert(bytecodeLength < MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, 'Bytecode exceeds maximum deployable size'); - return bytecodeLength == 0 ? new Fr(0) : poseidon2HashAccumulate(encodedBytecode.slice(1, bytecodeLength + 1)); + return bytecodeLength == 0 ? new Fr(0) : poseidon2HashAccumulate(encodedBytecode.slice(0, bytecodeLength + 1)); } diff --git a/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap b/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap index af7128a8549..fea366471cd 100644 --- a/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap +++ b/yarn-project/noir-protocol-circuits-types/src/__snapshots__/noir_test_gen.test.ts.snap @@ -4,11 +4,11 @@ exports[`Data generation for noir tests Computes contract info for defaultContra "{ contract_address_salt: 0x000000000000000000000000000000000000000000000000000000000000ddd5, artifact_hash: 0x0000000000000000000000000000000000000000000000000000000000003039, - public_bytecode_commitment: 0x11fb08be2c55e530172e671f647b16dddf9b66acf42489d0236785d8f7bc8b48, + public_bytecode_commitment: 0x2e018d154e7faf1ddf8400a4b3605574e69afc04425f3e662e054dc094fb0428, private_functions_root: 0x25d76df45434ec75a83321daf941cfc667ff3a9027942e17105da4f50d1d13f9, - address: AztecAddress { inner: 0x29bc2e90ff6ec5f4a7c7f502e368af01eb74131a2eec6320e0e45419cddc7b6d }, - partial_address: PartialAddress { inner: 0x1a68423cf4f04eaede2b0e93131916b8b7330dae6e8ee202679d12a4eb49cc0b }, - contract_class_id: ContractClassId { inner: 0x1195b865ef122d75c8c4d6102d536193b69bbb712c85bafcbf7694f52e2d8c36 }, + address: AztecAddress { inner: 0x0d4379385723a3f96a0d9d59be7dc869e042a9f5b832749039a993e5ee6b91b8 }, + partial_address: PartialAddress { inner: 0x0b419abfd8ca509592b671ed9808138dced1df38835cbf3b3b693ad71623c90d }, + contract_class_id: ContractClassId { inner: 0x23b90310794d8e4ee1f13c0b97b4f5d11ddfa1642ad60e46169c3d3b2c6e03a6 }, public_keys: PublicKeys { inner: 0x01498945581e0eb9f8427ad6021184c700ef091d570892c437d12c7d90364bbd170ae506787c5c43d6ca9255d571c10fa9ffa9d141666e290c347c5c9ab7e34400c044b05b6ca83b9c2dbae79cc1135155956a64e136819136e9947fe5e5866c1c1f0ca244c7cd46b682552bff8ae77dea40b966a71de076ec3b7678f2bdb1511b00316144359e9a3ec8e49c1cdb7eeb0cedd190dfd9dc90eea5115aa779e287080ffc74d7a8b0bccb88ac11f45874172f3847eb8b92654aaa58a3d2b8dc7833019c111f36ad3fc1d9b7a7a14344314d2864b94f030594cd67f753ef774a1efb2039907fe37f08d10739255141bb066c506a12f7d1e8dfec21abc58494705b6f }, salted_initialization_hash: SaltedInitializationHash { inner: 0x13a939daa511233e5446905ed2cadbee14948fa75df183b53b5c14b612bffe88 }, deployer: AztecAddress { inner: 0x0000000000000000000000000000000000000000000000000000000000000000 } @@ -19,11 +19,11 @@ exports[`Data generation for noir tests Computes contract info for parentContrac "{ contract_address_salt: 0x0000000000000000000000000000000000000000000000000000000000001618, artifact_hash: 0x00000000000000000000000000000000000000000000000000000000000004bc, - public_bytecode_commitment: 0x1c5009dd5e8baedc8dfb2e801c9f97361b1ae152ff52f82104119c476b369bef, + public_bytecode_commitment: 0x258ed5ce21c3a9c15a1c6108ef0e759bb0617f2b505b8d64e927027d30e71791, private_functions_root: 0x1228b39ba6702af03e595300e8484c6373f00790d0148cc3d4ff0fd1c778a83a, - address: AztecAddress { inner: 0x2749b685f752f6dfe1d4e532fc036839004926b7c18abf1a4f69ddf97d62f40e }, - partial_address: PartialAddress { inner: 0x1c30ee02dcd41bcdfc5191dc36ccaae15cdc7e1fc6bd8a0cbe1baeaf1335a771 }, - contract_class_id: ContractClassId { inner: 0x24f1b8df215c10ee7edd213b439c8f8e99198a802a3e1e41597b6554b17049a3 }, + address: AztecAddress { inner: 0x0907b0edaac863b283132ee3a4781395ace64ff8cec244f1c5235e88c320cca1 }, + partial_address: PartialAddress { inner: 0x00906eed7cc361cc78056365c5ddbb94fd8fa05c5fed118bd4033585914cda92 }, + contract_class_id: ContractClassId { inner: 0x1ffddcc4c6d113ae695384e3bae42cb0c4c6d8ba7edbb80c8c23848b55cd37f3 }, public_keys: PublicKeys { inner: 0x01498945581e0eb9f8427ad6021184c700ef091d570892c437d12c7d90364bbd170ae506787c5c43d6ca9255d571c10fa9ffa9d141666e290c347c5c9ab7e34400c044b05b6ca83b9c2dbae79cc1135155956a64e136819136e9947fe5e5866c1c1f0ca244c7cd46b682552bff8ae77dea40b966a71de076ec3b7678f2bdb1511b00316144359e9a3ec8e49c1cdb7eeb0cedd190dfd9dc90eea5115aa779e287080ffc74d7a8b0bccb88ac11f45874172f3847eb8b92654aaa58a3d2b8dc7833019c111f36ad3fc1d9b7a7a14344314d2864b94f030594cd67f753ef774a1efb2039907fe37f08d10739255141bb066c506a12f7d1e8dfec21abc58494705b6f }, salted_initialization_hash: SaltedInitializationHash { inner: 0x24bd6ac7a182e2cf25e437c72f53544ef81dfd97d9afee23abb07a638e7be749 }, deployer: AztecAddress { inner: 0x0000000000000000000000000000000000000000000000000000000000000000 } diff --git a/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.test.ts b/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.test.ts index 9a4bf4b0a1f..ee02a62a3b6 100644 --- a/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.test.ts +++ b/yarn-project/protocol-contracts/src/class-registerer/contract_class_registered_event.test.ts @@ -15,7 +15,7 @@ describe('ContractClassRegisteredEvent', () => { expect(event.artifactHash.toString()).toEqual('0x072dce903b1a299d6820eeed695480fe9ec46658b1101885816aed6dd86037f0'); expect(event.packedPublicBytecode.length).toEqual(27090); expect(computePublicBytecodeCommitment(event.packedPublicBytecode).toString()).toEqual( - '0x0378491b34825cd67d1e13e140bbc80f2cd3a9b52171ea577f8f11620d4198ba', + '0x1d7d509f736d09975b88d01b5779a6f52e70905ba9294776d4881e811e6c1e9f', ); }); });