diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index cc47c148175..2b4c656d8dd 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -15,8 +15,10 @@ #include #ifndef DISABLE_AZTEC_VM +#include "barretenberg/vm/avm/generated/flavor.hpp" #include "barretenberg/vm/avm/trace/common.hpp" #include "barretenberg/vm/avm/trace/execution.hpp" +#include "barretenberg/vm/aztec_constants.hpp" #include "barretenberg/vm/stats.hpp" #endif #include "config.hpp" @@ -1019,15 +1021,19 @@ bool avm_verify(const std::filesystem::path& proof_path, const std::filesystem:: vinfo("circuit size: ", circuit_size); vinfo("num of pub inputs: ", num_public_inputs); - // Each commitment (precomputed entity) is represented as 2 Fq field elements. - // Each Fq element is split into two limbs of Fr elements. - // We therefore need 2 (circuit_size, num_public_inputs) + 4 * NUM_PRECOMPUTED_ENTITIES fr elements. - ASSERT(vk_as_fields.size() == 4 * AvmFlavor::NUM_PRECOMPUTED_ENTITIES + 2); + if (vk_as_fields.size() != AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS) { + info("The supplied avm vk has incorrect size. Number of fields: ", + vk_as_fields.size(), + " but expected: ", + AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS); + return false; + } std::array precomputed_cmts; for (size_t i = 0; i < AvmFlavor::NUM_PRECOMPUTED_ENTITIES; i++) { - // Start at offset 2 and adds 4 fr elements per commitment. Therefore, index = 4 * i + 2. - precomputed_cmts[i] = field_conversion::convert_from_bn254_frs(vk_span.subspan(4 * i + 2, 4)); + // Start at offset 2 and adds 4 (NUM_FRS_COM) fr elements per commitment. Therefore, index = 4 * i + 2. + precomputed_cmts[i] = field_conversion::convert_from_bn254_frs( + vk_span.subspan(AvmFlavor::NUM_FRS_COM * i + 2, AvmFlavor::NUM_FRS_COM)); } auto vk = AvmFlavor::VerificationKey(circuit_size, num_public_inputs, precomputed_cmts); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp index 069acda9c57..68dcd61424b 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/avm_recursion_constraint.cpp @@ -1,12 +1,14 @@ #ifndef DISABLE_AZTEC_VM #include "avm_recursion_constraint.hpp" +#include "barretenberg/constants.hpp" #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp" #include "barretenberg/stdlib/primitives/curves/bn254.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" #include "barretenberg/vm/avm/recursion/avm_recursive_flavor.hpp" #include "barretenberg/vm/avm/recursion/avm_recursive_verifier.hpp" +#include "barretenberg/vm/aztec_constants.hpp" #include "proof_surgeon.hpp" #include @@ -38,21 +40,19 @@ void create_dummy_vkey_and_proof(Builder& builder, { using Flavor = AvmFlavor; - size_t num_frs_comm = bb::field_conversion::calc_num_bn254_frs(); - size_t num_frs_fr = bb::field_conversion::calc_num_bn254_frs(); - // Relevant source for proof layout: AvmFlavor::Transcript::serialize_full_transcript() - assert((proof_size - Flavor::NUM_WITNESS_ENTITIES * num_frs_comm - Flavor::NUM_ALL_ENTITIES * num_frs_fr - - 2 * num_frs_comm) % - (num_frs_comm + num_frs_fr * Flavor::BATCHED_RELATION_PARTIAL_LENGTH) == + assert((proof_size - Flavor::NUM_WITNESS_ENTITIES * Flavor::NUM_FRS_COM - + Flavor::NUM_ALL_ENTITIES * Flavor::NUM_FRS_FR - 2 * Flavor::NUM_FRS_COM - Flavor::NUM_FRS_FR) % + (Flavor::NUM_FRS_COM + Flavor::NUM_FRS_FR * Flavor::BATCHED_RELATION_PARTIAL_LENGTH) == 0); // Derivation of circuit size based on the proof // Here, we should always get CONST_PROOF_SIZE_LOG_N which is not what is // usually set for the AVM proof. As it is a dummy key/proof, it should not matter. - auto log_circuit_size = (proof_size - Flavor::NUM_WITNESS_ENTITIES * num_frs_comm - - Flavor::NUM_ALL_ENTITIES * num_frs_fr - 2 * num_frs_comm) / - (num_frs_comm + num_frs_fr * Flavor::BATCHED_RELATION_PARTIAL_LENGTH); + auto log_circuit_size = + (proof_size - Flavor::NUM_WITNESS_ENTITIES * Flavor::NUM_FRS_COM - + Flavor::NUM_ALL_ENTITIES * Flavor::NUM_FRS_FR - 2 * Flavor::NUM_FRS_COM - Flavor::NUM_FRS_FR) / + (Flavor::NUM_FRS_COM + Flavor::NUM_FRS_FR * Flavor::BATCHED_RELATION_PARTIAL_LENGTH); /*************************************************************************** * Construct Dummy Verification Key diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index e53937bc8be..0c243e6f357 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -8,6 +8,8 @@ namespace acir_format { // Used to specify the type of recursive verifier via the proof_type specified by the RecursiveAggregation opcode from // ACIR +// Keep this enum values in sync with their noir counterpart constants defined in +// noir-protocol-circuits/crates/types/src/constants.nr enum PROOF_TYPE { PLONK, HONK, OINK, PG, AVM }; using namespace bb::plonk; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/generated/composer.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/generated/composer.cpp index 45ffe4c9eea..dfa4af8ba04 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/generated/composer.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/generated/composer.cpp @@ -45,8 +45,10 @@ std::shared_ptr AvmComposer::compute_proving_key(CircuitCons } // Initialize proving_key - const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(); - proving_key = std::make_shared(subgroup_size, 0); + { + const size_t subgroup_size = circuit_constructor.get_circuit_subgroup_size(); + proving_key = std::make_shared(subgroup_size, 0); + } return proving_key; } diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp index 7d3cabeeca8..20ba2570207 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp @@ -13,6 +13,7 @@ #include "barretenberg/transcript/transcript.hpp" #include "barretenberg/vm/avm/generated/flavor_settings.hpp" +#include "barretenberg/vm/aztec_constants.hpp" // Relations #include "barretenberg/vm/avm/generated/relations/alu.hpp" @@ -223,6 +224,25 @@ class AvmFlavor { static constexpr bool has_zero_row = true; + static constexpr size_t NUM_FRS_COM = field_conversion::calc_num_bn254_frs(); + static constexpr size_t NUM_FRS_FR = field_conversion::calc_num_bn254_frs(); + + static_assert(AVM_PROOF_LENGTH_IN_FIELDS == + (NUM_WITNESS_ENTITIES + 2) * NUM_FRS_COM + (NUM_ALL_ENTITIES + 1) * NUM_FRS_FR + + CONST_PROOF_SIZE_LOG_N * (NUM_FRS_COM + NUM_FRS_FR * BATCHED_RELATION_PARTIAL_LENGTH), + "\nUnexpected AVM proof length. This might be due to some changes in the\n" + "AVM circuit layout. In this case, modify AVM_PROOF_LENGTH_IN_FIELDS \n" + "in constants.nr accordingly."); + + // VK is composed of + // - circuit size encoded as a fr field element + // - num of inputs encoded as a fr field element + // - NUM_PRECOMPUTED_ENTITIES commitments + static_assert(AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS == 2 * NUM_FRS_FR + NUM_PRECOMPUTED_ENTITIES * NUM_FRS_COM, + "\nUnexpected AVM VK length. This might be due to some changes in the\n" + "AVM circuit. In this case, modify AVM_VERIFICATION_LENGTH_IN_FIELDS \n" + "in constants.nr accordingly."); + template class PrecomputedEntities : public PrecomputedEntitiesBase { public: using DataType = DataType_; diff --git a/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp b/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp index 68c5ce65516..b9f8d958d07 100644 --- a/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp @@ -34,6 +34,7 @@ #define PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH 691 #define PUBLIC_CONTEXT_INPUTS_LENGTH 42 #define AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS 66 +#define AVM_PROOF_LENGTH_IN_FIELDS 3802 #define SENDER_SELECTOR 0 #define ADDRESS_SELECTOR 1 #define STORAGE_ADDRESS_SELECTOR 1 diff --git a/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs b/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs index 0a9830d51c5..69d22c1dcc3 100644 --- a/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs +++ b/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs @@ -13,6 +13,7 @@ #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/vm/{{snakeCase name}}/generated/flavor_settings.hpp" +#include "barretenberg/vm/aztec_constants.hpp" // Relations {{#each relation_file_names as |r|}} @@ -102,6 +103,25 @@ class {{name}}Flavor { static constexpr bool has_zero_row = true; + static constexpr size_t NUM_FRS_COM = field_conversion::calc_num_bn254_frs(); + static constexpr size_t NUM_FRS_FR = field_conversion::calc_num_bn254_frs(); + + static_assert(AVM_PROOF_LENGTH_IN_FIELDS == + (NUM_WITNESS_ENTITIES + 2) * NUM_FRS_COM + (NUM_ALL_ENTITIES + 1) * NUM_FRS_FR + + CONST_PROOF_SIZE_LOG_N * (NUM_FRS_COM + NUM_FRS_FR * BATCHED_RELATION_PARTIAL_LENGTH), + "\nUnexpected AVM proof length. This might be due to some changes in the\n" + "AVM circuit layout. In this case, modify AVM_PROOF_LENGTH_IN_FIELDS \n" + "in constants.nr accordingly."); + + // VK is composed of + // - circuit size encoded as a fr field element + // - num of inputs encoded as a fr field element + // - NUM_PRECOMPUTED_ENTITIES commitments + static_assert(AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS == 2 * NUM_FRS_FR + NUM_PRECOMPUTED_ENTITIES * NUM_FRS_COM, + "\nUnexpected AVM VK length. This might be due to some changes in the\n" + "AVM circuit. In this case, modify AVM_VERIFICATION_LENGTH_IN_FIELDS \n" + "in constants.nr accordingly."); + template class PrecomputedEntities : public PrecomputedEntitiesBase { public: using DataType = DataType_; diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 1e1d3606c5c..56a6996c563 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -246,4 +246,9 @@ library Constants { uint256 internal constant START_EMIT_NULLIFIER_WRITE_OFFSET = 144; uint256 internal constant START_EMIT_L2_TO_L1_MSG_WRITE_OFFSET = 160; uint256 internal constant START_EMIT_UNENCRYPTED_LOG_WRITE_OFFSET = 162; + uint256 internal constant PROOF_TYPE_PLONK = 0; + uint256 internal constant PROOF_TYPE_HONK = 1; + uint256 internal constant PROOF_TYPE_OINK = 2; + uint256 internal constant PROOF_TYPE_PG = 3; + uint256 internal constant PROOF_TYPE_AVM = 4; } diff --git a/noir-projects/mock-protocol-circuits/Nargo.toml b/noir-projects/mock-protocol-circuits/Nargo.toml index 2c08f6cb1d8..32eb55ee14b 100644 --- a/noir-projects/mock-protocol-circuits/Nargo.toml +++ b/noir-projects/mock-protocol-circuits/Nargo.toml @@ -7,4 +7,5 @@ members = [ "crates/mock-private-kernel-inner", "crates/mock-private-kernel-reset", "crates/mock-private-kernel-tail", + "crates/mock-public-kernel", ] diff --git a/noir-projects/mock-protocol-circuits/Readme.md b/noir-projects/mock-protocol-circuits/Readme.md index 75c8081e821..f4fa6e1968c 100644 --- a/noir-projects/mock-protocol-circuits/Readme.md +++ b/noir-projects/mock-protocol-circuits/Readme.md @@ -1,3 +1,7 @@ # Mocked protocol circuits These simplified circuits act as a way of testing the IVC integration between noir and barretenberg. They follow the same IVC scheme as the real circuits, but are much simpler and easier to reason about. + +# Public Kernel + +The folder public-kernel is currently used to test the noir integration of the avm recursive verifier. diff --git a/noir-projects/mock-protocol-circuits/crates/mock-public-kernel/Nargo.toml b/noir-projects/mock-protocol-circuits/crates/mock-public-kernel/Nargo.toml new file mode 100644 index 00000000000..26644c8f295 --- /dev/null +++ b/noir-projects/mock-protocol-circuits/crates/mock-public-kernel/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "mock_public_kernel" +type = "bin" +authors = [""] +compiler_version = ">=0.32.0" + +[dependencies] +types = { path = "../../../noir-protocol-circuits/crates/types" } \ No newline at end of file diff --git a/noir-projects/mock-protocol-circuits/crates/mock-public-kernel/src/main.nr b/noir-projects/mock-protocol-circuits/crates/mock-public-kernel/src/main.nr new file mode 100644 index 00000000000..b66b27da567 --- /dev/null +++ b/noir-projects/mock-protocol-circuits/crates/mock-public-kernel/src/main.nr @@ -0,0 +1,9 @@ +use dep::types::constants::{AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, AVM_PROOF_LENGTH_IN_FIELDS, PROOF_TYPE_AVM}; + +fn main( + verification_key: [Field; AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS], + proof: [Field; AVM_PROOF_LENGTH_IN_FIELDS] +) -> pub u8 { + std::verify_proof_with_type(verification_key, proof, [], 0, PROOF_TYPE_AVM); + 1 // Dummy value to return for the mock kernel as void function creates some pain. +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index d84713c9e86..3749ac376c5 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -289,13 +289,10 @@ global NESTED_RECURSIVE_PROOF_LENGTH = 439; global TUBE_PROOF_LENGTH = RECURSIVE_PROOF_LENGTH; // in the future these can differ global VERIFICATION_KEY_LENGTH_IN_FIELDS = 128; -// VK is composed of -// - circuit size encoded as a fr field element (32 bytes) -// - num of inputs encoded as a fr field element (32 bytes) -// - 16 affine elements (curve base field fq) encoded as fr elements takes (16 * 4 * 32 bytes) -// 16 above refers to the constant AvmFlavor::NUM_PRECOMPUTED_ENTITIES -global AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS = 2 + 16 * 4; +// The length is 2 + AvmFlavor::NUM_PRECOMPUTED_ENTITIES * 4 +global AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS = 2 + 16 * 4; +global AVM_PROOF_LENGTH_IN_FIELDS = 3802; /** * Enumerate the hash_indices which are used for pedersen hashing. * We start from 1 to avoid the default generators. The generator indices are listed @@ -530,3 +527,11 @@ global AVM_PEDERSENCOMMITMENT_DYN_L2_GAS: u16 = 0; global AVM_TORADIXLE_DYN_L2_GAS: u16 = 200; global AVM_SHA256COMPRESSION_DYN_L2_GAS: u16 = 0; global AVM_KECCAKF1600_DYN_L2_GAS: u16 = 0; + +// Constants related to proof type of a recursive proof verification. +// Keep following constants in sync with the enum acir_format::PROOF_TYPE in recursion_constraint.hpp +global PROOF_TYPE_PLONK : u32 = 0; +global PROOF_TYPE_HONK : u32 = 1; +global PROOF_TYPE_OINK : u32 = 2; +global PROOF_TYPE_PG : u32 = 3; +global PROOF_TYPE_AVM : u32 = 4; diff --git a/yarn-project/Earthfile b/yarn-project/Earthfile index ec298067247..17600ddb1c0 100644 --- a/yarn-project/Earthfile +++ b/yarn-project/Earthfile @@ -123,7 +123,7 @@ rollup-verifier-contract: txe: FROM +cli-base - RUN yarn workspaces focus @aztec/txe --production && yarn cache clean + RUN yarn workspaces focus @aztec/txe && yarn cache clean ENTRYPOINT ["node", "--no-warnings", "/usr/src/yarn-project/txe/dest/bin/index.js"] SAVE ARTIFACT /usr/src /usr/src diff --git a/yarn-project/bb-prover/package.json b/yarn-project/bb-prover/package.json index 3fc9ae17d27..7adf7f8a7eb 100644 --- a/yarn-project/bb-prover/package.json +++ b/yarn-project/bb-prover/package.json @@ -74,6 +74,7 @@ }, "devDependencies": { "@aztec/ethereum": "workspace:^", + "@aztec/types": "workspace:^", "@jest/globals": "^29.5.0", "@types/jest": "^29.5.0", "@types/memdown": "^3.0.0", diff --git a/yarn-project/bb-prover/src/avm_proving.test.ts b/yarn-project/bb-prover/src/avm_proving.test.ts index ebf5af81ea5..e23a0840211 100644 --- a/yarn-project/bb-prover/src/avm_proving.test.ts +++ b/yarn-project/bb-prover/src/avm_proving.test.ts @@ -1,61 +1,15 @@ -import { - AvmCircuitInputs, - AvmVerificationKeyData, - AztecAddress, - ContractStorageRead, - ContractStorageUpdateRequest, - FunctionSelector, - Gas, - GlobalVariables, - Header, - L2ToL1Message, - LogHash, - MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL, - MAX_L2_TO_L1_MSGS_PER_CALL, - MAX_NOTE_HASHES_PER_CALL, - MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, - MAX_NULLIFIERS_PER_CALL, - MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_READ_REQUESTS_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_DATA_READS_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, - MAX_UNENCRYPTED_LOGS_PER_CALL, - NoteHash, - Nullifier, - PublicCallRequest, - PublicCircuitPublicInputs, - ReadRequest, - RevertCode, - TreeLeafReadRequest, -} from '@aztec/circuits.js'; -import { computeVarArgsHash } from '@aztec/circuits.js/hash'; -import { padArrayEnd } from '@aztec/foundation/collection'; +import { GlobalVariables } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; -import { createDebugLogger } from '@aztec/foundation/log'; -import { AvmSimulator, type PublicContractsDB, type PublicExecutionResult, type PublicStateDB } from '@aztec/simulator'; -import { - getAvmTestContractBytecode, - initContext, - initExecutionEnvironment, - initHostStorage, - initPersistableStateManager, - resolveAvmTestContractAssertionMessage, -} from '@aztec/simulator/avm/fixtures'; import { jest } from '@jest/globals'; import { mock } from 'jest-mock-extended'; -import fs from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import path from 'path'; -import { PublicSideEffectTrace } from '../../simulator/src/public/side_effect_trace.js'; -import { SerializableContractInstance } from '../../types/src/contracts/contract_instance.js'; -import { type BBSuccess, BB_RESULT, generateAvmProof, verifyAvmProof } from './bb/execute.js'; -import { extractAvmVkData } from './verification_key/verification_key_data.js'; +import { proveAndVerifyAvmTestContract } from '../src/test/test_avm.js'; const TIMEOUT = 60_000; const TIMESTAMP = new Fr(99833); +const GLOBAL_VARIABLES = GlobalVariables.empty(); +GLOBAL_VARIABLES.timestamp = TIMESTAMP; describe('AVM WitGen, proof generation and verification', () => { const avmFunctionsAndCalldata: [string, Fr[]][] = [ @@ -79,7 +33,7 @@ describe('AVM WitGen, proof generation and verification', () => { it.each(avmFunctionsAndCalldata)( 'Should prove %s', async (name, calldata) => { - await proveAndVerifyAvmTestContract(name, calldata); + await proveAndVerifyAvmTestContract(jest, mock, name, calldata); }, TIMEOUT, ); @@ -125,7 +79,7 @@ describe('AVM WitGen, proof generation and verification', () => { it.each(avmHashFunctions)( 'Should prove %s', async (name, calldata) => { - await proveAndVerifyAvmTestContract(name, calldata); + await proveAndVerifyAvmTestContract(jest, mock, name, calldata); }, TIMEOUT * 2, // We need more for keccak for now ); @@ -134,7 +88,7 @@ describe('AVM WitGen, proof generation and verification', () => { it( 'Should prove that timestamp matches', async () => { - await proveAndVerifyAvmTestContract('assert_timestamp', [TIMESTAMP]); + await proveAndVerifyAvmTestContract(jest, mock, 'assert_timestamp', [TIMESTAMP], undefined, GLOBAL_VARIABLES); }, TIMEOUT, ); @@ -143,7 +97,14 @@ describe('AVM WitGen, proof generation and verification', () => { 'Should prove that mutated timestamp does not match and a revert is performed', async () => { // The error assertion string must match with that of assert_timestamp noir function. - await proveAndVerifyAvmTestContract('assert_timestamp', [TIMESTAMP.add(new Fr(1))], 'timestamp does not match'); + await proveAndVerifyAvmTestContract( + jest, + mock, + 'assert_timestamp', + [TIMESTAMP.add(new Fr(1))], + 'timestamp does not match', + GLOBAL_VARIABLES, + ); }, TIMEOUT, ); @@ -160,7 +121,7 @@ describe('AVM WitGen, proof generation and verification', () => { it.each(avmEmbeddedCurveFunctions)( 'Should prove %s', async (name, calldata) => { - await proveAndVerifyAvmTestContract(name, calldata); + await proveAndVerifyAvmTestContract(jest, mock, name, calldata); }, TIMEOUT, ); @@ -189,161 +150,9 @@ describe('AVM WitGen, proof generation and verification', () => { it.each(avmContextFunctions)( 'Should prove %s', async contextFunction => { - await proveAndVerifyAvmTestContract(contextFunction); + await proveAndVerifyAvmTestContract(jest, mock, contextFunction); }, TIMEOUT, ); }); }); - -/************************************************************************ - * Helpers - ************************************************************************/ - -/** - * If assertionErrString is set, we expect a (non exceptional halting) revert due to a failing assertion and - * we check that the revert reason error contains this string. However, the circuit must correctly prove the - * execution. - */ -const proveAndVerifyAvmTestContract = async ( - functionName: string, - calldata: Fr[] = [], - assertionErrString?: string, -) => { - const startSideEffectCounter = 0; - const functionSelector = FunctionSelector.random(); - const globals = GlobalVariables.empty(); - globals.timestamp = TIMESTAMP; - const environment = initExecutionEnvironment({ functionSelector, calldata, globals }); - - const contractsDb = mock(); - const contractInstance = new SerializableContractInstance({ - version: 1, - salt: new Fr(0x123), - deployer: new Fr(0x456), - contractClassId: new Fr(0x789), - initializationHash: new Fr(0x101112), - publicKeysHash: new Fr(0x161718), - }).withAddress(environment.address); - contractsDb.getContractInstance.mockResolvedValue(Promise.resolve(contractInstance)); - - const storageDb = mock(); - const storageValue = new Fr(5); - storageDb.storageRead.mockResolvedValue(Promise.resolve(storageValue)); - - const hostStorage = initHostStorage({ contractsDb }); - const trace = new PublicSideEffectTrace(startSideEffectCounter); - const persistableState = initPersistableStateManager({ hostStorage, trace }); - const context = initContext({ env: environment, persistableState }); - const nestedCallBytecode = getAvmTestContractBytecode('add_args_return'); - jest.spyOn(hostStorage.contractsDb, 'getBytecode').mockResolvedValue(nestedCallBytecode); - - const startGas = new Gas(context.machineState.gasLeft.daGas, context.machineState.gasLeft.l2Gas); - - const internalLogger = createDebugLogger('aztec:avm-proving-test'); - const logger = (msg: string, _data?: any) => internalLogger.verbose(msg); - - // Use a simple contract that emits a side effect - const bytecode = getAvmTestContractBytecode(functionName); - // The paths for the barretenberg binary and the write path are hardcoded for now. - const bbPath = path.resolve('../../barretenberg/cpp/build/bin/bb'); - const bbWorkingDirectory = await fs.mkdtemp(path.join(tmpdir(), 'bb-')); - - // First we simulate (though it's not needed in this simple case). - const simulator = new AvmSimulator(context); - const avmResult = await simulator.executeBytecode(bytecode); - - if (assertionErrString == undefined) { - expect(avmResult.reverted).toBe(false); - } else { - // Explicit revert when an assertion failed. - expect(avmResult.reverted).toBe(true); - expect(avmResult.revertReason).toBeDefined(); - expect(resolveAvmTestContractAssertionMessage(functionName, avmResult.revertReason!)).toContain(assertionErrString); - } - - const pxResult = trace.toPublicExecutionResult( - environment, - startGas, - /*endGasLeft=*/ Gas.from(context.machineState.gasLeft), - /*bytecode=*/ simulator.getBytecode()!, - avmResult, - functionName, - ); - - const avmCircuitInputs = new AvmCircuitInputs( - functionName, - /*bytecode=*/ simulator.getBytecode()!, // uncompressed bytecode - /*calldata=*/ context.environment.calldata, - /*publicInputs=*/ getPublicInputs(pxResult), - /*avmHints=*/ pxResult.avmCircuitHints, - ); - - // Then we prove. - const proofRes = await generateAvmProof(bbPath, bbWorkingDirectory, avmCircuitInputs, logger); - expect(proofRes.status).toEqual(BB_RESULT.SUCCESS); - - // Then we test VK extraction and serialization. - const succeededRes = proofRes as BBSuccess; - const vkData = await extractAvmVkData(succeededRes.vkPath!); - AvmVerificationKeyData.fromBuffer(vkData.toBuffer()); - - // Then we verify. - const rawVkPath = path.join(succeededRes.vkPath!, 'vk'); - const verificationRes = await verifyAvmProof(bbPath, succeededRes.proofPath!, rawVkPath, logger); - expect(verificationRes.status).toBe(BB_RESULT.SUCCESS); -}; - -// TODO: pub somewhere more usable - copied from abstract phase manager -const getPublicInputs = (result: PublicExecutionResult): PublicCircuitPublicInputs => { - return PublicCircuitPublicInputs.from({ - callContext: result.executionRequest.callContext, - proverAddress: AztecAddress.ZERO, - argsHash: computeVarArgsHash(result.executionRequest.args), - noteHashes: padArrayEnd(result.noteHashes, NoteHash.empty(), MAX_NOTE_HASHES_PER_CALL), - nullifiers: padArrayEnd(result.nullifiers, Nullifier.empty(), MAX_NULLIFIERS_PER_CALL), - l2ToL1Msgs: padArrayEnd(result.l2ToL1Messages, L2ToL1Message.empty(), MAX_L2_TO_L1_MSGS_PER_CALL), - startSideEffectCounter: result.startSideEffectCounter, - endSideEffectCounter: result.endSideEffectCounter, - returnsHash: computeVarArgsHash(result.returnValues), - noteHashReadRequests: padArrayEnd( - result.noteHashReadRequests, - TreeLeafReadRequest.empty(), - MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, - ), - nullifierReadRequests: padArrayEnd( - result.nullifierReadRequests, - ReadRequest.empty(), - MAX_NULLIFIER_READ_REQUESTS_PER_CALL, - ), - nullifierNonExistentReadRequests: padArrayEnd( - result.nullifierNonExistentReadRequests, - ReadRequest.empty(), - MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, - ), - l1ToL2MsgReadRequests: padArrayEnd( - result.l1ToL2MsgReadRequests, - TreeLeafReadRequest.empty(), - MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL, - ), - contractStorageReads: padArrayEnd( - result.contractStorageReads, - ContractStorageRead.empty(), - MAX_PUBLIC_DATA_READS_PER_CALL, - ), - contractStorageUpdateRequests: padArrayEnd( - result.contractStorageUpdateRequests, - ContractStorageUpdateRequest.empty(), - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, - ), - publicCallRequests: padArrayEnd([], PublicCallRequest.empty(), MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL), - unencryptedLogsHashes: padArrayEnd(result.unencryptedLogsHashes, LogHash.empty(), MAX_UNENCRYPTED_LOGS_PER_CALL), - historicalHeader: Header.empty(), - globalVariables: GlobalVariables.empty(), - startGasLeft: Gas.from(result.startGasLeft), - endGasLeft: Gas.from(result.endGasLeft), - transactionFee: result.transactionFee, - // TODO(@just-mitch): need better mapping from simulator to revert code. - revertCode: result.reverted ? RevertCode.APP_LOGIC_REVERTED : RevertCode.OK, - }); -}; diff --git a/yarn-project/bb-prover/src/test/index.ts b/yarn-project/bb-prover/src/test/index.ts index 3f84ad27da1..555536e8cb7 100644 --- a/yarn-project/bb-prover/src/test/index.ts +++ b/yarn-project/bb-prover/src/test/index.ts @@ -1,2 +1,3 @@ export * from './test_circuit_prover.js'; export * from './test_verifier.js'; +export * from './test_avm.js'; diff --git a/yarn-project/bb-prover/src/test/test_avm.ts b/yarn-project/bb-prover/src/test/test_avm.ts new file mode 100644 index 00000000000..d9f522bf711 --- /dev/null +++ b/yarn-project/bb-prover/src/test/test_avm.ts @@ -0,0 +1,229 @@ +import { + AvmCircuitInputs, + AvmVerificationKeyData, + AztecAddress, + ContractStorageRead, + ContractStorageUpdateRequest, + FunctionSelector, + Gas, + GlobalVariables, + Header, + L2ToL1Message, + LogHash, + MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL, + MAX_L2_TO_L1_MSGS_PER_CALL, + MAX_NOTE_HASHES_PER_CALL, + MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, + MAX_NULLIFIERS_PER_CALL, + MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_PUBLIC_DATA_READS_PER_CALL, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, + MAX_UNENCRYPTED_LOGS_PER_CALL, + NoteHash, + Nullifier, + PublicCallRequest, + PublicCircuitPublicInputs, + ReadRequest, + RevertCode, + TreeLeafReadRequest, +} from '@aztec/circuits.js'; +import { computeVarArgsHash } from '@aztec/circuits.js/hash'; +import { padArrayEnd } from '@aztec/foundation/collection'; +import { Fr } from '@aztec/foundation/fields'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { AvmSimulator, type PublicContractsDB, type PublicExecutionResult, type PublicStateDB } from '@aztec/simulator'; +import { PublicSideEffectTrace } from '@aztec/simulator'; +import { + getAvmTestContractBytecode, + initContext, + initExecutionEnvironment, + initHostStorage, + initPersistableStateManager, + resolveAvmTestContractAssertionMessage, +} from '@aztec/simulator/avm/fixtures'; +import { SerializableContractInstance } from '@aztec/types/contracts'; + +import fs from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import path from 'path'; + +import { type BBSuccess, BB_RESULT, generateAvmProof, verifyAvmProof } from '../bb/execute.js'; +import { extractAvmVkData } from '../verification_key/verification_key_data.js'; + +// The paths for the barretenberg binary and the write path are hardcoded for now. +const bbPath = path.resolve('../../barretenberg/cpp/build/bin/bb'); +const bbWorkingDirectory = await fs.mkdtemp(path.join(tmpdir(), 'bb-')); + +const internalLogger = createDebugLogger('aztec:avm-proving-test'); +const logger = (msg: string, _data?: any) => internalLogger.verbose(msg); + +// TODO: pub somewhere more usable - copied from abstract phase manager +const getPublicInputs = (result: PublicExecutionResult): PublicCircuitPublicInputs => { + return PublicCircuitPublicInputs.from({ + callContext: result.executionRequest.callContext, + proverAddress: AztecAddress.ZERO, + argsHash: computeVarArgsHash(result.executionRequest.args), + noteHashes: padArrayEnd(result.noteHashes, NoteHash.empty(), MAX_NOTE_HASHES_PER_CALL), + nullifiers: padArrayEnd(result.nullifiers, Nullifier.empty(), MAX_NULLIFIERS_PER_CALL), + l2ToL1Msgs: padArrayEnd(result.l2ToL1Messages, L2ToL1Message.empty(), MAX_L2_TO_L1_MSGS_PER_CALL), + startSideEffectCounter: result.startSideEffectCounter, + endSideEffectCounter: result.endSideEffectCounter, + returnsHash: computeVarArgsHash(result.returnValues), + noteHashReadRequests: padArrayEnd( + result.noteHashReadRequests, + TreeLeafReadRequest.empty(), + MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, + ), + nullifierReadRequests: padArrayEnd( + result.nullifierReadRequests, + ReadRequest.empty(), + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, + ), + nullifierNonExistentReadRequests: padArrayEnd( + result.nullifierNonExistentReadRequests, + ReadRequest.empty(), + MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, + ), + l1ToL2MsgReadRequests: padArrayEnd( + result.l1ToL2MsgReadRequests, + TreeLeafReadRequest.empty(), + MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL, + ), + contractStorageReads: padArrayEnd( + result.contractStorageReads, + ContractStorageRead.empty(), + MAX_PUBLIC_DATA_READS_PER_CALL, + ), + contractStorageUpdateRequests: padArrayEnd( + result.contractStorageUpdateRequests, + ContractStorageUpdateRequest.empty(), + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, + ), + publicCallRequests: padArrayEnd([], PublicCallRequest.empty(), MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL), + unencryptedLogsHashes: padArrayEnd(result.unencryptedLogsHashes, LogHash.empty(), MAX_UNENCRYPTED_LOGS_PER_CALL), + historicalHeader: Header.empty(), + globalVariables: GlobalVariables.empty(), + startGasLeft: Gas.from(result.startGasLeft), + endGasLeft: Gas.from(result.endGasLeft), + transactionFee: result.transactionFee, + // TODO(@just-mitch): need better mapping from simulator to revert code. + revertCode: result.reverted ? RevertCode.APP_LOGIC_REVERTED : RevertCode.OK, + }); +}; + +export async function proveAvmTestContract( + // NOTE(AD): these are hackishly passed instead of + // importing them as this module gets exported in our index.ts and jest is only a dev dependency/ + // In the future, a testing helper package that depends on jest might be a better solution + jest: any, + jestMock: any, + functionName: string, + calldata: Fr[] = [], + assertionErrString?: string, + globalVariables?: GlobalVariables, +): Promise { + const startSideEffectCounter = 0; + const functionSelector = FunctionSelector.random(); + const globals = globalVariables == undefined ? GlobalVariables.empty() : globalVariables; + const environment = initExecutionEnvironment({ functionSelector, calldata, globals }); + + const contractsDb = jestMock() as PublicContractsDB; + const contractInstance = new SerializableContractInstance({ + version: 1, + salt: new Fr(0x123), + deployer: new Fr(0x456), + contractClassId: new Fr(0x789), + initializationHash: new Fr(0x101112), + publicKeysHash: new Fr(0x161718), + }).withAddress(environment.address); + (contractsDb.getContractInstance as any).mockResolvedValue(Promise.resolve(contractInstance)); + + const storageDb = jestMock() as PublicStateDB; + const storageValue = new Fr(5); + (storageDb.storageRead as any).mockResolvedValue(Promise.resolve(storageValue)); + + const hostStorage = initHostStorage({ contractsDb }); + const trace = new PublicSideEffectTrace(startSideEffectCounter); + const persistableState = initPersistableStateManager({ hostStorage, trace }); + const context = initContext({ env: environment, persistableState }); + const nestedCallBytecode = getAvmTestContractBytecode('add_args_return'); + jest.spyOn(hostStorage.contractsDb, 'getBytecode').mockResolvedValue(nestedCallBytecode); + + const startGas = new Gas(context.machineState.gasLeft.daGas, context.machineState.gasLeft.l2Gas); + + // Use a simple contract that emits a side effect + const bytecode = getAvmTestContractBytecode(functionName); + + // First we simulate (though it's not needed in this simple case). + const simulator = new AvmSimulator(context); + const avmResult = await simulator.executeBytecode(bytecode); + + if (assertionErrString == undefined) { + expect(avmResult.reverted).toBe(false); + } else { + // Explicit revert when an assertion failed. + expect(avmResult.reverted).toBe(true); + expect(avmResult.revertReason).toBeDefined(); + expect(resolveAvmTestContractAssertionMessage(functionName, avmResult.revertReason!)).toContain(assertionErrString); + } + + const pxResult = trace.toPublicExecutionResult( + environment, + startGas, + /*endGasLeft=*/ Gas.from(context.machineState.gasLeft), + /*bytecode=*/ simulator.getBytecode()!, + avmResult, + functionName, + ); + + const avmCircuitInputs = new AvmCircuitInputs( + functionName, + /*bytecode=*/ simulator.getBytecode()!, // uncompressed bytecode + /*calldata=*/ context.environment.calldata, + /*publicInputs=*/ getPublicInputs(pxResult), + /*avmHints=*/ pxResult.avmCircuitHints, + ); + + // Then we prove. + const proofRes = await generateAvmProof(bbPath, bbWorkingDirectory, avmCircuitInputs, logger); + expect(proofRes.status).toEqual(BB_RESULT.SUCCESS); + + return proofRes as BBSuccess; +} + +/** + * If assertionErrString is set, we expect a (non exceptional halting) revert due to a failing assertion and + * we check that the revert reason error contains this string. However, the circuit must correctly prove the + * execution. + */ +export async function proveAndVerifyAvmTestContract( + // NOTE(AD): these are hackishly passed instead of + // importing them as this module gets exported in our index.ts and jest is only a dev dependency/ + // In the future, a testing helper package that depends on jest might be a better solution + jest: any, + jestMock: any, + functionName: string, + calldata: Fr[] = [], + assertionErrString?: string, + globalVariables?: GlobalVariables, +) { + const succeededRes = await proveAvmTestContract( + jest, + jestMock, + functionName, + calldata, + assertionErrString, + globalVariables, + ); + + // Then we test VK extraction and serialization. + const vkData = await extractAvmVkData(succeededRes.vkPath!); + AvmVerificationKeyData.fromBuffer(vkData.toBuffer()); + + // Then we verify. + const rawVkPath = path.join(succeededRes.vkPath!, 'vk'); + const verificationRes = await verifyAvmProof(bbPath, succeededRes.proofPath!, rawVkPath, logger); + expect(verificationRes.status).toBe(BB_RESULT.SUCCESS); +} diff --git a/yarn-project/bb-prover/tsconfig.json b/yarn-project/bb-prover/tsconfig.json index 77c9c6ff999..d3b184f3833 100644 --- a/yarn-project/bb-prover/tsconfig.json +++ b/yarn-project/bb-prover/tsconfig.json @@ -26,6 +26,9 @@ }, { "path": "../ethereum" + }, + { + "path": "../types" } ], "include": ["src"] diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 7d751f670a1..67282dd7aca 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -202,6 +202,7 @@ export const NESTED_RECURSIVE_PROOF_LENGTH = 439; export const TUBE_PROOF_LENGTH = 439; export const VERIFICATION_KEY_LENGTH_IN_FIELDS = 128; export const AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS = 66; +export const AVM_PROOF_LENGTH_IN_FIELDS = 3802; export const SENDER_SELECTOR = 0; export const ADDRESS_SELECTOR = 1; export const STORAGE_ADDRESS_SELECTOR = 1; @@ -355,6 +356,11 @@ export const AVM_PEDERSENCOMMITMENT_DYN_L2_GAS = 0; export const AVM_TORADIXLE_DYN_L2_GAS = 200; export const AVM_SHA256COMPRESSION_DYN_L2_GAS = 0; export const AVM_KECCAKF1600_DYN_L2_GAS = 0; +export const PROOF_TYPE_PLONK = 0; +export const PROOF_TYPE_HONK = 1; +export const PROOF_TYPE_OINK = 2; +export const PROOF_TYPE_PG = 3; +export const PROOF_TYPE_AVM = 4; export enum GeneratorIndex { NOTE_HASH = 1, NOTE_HASH_NONCE = 2, diff --git a/yarn-project/ivc-integration/package.json b/yarn-project/ivc-integration/package.json index 2976b6de2d8..2b12d2dceca 100644 --- a/yarn-project/ivc-integration/package.json +++ b/yarn-project/ivc-integration/package.json @@ -71,6 +71,7 @@ "@types/jest": "^29.5.0", "@types/node": "^18.7.23", "jest": "^29.5.0", + "jest-mock-extended": "^4.0.0-beta1", "levelup": "^5.1.1", "memdown": "^6.1.1", "ts-node": "^10.9.1", diff --git a/yarn-project/ivc-integration/src/avm_integration.test.ts b/yarn-project/ivc-integration/src/avm_integration.test.ts new file mode 100644 index 00000000000..f9439099d0d --- /dev/null +++ b/yarn-project/ivc-integration/src/avm_integration.test.ts @@ -0,0 +1,106 @@ +import { type BBSuccess, BB_RESULT, generateProof, verifyProof } from '@aztec/bb-prover'; +import { proveAvmTestContract } from '@aztec/bb-prover'; +import { + AVM_PROOF_LENGTH_IN_FIELDS, + AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, + PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH, +} from '@aztec/circuits.js/constants'; +import { Fr } from '@aztec/foundation/fields'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { BufferReader } from '@aztec/foundation/serialize'; +import { type FixedLengthArray } from '@aztec/noir-protocol-circuits-types/types'; + +import { jest } from '@jest/globals'; +import fs from 'fs/promises'; +import { mock } from 'jest-mock-extended'; +import os from 'os'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +import { MockPublicKernelCircuit, witnessGenMockPublicKernelCircuit } from './index.js'; + +// Auto-generated types from noir are not in camel case. +/* eslint-disable camelcase */ + +jest.setTimeout(120_000); + +describe('AVM Integration', () => { + const logger = createDebugLogger('aztec:avm-integration'); + + let bbWorkingDirectory: string; + let bbBinaryPath: string; + + beforeEach(async () => { + //Create a temp working dir + bbWorkingDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'bb-avm-integration-')); + bbBinaryPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../barretenberg/cpp/build/bin', 'bb'); + }); + + async function createHonkProof(witness: Uint8Array, bytecode: string): Promise { + const witnessFileName = path.join(bbWorkingDirectory, 'witnesses.gz'); + await fs.writeFile(witnessFileName, witness); + + const provingResult = await generateProof( + bbBinaryPath, + bbWorkingDirectory, + 'mock-public-kernel', + Buffer.from(bytecode, 'base64'), + witnessFileName, + 'ultra_honk', + logger.info, + ); + + expect(provingResult.status).toBe(BB_RESULT.SUCCESS); + return provingResult as BBSuccess; + } + + it('Should generate and verify an ultra honk proof from an AVM verification', async () => { + const bbSuccess = await proveAvmTestContract(jest, mock, 'new_note_hash', [new Fr(1)]); + + const avmProofPath = bbSuccess.proofPath; + const avmVkPath = bbSuccess.vkPath; + expect(avmProofPath).toBeDefined(); + expect(avmVkPath).toBeDefined(); + + // Read the binary proof + const avmProofBuffer = await fs.readFile(avmProofPath!); + const reader = BufferReader.asReader(avmProofBuffer); + reader.readArray(PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH, Fr); + const calldataSize = Fr.fromBuffer(reader).toNumber(); + reader.readArray(calldataSize, Fr); + const returnDataSize = Fr.fromBuffer(reader).toNumber(); + reader.readArray(returnDataSize, Fr); + + const proof: Fr[] = []; + while (!reader.isEmpty()) { + proof.push(Fr.fromBuffer(reader)); + } + expect(proof.length).toBe(AVM_PROOF_LENGTH_IN_FIELDS); + + // Read the key + const vkBuffer = await fs.readFile(path.join(avmVkPath!, 'vk')); + const vkReader = BufferReader.asReader(vkBuffer); + const vk = vkReader.readArray(AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, Fr); + expect(vk.length).toBe(AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS); + + const witGenResult = await witnessGenMockPublicKernelCircuit({ + verification_key: vk.map(x => x.toString()) as FixedLengthArray< + string, + typeof AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS + >, + proof: proof.map(x => x.toString()) as FixedLengthArray, + }); + + await createHonkProof(witGenResult.witness, MockPublicKernelCircuit.bytecode); + + const verifyResult = await verifyProof( + bbBinaryPath, + path.join(bbWorkingDirectory, 'proof'), + path.join(bbWorkingDirectory, 'vk'), + 'ultra_honk', + logger.info, + ); + + expect(verifyResult.status).toBe(BB_RESULT.SUCCESS); + }); +}); diff --git a/yarn-project/ivc-integration/src/client_ivc_integration.test.ts b/yarn-project/ivc-integration/src/client_ivc_integration.test.ts index 0b9a28a1c36..3610f1ab66e 100644 --- a/yarn-project/ivc-integration/src/client_ivc_integration.test.ts +++ b/yarn-project/ivc-integration/src/client_ivc_integration.test.ts @@ -116,7 +116,7 @@ describe('Client IVC Integration', () => { }; // Witness gen app and kernels const creatorAppWitnessGenResult = await witnessGenCreatorAppMockCircuit({ commitments_to_create: ['0x1', '0x2'] }); - const readerAppWitnessGenRult = await witnessGenReaderAppMockCircuit({ commitments_to_read: ['0x2', '0x0'] }); + const readerAppWitnessGenResult = await witnessGenReaderAppMockCircuit({ commitments_to_read: ['0x2', '0x0'] }); const initWitnessGenResult = await witnessGenMockPrivateKernelInitCircuit({ app_inputs: creatorAppWitnessGenResult.publicInputs, @@ -124,7 +124,7 @@ describe('Client IVC Integration', () => { }); const innerWitnessGenResult = await witnessGenMockPrivateKernelInnerCircuit({ prev_kernel_public_inputs: initWitnessGenResult.publicInputs, - app_inputs: readerAppWitnessGenRult.publicInputs, + app_inputs: readerAppWitnessGenResult.publicInputs, }); const resetWitnessGenResult = await witnessGenMockPrivateKernelResetCircuit({ @@ -153,7 +153,7 @@ describe('Client IVC Integration', () => { const witnessStack = [ creatorAppWitnessGenResult.witness, initWitnessGenResult.witness, - readerAppWitnessGenRult.witness, + readerAppWitnessGenResult.witness, innerWitnessGenResult.witness, resetWitnessGenResult.witness, tailWitnessGenResult.witness, diff --git a/yarn-project/ivc-integration/src/index.ts b/yarn-project/ivc-integration/src/index.ts index 820e2110a51..dac0fd16ea8 100644 --- a/yarn-project/ivc-integration/src/index.ts +++ b/yarn-project/ivc-integration/src/index.ts @@ -6,6 +6,7 @@ import MockPrivateKernelInitCircuit from '../artifacts/mock_private_kernel_init. import MockPrivateKernelInnerCircuit from '../artifacts/mock_private_kernel_inner.json' assert { type: 'json' }; import MockPrivateKernelResetCircuit from '../artifacts/mock_private_kernel_reset.json' assert { type: 'json' }; import MockPrivateKernelTailCircuit from '../artifacts/mock_private_kernel_tail.json' assert { type: 'json' }; +import MockPublicKernelCircuit from '../artifacts/mock_public_kernel.json' assert { type: 'json' }; import type { AppCreatorInputType, AppPublicInputs, @@ -15,7 +16,9 @@ import type { MockPrivateKernelInnerInputType, MockPrivateKernelResetInputType, MockPrivateKernelTailInputType, + MockPublicKernelInputType, PrivateKernelPublicInputs, + u8, } from './types/index.js'; // Re export the circuit jsons @@ -26,6 +29,7 @@ export { MockPrivateKernelInnerCircuit, MockPrivateKernelResetCircuit, MockPrivateKernelTailCircuit, + MockPublicKernelCircuit, }; export const MOCK_MAX_COMMITMENTS_PER_TX = 4; @@ -101,6 +105,17 @@ export async function witnessGenMockPrivateKernelTailCircuit( const { witness, returnValue } = await program.execute(args, foreignCallHandler); return { witness, - publicInputs: returnValue as PrivateKernelPublicInputs, + publicInputs: returnValue as KernelPublicInputs, + }; +} + +export async function witnessGenMockPublicKernelCircuit( + args: MockPublicKernelInputType, +): Promise> { + const program = new Noir(MockPublicKernelCircuit); + const { witness, returnValue } = await program.execute(args, foreignCallHandler); + return { + witness, + publicInputs: returnValue as u8, }; } diff --git a/yarn-project/ivc-integration/src/scripts/generate_ts_from_abi.ts b/yarn-project/ivc-integration/src/scripts/generate_ts_from_abi.ts index 43da58670c1..d020c891fa5 100644 --- a/yarn-project/ivc-integration/src/scripts/generate_ts_from_abi.ts +++ b/yarn-project/ivc-integration/src/scripts/generate_ts_from_abi.ts @@ -14,6 +14,7 @@ const circuits = [ 'mock_private_kernel_inner', 'mock_private_kernel_reset', 'mock_private_kernel_tail', + 'mock_public_kernel', ]; const main = async () => { diff --git a/yarn-project/simulator/src/public/index.ts b/yarn-project/simulator/src/public/index.ts index cf32d4b4e19..278c86d25bd 100644 --- a/yarn-project/simulator/src/public/index.ts +++ b/yarn-project/simulator/src/public/index.ts @@ -8,3 +8,4 @@ export * from './public_db_sources.js'; export * from './public_kernel.js'; export * from './public_kernel_circuit_simulator.js'; export { PublicProcessor, PublicProcessorFactory } from './public_processor.js'; +export { PublicSideEffectTrace } from './side_effect_trace.js'; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 159c6eecb4a..573f906c74e 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -292,6 +292,7 @@ __metadata: "@aztec/noir-protocol-circuits-types": "workspace:^" "@aztec/simulator": "workspace:^" "@aztec/telemetry-client": "workspace:^" + "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 "@msgpack/msgpack": ^3.0.0-beta2 "@noir-lang/noirc_abi": "portal:../../noir/packages/noirc_abi" @@ -700,6 +701,7 @@ __metadata: "@types/node": ^18.7.23 change-case: ^5.4.4 jest: ^29.5.0 + jest-mock-extended: ^4.0.0-beta1 levelup: ^5.1.1 memdown: ^6.1.1 ts-node: ^10.9.1 @@ -10833,6 +10835,19 @@ __metadata: languageName: node linkType: hard +"jest-mock-extended@npm:^4.0.0-beta1": + version: 4.0.0-beta1 + resolution: "jest-mock-extended@npm:4.0.0-beta1" + dependencies: + ts-essentials: ^10.0.2 + peerDependencies: + "@jest/globals": ^28.0.0 || ^29.0.0 + jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 + typescript: ^3.0.0 || ^4.0.0 || ^5.0.0 + checksum: de84487f07a4a812fcfdcfde5234dedb35676103a8542bd30b4dd8b7ee4fdaa4f5d5c60b6c076539d10a8d303af36cbee24a9a16a20aa4f43413a69427a4e99b + languageName: node + linkType: hard + "jest-mock@npm:^29.7.0": version: 29.7.0 resolution: "jest-mock@npm:29.7.0" @@ -15356,6 +15371,18 @@ __metadata: languageName: node linkType: hard +"ts-essentials@npm:^10.0.2": + version: 10.0.2 + resolution: "ts-essentials@npm:10.0.2" + peerDependencies: + typescript: ">=4.5.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: f2d612b543b405f27cb890f15667454ad48d7bd3e699d62adf0fedd8c3797dc4ecd93d33ba5983a02f97912cb6383f2ca349d02747bd57fd4384fc1f264f1826 + languageName: node + linkType: hard + "ts-graphviz@npm:^1.5.0": version: 1.8.2 resolution: "ts-graphviz@npm:1.8.2"