diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index 92e618fe69e..43f2d75ac6b 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -62,7 +62,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup { IProofCommitmentEscrow public immutable PROOF_COMMITMENT_ESCROW; uint256 public immutable VERSION; IFeeJuicePortal public immutable FEE_JUICE_PORTAL; - IVerifier public verifier; + IVerifier public blockProofVerifier; ChainTips public tips; DataStructures.EpochProofClaim public proofClaim; @@ -80,6 +80,10 @@ contract Rollup is Leonidas, IRollup, ITestRollup { // Testing only. This should be removed eventually. uint256 private assumeProvenThroughBlockNumber; + // Listed at the end of the contract to avoid changing storage slots + // TODO(palla/prover) Drop blockProofVerifier and move this verifier to that slot + IVerifier public epochProofVerifier; + constructor( IRegistry _registry, IFeeJuicePortal _fpcJuicePortal, @@ -87,7 +91,8 @@ contract Rollup is Leonidas, IRollup, ITestRollup { address _ares, address[] memory _validators ) Leonidas(_ares) { - verifier = new MockVerifier(); + blockProofVerifier = new MockVerifier(); + epochProofVerifier = new MockVerifier(); REGISTRY = _registry; FEE_JUICE_PORTAL = _fpcJuicePortal; PROOF_COMMITMENT_ESCROW = new MockProofCommitmentEscrow(); @@ -100,7 +105,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup { // Genesis block blocks[0] = BlockLog({ archive: bytes32(Constants.GENESIS_ARCHIVE_ROOT), - blockHash: bytes32(0), + blockHash: bytes32(0), // TODO(palla/prover): The first block does not have hash zero slotNumber: Slot.wrap(0) }); for (uint256 i = 0; i < _validators.length; i++) { @@ -144,8 +149,19 @@ contract Rollup is Leonidas, IRollup, ITestRollup { * * @param _verifier - The new verifier contract */ - function setVerifier(address _verifier) external override(ITestRollup) onlyOwner { - verifier = IVerifier(_verifier); + function setBlockVerifier(address _verifier) external override(ITestRollup) onlyOwner { + blockProofVerifier = IVerifier(_verifier); + } + + /** + * @notice Set the verifier contract + * + * @dev This is only needed for testing, and should be removed + * + * @param _verifier - The new verifier contract + */ + function setEpochVerifier(address _verifier) external override(ITestRollup) onlyOwner { + epochProofVerifier = IVerifier(_verifier); } /** @@ -410,7 +426,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup { publicInputs[i + 91] = part; } - if (!verifier.verify(_proof, publicInputs)) { + if (!blockProofVerifier.verify(_proof, publicInputs)) { revert Errors.Rollup__InvalidProof(); } @@ -484,7 +500,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup { bytes32[] memory publicInputs = getEpochProofPublicInputs(_epochSize, _args, _fees, _aggregationObject); - if (!verifier.verify(_proof, publicInputs)) { + if (!epochProofVerifier.verify(_proof, publicInputs)) { revert Errors.Rollup__InvalidProof(); } @@ -550,7 +566,8 @@ contract Rollup is Leonidas, IRollup, ITestRollup { } bytes32 expectedPreviousBlockHash = blocks[previousBlockNumber].blockHash; - if (expectedPreviousBlockHash != _args[2]) { + // TODO: Remove 0 check once we inject the proper genesis block hash + if (expectedPreviousBlockHash != 0 && expectedPreviousBlockHash != _args[2]) { revert Errors.Rollup__InvalidPreviousBlockHash(expectedPreviousBlockHash, _args[2]); } @@ -608,16 +625,16 @@ contract Rollup is Leonidas, IRollup, ITestRollup { // out_hash: root of this epoch's l2 to l1 message tree publicInputs[8] = _args[5]; - // fees[9-40]: array of recipient-value pairs + // fees[9-72]: array of recipient-value pairs for (uint256 i = 0; i < 64; i++) { publicInputs[9 + i] = _fees[i]; } // vk_tree_root - publicInputs[41] = vkTreeRoot; + publicInputs[73] = vkTreeRoot; // prover_id: id of current epoch's prover - publicInputs[42] = _args[6]; + publicInputs[74] = _args[6]; // the block proof is recursive, which means it comes with an aggregation object // this snippet copies it into the public inputs needed for verification @@ -628,7 +645,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup { assembly { part := calldataload(add(_aggregationObject.offset, mul(i, 32))) } - publicInputs[i + 43] = part; + publicInputs[i + 75] = part; } return publicInputs; diff --git a/l1-contracts/src/core/interfaces/IRollup.sol b/l1-contracts/src/core/interfaces/IRollup.sol index 3ed428cfab8..62aad69681b 100644 --- a/l1-contracts/src/core/interfaces/IRollup.sol +++ b/l1-contracts/src/core/interfaces/IRollup.sol @@ -11,7 +11,8 @@ import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; import {Timestamp, Slot, Epoch} from "@aztec/core/libraries/TimeMath.sol"; interface ITestRollup { - function setVerifier(address _verifier) external; + function setBlockVerifier(address _verifier) external; + function setEpochVerifier(address _verifier) external; function setVkTreeRoot(bytes32 _vkTreeRoot) external; function setAssumeProvenThroughBlockNumber(uint256 blockNumber) external; } diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 90798ca24f7..f1ea19b3daf 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -671,10 +671,11 @@ contract RollupTest is DecoderBase { ); _submitEpochProof(rollup, 1, wrong, data.archive, preBlockHash, data.blockHash, bytes32(0)); - vm.expectRevert( - abi.encodeWithSelector(Errors.Rollup__InvalidPreviousBlockHash.selector, preBlockHash, wrong) - ); - _submitEpochProof(rollup, 1, preArchive, data.archive, wrong, data.blockHash, bytes32(0)); + // TODO: Reenable when we setup proper initial block hash + // vm.expectRevert( + // abi.encodeWithSelector(Errors.Rollup__InvalidPreviousBlockHash.selector, preBlockHash, wrong) + // ); + // _submitEpochProof(rollup, 1, preArchive, data.archive, wrong, data.blockHash, bytes32(0)); } function testSubmitProofInvalidArchive() public setUpFor("empty_block_1") { diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/empty_block_root_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/empty_block_root_rollup_inputs.nr index 3ca5cb691a0..ecff942e975 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/empty_block_root_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/empty_block_root_rollup_inputs.nr @@ -6,7 +6,6 @@ pub struct EmptyBlockRootRollupInputs { archive: AppendOnlyTreeSnapshot, block_hash: Field, global_variables: GlobalVariables, - out_hash: Field, vk_tree_root: Field, // TODO(#7346): Temporarily added prover_id while we verify block-root proofs on L1 prover_id: Field, @@ -21,7 +20,7 @@ impl EmptyBlockRootRollupInputs { end_block_hash: self.block_hash, start_global_variables: self.global_variables, end_global_variables: self.global_variables, - out_hash: self.out_hash, + out_hash: 0, // out_hash is ignored when merging if the block proof is padding fees: [FeeRecipient::empty(); 32], vk_tree_root: self.vk_tree_root, prover_id: self.prover_id diff --git a/yarn-project/.prettierignore b/yarn-project/.prettierignore index 68e0d4e77dd..9a8465b7bdb 100644 --- a/yarn-project/.prettierignore +++ b/yarn-project/.prettierignore @@ -7,4 +7,4 @@ boxes/*/src/artifacts/*.json boxes/*/src/artifacts/*.ts boxes/*/src/contracts/target/*.json *.md -end-to-end/src/fixtures/dumps/block_result.json +end-to-end/src/fixtures/dumps/*.json diff --git a/yarn-project/bb-prover/src/honk.ts b/yarn-project/bb-prover/src/honk.ts index 93c72cae6e8..a01776ff46a 100644 --- a/yarn-project/bb-prover/src/honk.ts +++ b/yarn-project/bb-prover/src/honk.ts @@ -6,16 +6,16 @@ const UltraKeccakHonkCircuits = [ 'BlockRootRollupFinalArtifact', 'RootRollupArtifact', ] as const satisfies ProtocolArtifact[]; -type UltraKeccakHonkCircuits = (typeof UltraKeccakHonkCircuits)[number]; -type UltraHonkCircuits = Exclude; +export type UltraKeccakHonkProtocolArtifact = (typeof UltraKeccakHonkCircuits)[number]; +export type UltraHonkProtocolArtifact = Exclude; -export function getUltraHonkFlavorForCircuit(artifact: UltraKeccakHonkCircuits): 'ultra_keccak_honk'; -export function getUltraHonkFlavorForCircuit(artifact: UltraHonkCircuits): 'ultra_honk'; +export function getUltraHonkFlavorForCircuit(artifact: UltraKeccakHonkProtocolArtifact): 'ultra_keccak_honk'; +export function getUltraHonkFlavorForCircuit(artifact: UltraHonkProtocolArtifact): 'ultra_honk'; export function getUltraHonkFlavorForCircuit(artifact: ProtocolArtifact): UltraHonkFlavor; export function getUltraHonkFlavorForCircuit(artifact: ProtocolArtifact): UltraHonkFlavor { return isUltraKeccakHonkCircuit(artifact) ? 'ultra_keccak_honk' : 'ultra_honk'; } -function isUltraKeccakHonkCircuit(artifact: ProtocolArtifact): artifact is UltraKeccakHonkCircuits { - return UltraKeccakHonkCircuits.includes(artifact as UltraKeccakHonkCircuits); +function isUltraKeccakHonkCircuit(artifact: ProtocolArtifact): artifact is UltraKeccakHonkProtocolArtifact { + return UltraKeccakHonkCircuits.includes(artifact as UltraKeccakHonkProtocolArtifact); } diff --git a/yarn-project/bb-prover/src/index.ts b/yarn-project/bb-prover/src/index.ts index e8914146199..0ea93e76263 100644 --- a/yarn-project/bb-prover/src/index.ts +++ b/yarn-project/bb-prover/src/index.ts @@ -3,5 +3,6 @@ export * from './test/index.js'; export * from './verifier/index.js'; export * from './config.js'; export * from './bb/execute.js'; +export * from './honk.js'; export { type ClientProtocolCircuitVerifier } from '@aztec/circuit-types'; diff --git a/yarn-project/bb-prover/src/prover/bb_prover.ts b/yarn-project/bb-prover/src/prover/bb_prover.ts index 40c75b28912..1e945b3c58d 100644 --- a/yarn-project/bb-prover/src/prover/bb_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_prover.ts @@ -59,6 +59,7 @@ import { convertBlockRootRollupInputsToWitnessMap, convertBlockRootRollupOutputsFromWitnessMap, convertEmptyBlockRootRollupInputsToWitnessMap, + convertEmptyBlockRootRollupOutputsFromWitnessMap, convertMergeRollupInputsToWitnessMap, convertMergeRollupOutputsFromWitnessMap, convertPrivateKernelEmptyInputsToWitnessMap, @@ -395,12 +396,12 @@ export class BBNativeRollupProver implements ServerCircuitProver { 'EmptyBlockRootRollupArtifact', RECURSIVE_PROOF_LENGTH, convertEmptyBlockRootRollupInputsToWitnessMap, - convertBlockRootRollupOutputsFromWitnessMap, + convertEmptyBlockRootRollupOutputsFromWitnessMap, ); - const verificationKey = await this.getVerificationKeyDataForCircuit('BlockRootRollupArtifact'); + const verificationKey = await this.getVerificationKeyDataForCircuit('EmptyBlockRootRollupArtifact'); - await this.verifyProof('BlockRootRollupArtifact', proof.binaryProof); + await this.verifyProof('EmptyBlockRootRollupArtifact', proof.binaryProof); return makePublicInputsAndRecursiveProof(circuitOutput, proof, verificationKey); } diff --git a/yarn-project/bb-prover/src/test/test_circuit_prover.ts b/yarn-project/bb-prover/src/test/test_circuit_prover.ts index 8a56a11119e..89df65b7fa5 100644 --- a/yarn-project/bb-prover/src/test/test_circuit_prover.ts +++ b/yarn-project/bb-prover/src/test/test_circuit_prover.ts @@ -54,6 +54,7 @@ import { convertBlockRootRollupInputsToWitnessMap, convertBlockRootRollupOutputsFromWitnessMap, convertEmptyBlockRootRollupInputsToWitnessMap, + convertEmptyBlockRootRollupOutputsFromWitnessMap, convertMergeRollupInputsToWitnessMap, convertMergeRollupOutputsFromWitnessMap, convertPrivateKernelEmptyInputsToWitnessMap, @@ -367,7 +368,7 @@ export class TestCircuitProver implements ServerCircuitProver { SimulatedServerCircuitArtifacts.EmptyBlockRootRollupArtifact, ); - const result = convertBlockRootRollupOutputsFromWitnessMap(witness); + const result = convertEmptyBlockRootRollupOutputsFromWitnessMap(witness); this.instrumentation.recordDuration('simulationDuration', 'empty-block-root-rollup', timer); emitCircuitSimulationStats( diff --git a/yarn-project/bb-prover/src/verifier/bb_verifier.ts b/yarn-project/bb-prover/src/verifier/bb_verifier.ts index 55b16f1b846..394b582c17f 100644 --- a/yarn-project/bb-prover/src/verifier/bb_verifier.ts +++ b/yarn-project/bb-prover/src/verifier/bb_verifier.ts @@ -22,7 +22,7 @@ import { verifyProof, } from '../bb/execute.js'; import { type BBConfig } from '../config.js'; -import { getUltraHonkFlavorForCircuit } from '../honk.js'; +import { type UltraKeccakHonkProtocolArtifact, getUltraHonkFlavorForCircuit } from '../honk.js'; import { mapProtocolArtifactNameToCircuitName } from '../stats.js'; import { extractVkData } from '../verification_key/verification_key_data.js'; @@ -127,7 +127,7 @@ export class BBCircuitVerifier implements ClientProtocolCircuitVerifier { await runInDirectory(this.config.bbWorkingDirectory, operation, this.config.bbSkipCleanup); } - public async generateSolidityContract(circuit: ProtocolArtifact, contractName: string) { + public async generateSolidityContract(circuit: UltraKeccakHonkProtocolArtifact, contractName: string) { const result = await generateContractForCircuit( this.config.bbBinaryPath, this.config.bbWorkingDirectory, diff --git a/yarn-project/circuit-types/src/interfaces/block-prover.ts b/yarn-project/circuit-types/src/interfaces/block-prover.ts index 8ef43b17733..a2a053d8a37 100644 --- a/yarn-project/circuit-types/src/interfaces/block-prover.ts +++ b/yarn-project/circuit-types/src/interfaces/block-prover.ts @@ -1,4 +1,4 @@ -import { type Fr, type GlobalVariables, type Proof } from '@aztec/circuits.js'; +import { type Fr, type GlobalVariables, type Proof, type RootRollupPublicInputs } from '@aztec/circuits.js'; import { type L2Block } from '../l2_block.js'; import { type ProcessedTx } from '../tx/processed_tx.js'; @@ -32,6 +32,8 @@ export type ProvingBlockResult = SimulationBlockResult & { aggregationObject: Fr[]; }; +export type ProvingEpochResult = { publicInputs: RootRollupPublicInputs; proof: Proof }; + /** Receives processed txs as part of block simulation or proving. */ export interface ProcessedTxHandler { /** @@ -75,4 +77,8 @@ export interface BlockProver extends BlockSimulator { export interface EpochProver extends BlockProver { startNewEpoch(epochNumber: number, totalNumBlocks: number): ProvingTicket; + + setEpochCompleted(): void; + + finaliseEpoch(): ProvingEpochResult; } diff --git a/yarn-project/circuit-types/src/interfaces/prover-client.ts b/yarn-project/circuit-types/src/interfaces/prover-client.ts index 7e8aa848f4f..e7648c0ada8 100644 --- a/yarn-project/circuit-types/src/interfaces/prover-client.ts +++ b/yarn-project/circuit-types/src/interfaces/prover-client.ts @@ -2,7 +2,7 @@ import { type TxHash } from '@aztec/circuit-types'; import { Fr } from '@aztec/circuits.js'; import { type ConfigMappingsType, booleanConfigHelper, numberConfigHelper } from '@aztec/foundation/config'; -import { type BlockProver } from './block-prover.js'; +import { type EpochProver } from './block-prover.js'; import { type MerkleTreeOperations } from './merkle_tree_operations.js'; import { type ProvingJobSource } from './proving-job.js'; @@ -84,10 +84,9 @@ function parseProverId(str: string) { /** * The interface to the prover client. * Provides the ability to generate proofs and build rollups. - * TODO(palla/prover-node): Rename this interface */ -export interface ProverClient { - createBlockProver(db: MerkleTreeOperations): BlockProver; +export interface EpochProverManager { + createEpochProver(db: MerkleTreeOperations): EpochProver; start(): Promise; diff --git a/yarn-project/circuits.js/src/structs/proof.ts b/yarn-project/circuits.js/src/structs/proof.ts index 298210cc551..57b57606df4 100644 --- a/yarn-project/circuits.js/src/structs/proof.ts +++ b/yarn-project/circuits.js/src/structs/proof.ts @@ -91,6 +91,13 @@ export class Proof { static fromString(str: string) { return Proof.fromBuffer(Buffer.from(str, 'hex')); } + + /** Returns whether this proof is actually empty. */ + public isEmpty() { + return ( + this.buffer.length === EMPTY_PROOF_SIZE && this.buffer.every(byte => byte === 0) && this.numPublicInputs === 0 + ); + } } /** diff --git a/yarn-project/circuits.js/src/structs/rollup/empty_block_root_rollup_inputs.ts b/yarn-project/circuits.js/src/structs/rollup/empty_block_root_rollup_inputs.ts index 52de60ff223..87506ad0a7c 100644 --- a/yarn-project/circuits.js/src/structs/rollup/empty_block_root_rollup_inputs.ts +++ b/yarn-project/circuits.js/src/structs/rollup/empty_block_root_rollup_inputs.ts @@ -13,7 +13,6 @@ export class EmptyBlockRootRollupInputs { public readonly archive: AppendOnlyTreeSnapshot, public readonly blockHash: Fr, public readonly globalVariables: GlobalVariables, - public readonly outHash: Fr, public readonly vkTreeRoot: Fr, // // TODO(#7346): Temporarily added prover_id while we verify block-root proofs on L1 public readonly proverId: Fr, @@ -50,14 +49,7 @@ export class EmptyBlockRootRollupInputs { * @returns An array of fields. */ static getFields(fields: FieldsOf) { - return [ - fields.archive, - fields.blockHash, - fields.globalVariables, - fields.outHash, - fields.vkTreeRoot, - fields.proverId, - ] as const; + return [fields.archive, fields.blockHash, fields.globalVariables, fields.vkTreeRoot, fields.proverId] as const; } /** @@ -73,7 +65,6 @@ export class EmptyBlockRootRollupInputs { GlobalVariables.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader), - Fr.fromBuffer(reader), ); } diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 490108f4920..50f332c96d9 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -1077,7 +1077,6 @@ export function makeEmptyBlockRootRollupInputs( globalVariables ?? makeGlobalVariables(seed + 0x200), fr(seed + 0x300), fr(seed + 0x400), - fr(seed + 0x500), ); } diff --git a/yarn-project/cli/src/cmds/l1/deploy_l1_verifier.ts b/yarn-project/cli/src/cmds/l1/deploy_l1_verifier.ts index 656986f5b58..675905d1b8a 100644 --- a/yarn-project/cli/src/cmds/l1/deploy_l1_verifier.ts +++ b/yarn-project/cli/src/cmds/l1/deploy_l1_verifier.ts @@ -1,5 +1,5 @@ import { createCompatibleClient } from '@aztec/aztec.js'; -import { createEthereumChain, createL1Clients, deployL1Contract } from '@aztec/ethereum'; +import { compileContract, createEthereumChain, createL1Clients, deployL1Contract } from '@aztec/ethereum'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { InvalidOptionArgumentError } from 'commander'; @@ -24,41 +24,7 @@ export async function deployUltraHonkVerifier( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing bb-prover even in devDeps results in a circular dependency error through @aztec/simulator. Need to ignore because this line doesn't cause an error in a dev environment const { BBCircuitVerifier } = await import('@aztec/bb-prover'); - - const circuitVerifier = await BBCircuitVerifier.new({ bbBinaryPath, bbWorkingDirectory }); - const contractSrc = await circuitVerifier.generateSolidityContract( - 'BlockRootRollupFinalArtifact', - 'UltraHonkVerifier.sol', - ); - log('Generated UltraHonkVerifier contract'); - - const input = { - language: 'Solidity', - sources: { - 'UltraHonkVerifier.sol': { - content: contractSrc, - }, - }, - settings: { - // we require the optimizer - optimizer: { - enabled: true, - runs: 200, - }, - evmVersion: 'paris', - outputSelection: { - '*': { - '*': ['evm.bytecode.object', 'abi'], - }, - }, - }, - }; - - const output = JSON.parse(solc.compile(JSON.stringify(input))); - log('Compiled UltraHonkVerifier'); - - const abi = output.contracts['UltraHonkVerifier.sol']['HonkVerifier'].abi; - const bytecode: string = output.contracts['UltraHonkVerifier.sol']['HonkVerifier'].evm.bytecode.object; + const verifier = await BBCircuitVerifier.new({ bbBinaryPath, bbWorkingDirectory }); const { publicClient, walletClient } = createL1Clients( ethRpcUrl, @@ -66,9 +32,6 @@ export async function deployUltraHonkVerifier( createEthereumChain(ethRpcUrl, l1ChainId).chainInfo, ); - const { address: verifierAddress } = await deployL1Contract(walletClient, publicClient, abi, `0x${bytecode}`); - log(`Deployed HonkVerifier at ${verifierAddress.toString()}`); - const pxe = await createCompatibleClient(pxeRpcUrl, debugLogger); const { l1ContractAddresses } = await pxe.getNodeInfo(); @@ -80,7 +43,25 @@ export async function deployUltraHonkVerifier( client: walletClient, }); - await rollup.write.setVerifier([verifierAddress.toString()]); + // REFACTOR: Extract this method to a common package. We need a package that deals with L1 + // but also has a reference to L1 artifacts and bb-prover. + const setupVerifier = async ( + artifact: Parameters<(typeof verifier)['generateSolidityContract']>[0], // Cannot properly import the type here due to the hack above + method: 'setBlockVerifier' | 'setEpochVerifier', + ) => { + const contract = await verifier.generateSolidityContract(artifact, 'UltraHonkVerifier.sol'); + log(`Generated UltraHonkVerifier contract for ${artifact}`); + const { abi, bytecode } = compileContract('UltraHonkVerifier.sol', 'HonkVerifier', contract, solc); + log(`Compiled UltraHonkVerifier contract for ${artifact}`); + const { address: verifierAddress } = await deployL1Contract(walletClient, publicClient, abi, bytecode); + log(`Deployed real ${artifact} verifier at ${verifierAddress}`); + await rollup.write[method]([verifierAddress.toString()]); + log(`Set ${artifact} verifier in ${rollup.address} rollup contract to ${verifierAddress}`); + }; + + await setupVerifier('BlockRootRollupFinalArtifact', 'setBlockVerifier'); + await setupVerifier('RootRollupArtifact', 'setEpochVerifier'); + log(`Rollup accepts only real proofs now`); } @@ -117,6 +98,7 @@ export async function deployMockVerifier( client: walletClient, }); - await rollup.write.setVerifier([mockVerifierAddress.toString()]); + await rollup.write.setBlockVerifier([mockVerifierAddress.toString()]); + await rollup.write.setEpochVerifier([mockVerifierAddress.toString()]); log(`Rollup accepts only fake proofs now`); } diff --git a/yarn-project/end-to-end/.prettierignore b/yarn-project/end-to-end/.prettierignore index d16e4d38d98..b1979911a8c 100644 --- a/yarn-project/end-to-end/.prettierignore +++ b/yarn-project/end-to-end/.prettierignore @@ -1,2 +1,2 @@ src/e2e_sandbox_example.test.ts -src/fixtures/dumps/block_result.json +src/fixtures/dumps/*.json diff --git a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts index 84ccd686e44..4463505fb1f 100644 --- a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts @@ -1,9 +1,9 @@ -import { L2Block, deployL1Contract, fileURLToPath } from '@aztec/aztec.js'; +import { deployL1Contract, fileURLToPath } from '@aztec/aztec.js'; import { BBCircuitVerifier } from '@aztec/bb-prover'; -import { Fr, Proof } from '@aztec/circuits.js'; -import { type L1ContractAddresses } from '@aztec/ethereum'; +import { Proof, RootRollupPublicInputs } from '@aztec/circuits.js'; +import { compileContract, createL1Clients } from '@aztec/ethereum'; import { type Logger } from '@aztec/foundation/log'; -import { BufferReader } from '@aztec/foundation/serialize'; +import { IVerifierAbi } from '@aztec/l1-artifacts'; import { type Anvil } from '@viem/anvil'; import { readFile } from 'fs/promises'; @@ -14,6 +14,7 @@ import { type Account, type Chain, type GetContractReturnType, + type Hex, type HttpTransport, type PublicClient, type WalletClient, @@ -24,28 +25,23 @@ import { mnemonicToAccount } from 'viem/accounts'; import { MNEMONIC } from '../fixtures/fixtures.js'; import { getACVMConfig } from '../fixtures/get_acvm_config.js'; import { getBBConfig } from '../fixtures/get_bb_config.js'; -import { getLogger, setupL1Contracts, startAnvil } from '../fixtures/utils.js'; +import { getLogger, startAnvil } from '../fixtures/utils.js'; /** * Regenerate this test's fixture with - * AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/end-to-end test e2e_prover + * AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/prover-client test bb_prover_full_rollup */ describe('proof_verification', () => { let proof: Proof; - let proverId: Fr; - let vkTreeRoot: Fr; - let block: L2Block; - let aggregationObject: Fr[]; + let publicInputs: RootRollupPublicInputs; let anvil: Anvil | undefined; let walletClient: WalletClient; let publicClient: PublicClient; - // eslint-disable-next-line - let l1ContractAddresses: L1ContractAddresses; let logger: Logger; let circuitVerifier: BBCircuitVerifier; let bbTeardown: () => Promise; let acvmTeardown: () => Promise; - let verifierContract: GetContractReturnType; + let verifierContract: GetContractReturnType; beforeAll(async () => { logger = getLogger(); @@ -55,14 +51,6 @@ describe('proof_verification', () => { } logger.info('Anvil started'); - ({ l1ContractAddresses, publicClient, walletClient } = await setupL1Contracts( - rpcUrl, - mnemonicToAccount(MNEMONIC), - logger, - { assumeProvenThrough: undefined }, - )); - logger.info('l1 contracts done'); - const bb = await getBBConfig(logger); const acvm = await getACVMConfig(logger); @@ -73,134 +61,54 @@ describe('proof_verification', () => { bbTeardown = bb!.cleanup; acvmTeardown = acvm!.cleanup; - logger.info('bb, acvm done'); - - const content = await circuitVerifier.generateSolidityContract( - 'BlockRootRollupFinalArtifact', - 'UltraHonkVerifier.sol', - ); - logger.info('generated contract'); - - const input = { - language: 'Solidity', - sources: { - 'UltraHonkVerifier.sol': { - content, - }, - }, - settings: { - // we require the optimizer - optimizer: { - enabled: true, - runs: 200, - }, - evmVersion: 'paris', - outputSelection: { - '*': { - '*': ['evm.bytecode.object', 'abi'], - }, - }, - }, - }; - - const output = JSON.parse(solc.compile(JSON.stringify(input))); - logger.info('compiled contract'); - - const abi = output.contracts['UltraHonkVerifier.sol']['HonkVerifier'].abi; - const bytecode: string = output.contracts['UltraHonkVerifier.sol']['HonkVerifier'].evm.bytecode.object; - - const { address: verifierAddress } = await deployL1Contract(walletClient, publicClient, abi, `0x${bytecode}`); - verifierContract = getContract({ - address: verifierAddress.toString(), - client: publicClient, - abi, - }) as any; - logger.info('deployed verifier'); + logger.info('BB and ACVM initialized'); + + ({ publicClient, walletClient } = createL1Clients(rpcUrl, mnemonicToAccount(MNEMONIC))); + const content = await circuitVerifier.generateSolidityContract('RootRollupArtifact', 'UltraHonkVerifier.sol'); + const { bytecode, abi } = compileContract('UltraHonkVerifier.sol', 'HonkVerifier', content, solc); + const { address: verifierAddress } = await deployL1Contract(walletClient, publicClient, abi, bytecode); + verifierContract = getContract({ address: verifierAddress.toString(), client: publicClient, abi: IVerifierAbi }); + logger.info('Deployed verifier'); }); afterAll(async () => { - // await ctx.teardown(); await anvil?.stop(); await bbTeardown(); await acvmTeardown(); }); beforeAll(async () => { - // regenerate with - // AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/end-to-end test e2e_prover - const blockResult = JSON.parse( - await readFile(join(fileURLToPath(import.meta.url), '../../fixtures/dumps/block_result.json'), 'utf-8'), + // AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/prover-client test bb_prover_full_rollup + const epochProof = JSON.parse( + await readFile(join(fileURLToPath(import.meta.url), '../../fixtures/dumps/epoch_proof_result.json'), 'utf-8'), ); - block = L2Block.fromString(blockResult.block); - proof = Proof.fromString(blockResult.proof); - proverId = Fr.fromString(blockResult.proverId); - vkTreeRoot = Fr.fromString(blockResult.vkTreeRoot); - aggregationObject = blockResult.aggregationObject.map((x: string) => Fr.fromString(x)); + proof = Proof.fromString(epochProof.proof); + publicInputs = RootRollupPublicInputs.fromString(epochProof.publicInputs); + }); + + describe('public inputs', () => { + it('output and proof public inputs are equal', () => { + const proofPublicInputs = proof.extractPublicInputs().map(x => x.toString()); + const aggregationObject = proof.extractAggregationObject(); + const outputPublicInputs = [...publicInputs.toFields(), ...aggregationObject].map(x => x.toString()); + + expect(proofPublicInputs).toEqual(outputPublicInputs); + }); }); describe('bb', () => { it('verifies proof', async () => { - await expect( - circuitVerifier.verifyProofForCircuit('BlockRootRollupFinalArtifact', proof), - ).resolves.toBeUndefined(); + await expect(circuitVerifier.verifyProofForCircuit('RootRollupArtifact', proof)).resolves.toBeUndefined(); }); }); - describe('HonkVerifier', () => { - it('verifies full proof', async () => { - // skip proof size which is an uint32 - const reader = BufferReader.asReader(proof.buffer.subarray(4)); - const [circuitSize, numPublicInputs, publicInputsOffset] = reader.readArray(3, Fr); - const publicInputs = reader.readArray(numPublicInputs.toNumber(), Fr).map(x => x.toString()); - - const proofStr = `0x${Buffer.concat([ - circuitSize.toBuffer(), - numPublicInputs.toBuffer(), - publicInputsOffset.toBuffer(), - reader.readToEnd(), - ]).toString('hex')}` as const; - - await expect(verifierContract.read.verify([proofStr, publicInputs])).resolves.toBeTruthy(); - }); + describe('honk verifier', () => { + it('verifies proof', async () => { + const proofStr = `0x${proof.withoutPublicInputs().toString('hex')}` as Hex; + const proofPublicInputs = proof.extractPublicInputs().map(x => x.toString()); - it('verifies proof taking public inputs from block', async () => { - const reader = BufferReader.asReader(proof.buffer.subarray(4)); - const [circuitSize, numPublicInputs, publicInputsOffset] = reader.readArray(3, Fr); - const publicInputsFromProof = reader.readArray(numPublicInputs.toNumber(), Fr).map(x => x.toString()); - - const proofStr = `0x${Buffer.concat([ - circuitSize.toBuffer(), - numPublicInputs.toBuffer(), - publicInputsOffset.toBuffer(), - reader.readToEnd(), - ]).toString('hex')}` as const; - - const publicInputs = [ - block.header.lastArchive.root, - block.header.globalVariables.blockNumber, - block.archive.root, - new Fr(block.archive.nextAvailableLeafIndex), - Fr.ZERO, // prev block hash - block.hash(), - ...block.header.globalVariables.toFields(), // start global vars - ...block.header.globalVariables.toFields(), // end global vars - new Fr(block.header.contentCommitment.outHash), - block.header.globalVariables.coinbase.toField(), // the fee taker's address - block.header.totalFees, // how much they got - ...Array(62).fill(Fr.ZERO), // 31 other (fee takers, fee) pairs - vkTreeRoot, - proverId, // 0x51 - ...aggregationObject, - ].map((x: Fr) => x.toString()); - - expect(publicInputs.length).toEqual(publicInputsFromProof.length); - expect(publicInputs.slice(0, 27)).toEqual(publicInputsFromProof.slice(0, 27)); - expect(publicInputs.slice(27, 89)).toEqual(publicInputsFromProof.slice(27, 89)); - expect(publicInputs.slice(89, 91)).toEqual(publicInputsFromProof.slice(89, 91)); - expect(publicInputs.slice(91)).toEqual(publicInputsFromProof.slice(91)); - - await expect(verifierContract.read.verify([proofStr, publicInputs])).resolves.toBeTruthy(); + await expect(verifierContract.read.verify([proofStr, proofPublicInputs])).resolves.toBeTruthy(); }); }); }); diff --git a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts index 89a833ff216..7dc346215f6 100644 --- a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts @@ -16,7 +16,13 @@ import { createDebugLogger, deployL1Contract, } from '@aztec/aztec.js'; -import { BBCircuitVerifier, type ClientProtocolCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover'; +import { + BBCircuitVerifier, + type ClientProtocolCircuitVerifier, + TestCircuitVerifier, + type UltraKeccakHonkProtocolArtifact, +} from '@aztec/bb-prover'; +import { compileContract } from '@aztec/ethereum'; import { RollupAbi } from '@aztec/l1-artifacts'; import { TokenContract } from '@aztec/noir-contracts.js'; import { type ProverNode, type ProverNodeConfig, createProverNode } from '@aztec/prover-node'; @@ -116,7 +122,7 @@ export class FullProverTest { FullProverTest.TOKEN_DECIMALS, ) .send() - .deployed({ proven: true }); + .deployed(); this.logger.verbose(`Token deployed to ${asset.address}`); return { tokenContractAddress: asset.address }; @@ -172,7 +178,11 @@ export class FullProverTest { minTxsPerBlock: this.minNumberOfTxsPerBlock, }); } else { + this.logger.debug(`Configuring the node min txs per block ${this.minNumberOfTxsPerBlock}...`); this.circuitProofVerifier = new TestCircuitVerifier(); + await this.aztecNode.setConfig({ + minTxsPerBlock: this.minNumberOfTxsPerBlock, + }); } this.logger.debug(`Main setup completed, initializing full prover PXE, Node, and Prover Node...`); @@ -302,7 +312,7 @@ export class FullProverTest { const { fakeProofsAsset: asset, accounts } = this; const amount = 10000n; - const waitOpts = { proven: true }; + const waitOpts = { proven: false }; this.logger.verbose(`Minting ${amount} publicly...`); await asset.methods.mint_public(accounts[0].address, amount).send().wait(waitOpts); @@ -314,7 +324,7 @@ export class FullProverTest { await this.addPendingShieldNoteToPXE(0, amount, secretHash, receipt.txHash); const txClaim = asset.methods.redeem_shield(accounts[0].address, amount, secret).send(); - await txClaim.wait({ debug: true, proven: true }); + await txClaim.wait({ ...waitOpts, debug: true }); this.logger.verbose(`Minting complete.`); return { amount }; @@ -355,51 +365,31 @@ export class FullProverTest { throw new Error('No verifier'); } + const verifier = this.circuitProofVerifier as BBCircuitVerifier; const { walletClient, publicClient, l1ContractAddresses } = this.context.deployL1ContractsValues; - - const contract = await (this.circuitProofVerifier as BBCircuitVerifier).generateSolidityContract( - 'BlockRootRollupFinalArtifact', - 'UltraHonkVerifier.sol', - ); - - const input = { - language: 'Solidity', - sources: { - 'UltraHonkVerifier.sol': { - content: contract, - }, - }, - settings: { - // we require the optimizer - optimizer: { - enabled: true, - runs: 200, - }, - evmVersion: 'paris', - outputSelection: { - '*': { - '*': ['evm.bytecode.object', 'abi'], - }, - }, - }, - }; - - const output = JSON.parse(solc.compile(JSON.stringify(input))); - - const abi = output.contracts['UltraHonkVerifier.sol']['HonkVerifier'].abi; - const bytecode: string = output.contracts['UltraHonkVerifier.sol']['HonkVerifier'].evm.bytecode.object; - - const { address: verifierAddress } = await deployL1Contract(walletClient, publicClient, abi, `0x${bytecode}`); - - this.logger.info(`Deployed Real verifier at ${verifierAddress}`); - const rollup = getContract({ abi: RollupAbi, address: l1ContractAddresses.rollupAddress.toString(), client: walletClient, }); - await rollup.write.setVerifier([verifierAddress.toString()]); + // REFACTOR: Extract this method to a common package. We need a package that deals with L1 + // but also has a reference to L1 artifacts and bb-prover. + const setupVerifier = async ( + artifact: UltraKeccakHonkProtocolArtifact, + method: 'setBlockVerifier' | 'setEpochVerifier', + ) => { + const contract = await verifier.generateSolidityContract(artifact, 'UltraHonkVerifier.sol'); + const { abi, bytecode } = compileContract('UltraHonkVerifier.sol', 'HonkVerifier', contract, solc); + const { address: verifierAddress } = await deployL1Contract(walletClient, publicClient, abi, bytecode); + this.logger.info(`Deployed real ${artifact} verifier at ${verifierAddress}`); + + await rollup.write[method]([verifierAddress.toString()]); + }; + + await setupVerifier('BlockRootRollupFinalArtifact', 'setBlockVerifier'); + await setupVerifier('RootRollupArtifact', 'setEpochVerifier'); + this.logger.info('Rollup only accepts valid proofs now'); } } diff --git a/yarn-project/end-to-end/src/e2e_prover/full.test.ts b/yarn-project/end-to-end/src/e2e_prover/full.test.ts index f6d52aabb76..a5afa757502 100644 --- a/yarn-project/end-to-end/src/e2e_prover/full.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/full.test.ts @@ -1,5 +1,3 @@ -import { getTestData, isGenerateTestDataEnabled, writeTestData } from '@aztec/foundation/testing'; - import { FullProverTest } from './e2e_prover_test.js'; const TIMEOUT = 1_800_000; @@ -9,7 +7,7 @@ process.env.AVM_PROVING_STRICT = '1'; describe('full_prover', () => { const realProofs = !['true', '1'].includes(process.env.FAKE_PROOFS ?? ''); - const t = new FullProverTest('full_prover', 2, realProofs); + const t = new FullProverTest('full_prover', 1, realProofs); let { provenAssets, accounts, tokenSim, logger } = t; beforeAll(async () => { @@ -58,27 +56,20 @@ describe('full_prover', () => { logger.info(`Verifying private kernel tail proof`); await expect(t.circuitProofVerifier?.verifyProof(privateTx)).resolves.not.toThrow(); - const sentPrivateTx = privateInteraction.send({ skipPublicSimulation: true }); - const sentPublicTx = publicInteraction.send({ skipPublicSimulation: true }); - await Promise.all([ - sentPrivateTx.wait({ timeout: 60, interval: 10, proven: true, provenTimeout: 1200 }), - sentPublicTx.wait({ timeout: 60, interval: 10, proven: true, provenTimeout: 1200 }), - ]); + // TODO(palla/prover): The following depends on the epoch boundaries to work. It assumes that we're proving + // 2-block epochs, and a new epoch is starting now, so the 2nd tx will land on the last block of the epoch and + // get proven. That relies on how many blocks we mined before getting here. + // We can make this more robust when we add padding, set 1-block epochs, and rollback the test config to + // have a min of 2 txs per block, so these both land on the same block. + logger.info(`Sending first tx and awaiting it to be mined`); + await privateInteraction.send({ skipPublicSimulation: true }).wait({ timeout: 300, interval: 10 }); + logger.info(`Sending second tx and awaiting it to be proven`); + await publicInteraction + .send({ skipPublicSimulation: true }) + .wait({ timeout: 300, interval: 10, proven: true, provenTimeout: 1500 }); + tokenSim.transferPrivate(accounts[0].address, accounts[1].address, privateSendAmount); tokenSim.transferPublic(accounts[0].address, accounts[1].address, publicSendAmount); - - if (isGenerateTestDataEnabled()) { - const blockResults = getTestData('blockResults'); - // the first blocks were setup blocks with fake proofs - // the last block is the one that was actually proven to the end - const blockResult: any = blockResults.at(-1); - - if (!blockResult) { - // fail the test. User asked for fixtures but we don't have any - throw new Error('No block result found in test data'); - } - writeTestData('yarn-project/end-to-end/src/fixtures/dumps/block_result.json', JSON.stringify(blockResult)); - } }, TIMEOUT, ); diff --git a/yarn-project/end-to-end/src/e2e_prover_node.test.ts b/yarn-project/end-to-end/src/e2e_prover_node.test.ts index d4683ad294c..4172ac90fb1 100644 --- a/yarn-project/end-to-end/src/e2e_prover_node.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover_node.test.ts @@ -62,7 +62,7 @@ describe('e2e_prover_node', () => { }, ); - await snapshotManager.snapshot('setup', addAccounts(2, logger), async ({ accountKeys }, ctx) => { + await snapshotManager.snapshot('setup', addAccounts(2, logger, false), async ({ accountKeys }, ctx) => { const accountManagers = accountKeys.map(ak => getSchnorrAccount(ctx.pxe, ak[0], ak[1], 1)); await Promise.all(accountManagers.map(a => a.register())); const wallets = await Promise.all(accountManagers.map(a => a.getWallet())); @@ -74,6 +74,7 @@ describe('e2e_prover_node', () => { await snapshotManager.snapshot( 'deploy-test-contract', async () => { + logger.info(`Deploying test contract`); const owner = wallet.getAddress(); const contract = await StatefulTestContract.deploy(wallet, owner, owner, 42).send().deployed(); return { contractAddress: contract.address }; @@ -86,33 +87,43 @@ describe('e2e_prover_node', () => { ctx = await snapshotManager.setup(); }); - it('submits three blocks, then prover proves the first two', async () => { + it('submits five blocks, then prover proves the first two epochs', async () => { // wait for the proven chain to catch up with the pending chain before we shut off the prover node + logger.info(`Waiting for proven chain to catch up with pending chain`); await waitForProvenChain(ctx.aztecNode); // Stop the current prover node await ctx.proverNode.stop(); + logger.info(`Sending txs`); const msgSender = ctx.deployL1ContractsValues.walletClient.account.address; const txReceipt1 = await msgTestContract.methods .consume_message_from_arbitrary_sender_private(msgContent, msgSecret, EthAddress.fromString(msgSender)) .send() .wait(); + logger.info(`Tx #1 ${txReceipt1.txHash} mined in ${txReceipt1.blockNumber}`); const txReceipt2 = await contract.methods.create_note(recipient, recipient, 10).send().wait(); + logger.info(`Tx #2 ${txReceipt2.txHash} mined in ${txReceipt2.blockNumber}`); const txReceipt3 = await contract.methods.increment_public_value(recipient, 20).send().wait(); - - // Check everything went well during setup and txs were mined in two different blocks - const firstBlock = txReceipt1.blockNumber!; - const secondBlock = firstBlock + 1; - expect(txReceipt2.blockNumber).toEqual(secondBlock); - expect(txReceipt3.blockNumber).toEqual(firstBlock + 2); - expect(await contract.methods.get_public_value(recipient).simulate()).toEqual(20n); - expect(await contract.methods.summed_values(recipient).simulate()).toEqual(10n); + logger.info(`Tx #3 ${txReceipt3.txHash} mined in ${txReceipt3.blockNumber}`); + const txReceipt4 = await contract.methods.create_note(recipient, recipient, 30).send().wait(); + logger.info(`Tx #4 ${txReceipt4.txHash} mined in ${txReceipt4.blockNumber}`); + const txReceipt5 = await contract.methods.increment_public_value(recipient, 40).send().wait(); + logger.info(`Tx #5 ${txReceipt5.txHash} mined in ${txReceipt5.blockNumber}`); + + // Check everything went well during setup and txs were mined in different blocks + const startBlock = txReceipt1.blockNumber!; + expect(txReceipt2.blockNumber).toEqual(startBlock + 1); + expect(txReceipt3.blockNumber).toEqual(startBlock + 2); + expect(txReceipt4.blockNumber).toEqual(startBlock + 3); + expect(txReceipt5.blockNumber).toEqual(startBlock + 4); + expect(await contract.methods.get_public_value(recipient).simulate()).toEqual(60n); + expect(await contract.methods.summed_values(recipient).simulate()).toEqual(40n); // Kick off a prover node await sleep(1000); const proverId = Fr.fromString(Buffer.from('awesome-prover', 'utf-8').toString('hex')); - logger.info(`Creating prover node ${proverId.toString()}`); + logger.info(`Creating prover node with prover id ${proverId.toString()}`); // HACK: We have to use the existing archiver to fetch L2 data, since anvil's chain dump/load used by the // snapshot manager does not include events nor txs, so a new archiver would not "see" old blocks. const proverConfig: ProverNodeConfig = { @@ -121,34 +132,34 @@ describe('e2e_prover_node', () => { dataDirectory: undefined, proverId, proverNodeMaxPendingJobs: 100, + proverNodeEpochSize: 2, }; const archiver = ctx.aztecNode.getBlockSource() as Archiver; const proverNode = await createProverNode(proverConfig, { aztecNodeTxProvider: ctx.aztecNode, archiver }); - // Prove the first two blocks simultaneously - logger.info(`Starting proof for first block #${firstBlock}`); - await proverNode.startProof(firstBlock, firstBlock); - logger.info(`Starting proof for second block #${secondBlock}`); - await proverNode.startProof(secondBlock, secondBlock); + // Prove the first two epochs simultaneously + logger.info(`Starting proof for first epoch ${startBlock}-${startBlock + 1}`); + await proverNode.startProof(startBlock, startBlock + 1); + logger.info(`Starting proof for second epoch ${startBlock + 2}-${startBlock + 3}`); + await proverNode.startProof(startBlock + 2, startBlock + 3); // Confirm that we cannot go back to prove an old one - await expect(proverNode.startProof(firstBlock, firstBlock)).rejects.toThrow(/behind the current world state/i); + await expect(proverNode.startProof(startBlock, startBlock + 1)).rejects.toThrow(/behind the current world state/i); // Await until proofs get submitted - await waitForProvenChain(ctx.aztecNode, secondBlock); - expect(await ctx.aztecNode.getProvenBlockNumber()).toEqual(secondBlock); + await waitForProvenChain(ctx.aztecNode, startBlock + 3); + expect(await ctx.aztecNode.getProvenBlockNumber()).toEqual(startBlock + 3); // Check that the prover id made it to the emitted event const { publicClient, l1ContractAddresses } = ctx.deployL1ContractsValues; const logs = await retrieveL2ProofVerifiedEvents(publicClient, l1ContractAddresses.rollupAddress, 1n); - expect(logs.length).toEqual(secondBlock); - - const expectedBlockNumbers = [firstBlock, secondBlock]; - const logsSlice = logs.slice(firstBlock - 1); - for (let i = 0; i < 2; i++) { - const log = logsSlice[i]; - expect(log.l2BlockNumber).toEqual(BigInt(expectedBlockNumbers[i])); - expect(log.proverId.toString()).toEqual(proverId.toString()); - } + + // Logs for first epoch + expect(logs[logs.length - 2].l2BlockNumber).toEqual(BigInt(startBlock + 1)); + expect(logs[logs.length - 2].proverId.toString()).toEqual(proverId.toString()); + + // Logs for 2nd epoch + expect(logs[logs.length - 1].l2BlockNumber).toEqual(BigInt(startBlock + 3)); + expect(logs[logs.length - 1].proverId.toString()).toEqual(proverId.toString()); }); }); diff --git a/yarn-project/end-to-end/src/fixtures/dumps/block_result.json b/yarn-project/end-to-end/src/fixtures/dumps/block_result.json deleted file mode 100644 index 571794253af..00000000000 --- a/yarn-project/end-to-end/src/fixtures/dumps/block_result.json +++ /dev/null @@ -1 +0,0 @@ -{"proverId":"0x0000000000000000000000000000000000000000000000000000000000000051","vkTreeRoot":"0x0682f798cbd4d79c13a2f650654b14b1c1f90cf56ab0eb53f2303137316fd110","block":"168412281b25723f31c01d87dcbd5eb4bfa1c9d497054276811f7b1b60e8a9260000000a000000000000000000000000000000000000000000000000000000000000000200770f3c19522567719f7c16a16168cd7d360a58a085351d24e19981ced404bd00089a9d421a82c4a25f7acbebe69e638d5b064fa8a60e018793dcb0be53752c00f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb14f44d672eb357739e42463497f9fdac46623af863eea4d947ca00a497dcdeb3000000a01c4f87a8c97de8261d1c8917492b3f4c52a4f7d62783a50c8068bc54a9f095f2000005002164b077e0d3ae48b923af55339853f6077a68b50272d8414a9336b484a7fdf3000005803001c6ef98334f2cc43764ea7d811fa76f1ae4920f794737f04227ed57f35a9b000005800000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000066db32e400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000017d9770e08ec6a88f0e5308085c1da0e4271d46ae7a9a42a52d7967aaae9da14ef5a24a10000000b0000000200000000000000000000000000000000000000000000000000000000000bed2f5e000107aa36912351af5e47e85b6bde747868b34cb09daccda474616d09075bbce1780002098021189e3f5a9a448d3283e621791b2a280964f8e7e57bddd10f8faf79154b00000000000000000000000000000000000000000000000000000000000013880d87e811041d9b7f4747717d508f7be2570a92a24dd1f7572e871bae1cff83d800000000000000000000000000000000000000000000000000000000000013880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000040000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000bec47b00207d0acbb35869b1b8d7df8f9c9ea217f9cad8f9a4b2ab2a655acfe9ff672019d0f8434eac2f39429fc5a560045e27c6e272a4fdce8fb4f891a2abd2cfa4f72d2022cf34eca3766f7f0c15ed93ea433946d790d54ef538a51635bbe7b9fa7d09cc90e94192696ac67a8c77aecd3540d3654bbbf79a4a52bbe350557052764c7068600000000000000000000000000000000000000000000000000000000000000000408000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000000000000040c00000408000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000088560f0f994ba3f5bbd628798917d2603cf3459a90d8b2e12918ee2364b712034c8a99cb64ca689e91887a76a4704ef83e800a49fba0d4b8c9f1db7bac462020b110f454eaccbb0080fe5e8c2ca5e8725a1a4126a8428707c21475b05c6c879669ffefa13c2e31ed4fb7ede224b5271c5616d72847e415d8782620edd49dca52c6920644ef4b77f85abe3d2af9bd5b3c3810a30d76090bec7ed9df0a815b0544aa227930a27ae9d052b02cce25d703b392a48101fedc789dd9e93406f944014858347901598744d5c8aaeaa8daddb794023ac23385c49bdab78bcb8b3a7c1068dd8a545be5829fa9ccf3c28ee45be5ddf76e1c446ec57d422676056b7ea388f2ee1003dc5f085642323ac9405fe528cda36331f6f5fb0da478f0f4c6b79a76584f0bd2a67ec965743ee2edfb9a8dad254354c72aab7cad1add5be6f9189a797d93ca2dc84be3d4dcea7a9a1df295e9b690387f99ecbbd46cd35f036795f60ecb2a6dfad512fb601828602e62b0d4aaf54019e0613eb2882582dff3903d609bea13742e186e1a574ca42caa496f05a6e7cedc8f1b706ac27b179e045ab1c87749806aa19d6e6c8ed0957098fa38c99da5019278e0f86bcd1f83e801e10debef5900000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001fb5cfc23d13c58bccbe4aba8f1b0649aad5f47c67aecac3d288dd72040d4b46cbce4fd84f3e184a7e0b995ccd758577cbf2e2516e369479063acfe2193aafc1c07cad0765fa4afa491214a84cdb36a65b21c0d0011cd24c5118b2e2132d4d6eae2923a211286d0d5106a9ef375139435ed7423910967724d33cb2c1455eabefc39395d3ca5482f4450672e1d7733e9737faa3755756eb92435ae5524bb4cc43a6f0751e92c8a78015099e97804fb3e09df278f828b4f2df39969e49dd42919fa9bc412ebe799302d7147ca7c3057f0e13d0dfbe8a0a799603cad60db29bbe587e0235dd3c271354b02103e9e3a3b0fec304fc07123331fe8efd4dee89c70a04707907e8a4737bc23ee7ea6aa737412f06adbbed64f3f88cab2b9f25e342bc37ccfd63ea6c2ea22fa0236863db4c659bafadb24041b51013403d813b9c923267629c4d21becd352980b84273d42320b8fe7b132c60e508832570fe23906ba61329934e265fbc4bf58a6a1c1b9e3d8816b6169acd318b3c5ee6083d2e41836dd5cd288d10367253af00a383368f798df4e5456986a756f2476c50309fd71741ca62d125903345799708ef0aef33099ef6e618282cb942e63bc0b5474e4ca651a20000022800000224000002200513f23dac12350cd76868b8a3d05065c3791a3789383d1fea9ac3ccc3be7c830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006fb08e47bf4a5bacd50e4a162b67d37ddc33cd3aa7216a9e8ff64e301e30df68a39e315731c6cf7ec6fc433a3b8c1a5f0ea486d6b693c1f4180b57476b9b2c0eb87ae8203a12a3a53798bdd781187f28888ad2c4d0cb583c9102610fdb527ad793c1bb58f0542b77d710c2eb23b5f41c5fa75139cf5cbed830552adfd309ba56e4704ffb7d2f1a181d88a4c29dc8413fc873a5c127e221f5f0925613f06fbe49af6b243238ed369489e0e5a6e23f81968ebc1b45159de50996370730538a24316bffe41cc183f5afbbd630f8a6c1bc1d56782546e63baf390a9a95a8a4f707412f2ca990db35154b0eeee962bb363de1448b249cff1258e0c3964e5b922ea23e967025a05b79cdb949cc14156b74e306b254d304ea54662171c68c622e36687f3f8b029a40fde2493e336aec8e03b1b883bb11b849a100a30a591f805fa2e2a65d9b6aea0a78fb16047c7aca474818d342aec0e71d695b7e20af06a78e5f4052f6173139f83a3f183995062c4a7e3212c9c48ed0581909f39b4fe3f4a834c9f3be24047a2226fe7b78e7974521c71d114c8b318e93e3573a1da616d90dbe3d9d73963df0e3355a197feb54545ddaf20e376bbb0ade6b0e7c693510d1da27b9a0000000400000000","proof":"00004244000002120000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000006b0000000000000000000000000000000000000000000000000000000000000001168412281b25723f31c01d87dcbd5eb4bfa1c9d497054276811f7b1b60e8a926000000000000000000000000000000000000000000000000000000000000000a08ec6a88f0e5308085c1da0e4271d46ae7a9a42a52d7967aaae9da14ef5a24a1000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000016e43c5ced27da8b6f2fc788451dc743bd619c3763dd038512e1e02e4c8c6f440000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000066db32e400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000066db32e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017d9770e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000682f798cbd4d79c13a2f650654b14b1c1f90cf56ab0eb53f2303137316fd1100000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000002e3059a3a513ebd2100000000000000000000000000000000000000000000000acb31b01e879a466900000000000000000000000000000000000000000000000e4156ce7e68f8d05b000000000000000000000000000000000000000000000000000207a06299fa9a00000000000000000000000000000000000000000000000165ad30b03ce3eb2d00000000000000000000000000000000000000000000000a3a7147ddb3b9458c0000000000000000000000000000000000000000000000016e5155e9178c65c8000000000000000000000000000000000000000000000000000068b23602e6fe00000000000000000000000000000000000000000000000f6efd39e580a3e6100000000000000000000000000000000000000000000000082e36b8da2b12bc49000000000000000000000000000000000000000000000008ed75e19a27997c5900000000000000000000000000000000000000000000000000017cef742b6868000000000000000000000000000000000000000000000000fd810553a9e73f3400000000000000000000000000000000000000000000000b145e0a2ee85822fc000000000000000000000000000000000000000000000003b13582511a99c9940000000000000000000000000000000000000000000000000002865bf9f95be9000000000000000000000000000000e890e23ea66995ac0cd157ad66203420ba00000000000000000000000000000000001c001ed135ba5936f3a916a97177a900000000000000000000000000000064678cfe06a8015932a3e281645a534c0200000000000000000000000000000000000675ccb0e38833042f9c8dabdfbbf300000000000000000000000000000007ee7d86d5d35b8497170aa93149d6ea9f000000000000000000000000000000000017a0ff465881fb66307b71208429c3000000000000000000000000000000fdde0ed9f5c731c7f476c7df130e7e4a200000000000000000000000000000000000017f5ddf57f45a9aa6288b3feb101800000000000000000000000000000027995bc3cd9f6ffb368b2d8524bd9b802b00000000000000000000000000000000001cec85cc344dcbf75dc4b68e640e34000000000000000000000000000000dceeaa901d6af7095576c5f01243416eb700000000000000000000000000000000001f0de24acb0d0392930bf4336c8ce8000000000000000000000000000000c3562d3de20254558799fe1cc6dcd83d47000000000000000000000000000000000012e94fb56eca2710e5ee89f76ca7bf000000000000000000000000000000c8e15d87aa3d60f470e3f45067e43f8ea900000000000000000000000000000000001d068a50ca319ecc7c91255068c4cd000000000000000000000000000000f10600b8565647170aba4864d7d764729600000000000000000000000000000000000890c5f08938a81e9f7749cb8d0fe5000000000000000000000000000000c5236a4737f4ba302ae6a186735d6703cd00000000000000000000000000000000001245132eeab701ce68da11c25e6f7400000000000000000000000000000058af1b0a892816a68b47f1d913396c02a4000000000000000000000000000000000007228bac9d853b2aaa5a424bd20eb8000000000000000000000000000000f18e69117cb80bf7fdea3aeeff5dab8d9900000000000000000000000000000000002c015dbb072d722403f4728fea05aa0000000000000000000000000000001156803aabf9b319bee320bbef73efaf6600000000000000000000000000000000000453bba56f5d72c28e338eae272c17000000000000000000000000000000c299833e9f322019bffb0bd9e75656a60300000000000000000000000000000000002330e2edafd8bbe9a0abb1cd860440000000000000000000000000000000372449d66cd4cc5db696dc322f7ee3012800000000000000000000000000000000001583bdb63325fbeb57bda9e2ff70f8000000000000000000000000000000be29e683f6ada33319ac9a523407fe264e00000000000000000000000000000000000de6b54f7f3d9d54d36e2961fa376c17b7f860176ee3972e7cc5bd5511f9c2abebff7e18143725b603eafba3a505f518ac5612c9c2bc9289d37ff92c6f5e9a7c47e8ca61a5396b8dde0a984c5afa0c20d87055e7d7872895512f79682588689bd04a7053bdf14c54973d913c2a8ad11c627a0c55d075e872109b2c525f2c904c860ad67e74228e47dc4e1082321b1117f1d69d1069d0c732313407ffa64e4d757689ec7df3b6d6e8dbada090f4f7e40ae8d5151e9b492b63f3c92f8f8276201887061c7b34bf1d8a4be1c6d1e8c2790c07331c29013c619653684d2d56af2b01f123f6bc659b5caf2e30d8f13c2d4628a582d3882a9af99e1216411f61ede2117ad47124bbfdf2a2c34cfe8a3ad55801431292fae6ae7152f2ccd6a1e97dd6a006c867c79ec9a88cebde40cea2837814d566cfbd8792b59aeb248ad90af535a01b91ea94039e4d73e246c68427fe320f89d71c640a23c60f5e98ac5951595bafd93b1cffb420994a5281456a5174602f6a07795c2b9df7994e0429c1830616218fc6c0a43a2013a934bf1ed6a83851294c4cf42781ba7ccbcd0c874e14ec3a73e76dacd2f4fa4c97ec61f681ff5fdd23c8c84c119e4e874e80e11b68a8e564ed6d5b3957023c8cfc3b085781fe59c617dc32cd82fe63fa9cdcaf5b711d9b5cb6584e97cbbac645c7b1964134c52e1e26bb7e9efeb4c2143181f3add3dc8113f124ed3bf393590824af3b1522dc3bf626db5481496d903b8101a71797aac69bdc8296591d77177ec38eb47d6a03ef5e006752754121fdcd97edaeda2fc4eaadc34598310fed1e51817b403bfcf8ace526079895e0b13bb1b39d335a3040e6bcd9647559aac3a9061281288859fd278d00ea848fc7253a53b8e12e578e1dcf176b4ab824fd977dd9d00fe1c644d1c83b2fb9cbf6fe97157f7ec36ae4491ce00589acd2404a2eeba16c1d2eba111806080e366300e8c7176ed2718dbf8fdfd48bb3898ab6b0d365b0322aac89562eb20518f27f51f4ee9a02c95e01e959f8a3a52a62de398387703dcf8c17e30a857d6f042a273ea54f5b686b0d83c242a2736ac45e9213d73a6ac3e18bb15016d9f9e8274239f20372290daaa50b8146c7a8bf1b151da1e3ab8d6366b655299b3302aa2b3be763a558172f183b9ef49f9bd423402d74db715c465cde710586c8c343d30abc860c1690ffafdc4caaf069ba2b47883a5af98f542e2c592d0e52fddb6f3d198e4fd8c00fb5294eb59709c928197f5cdeafe1e5a1b8e80a07a225cec02dde2d01556017cff634782415df3e0d2a366af18ee7d59610fbeffb8345b05546f71075899a23429760934844ae9fd56302dfa82e98a8be785f1c5be63885f1a2fd1ce066b240980d270630eb6fbc04d8389773651b293c143781305c42e5592d2802feb21684df8dddf3e437493d17d3b9cbdf47c1c916d7f2bd49ee3e766edbd626c47b3935b3603eaddaf1d7844a79b3163846ce49796c740021316f6c6ac83e076375c457b32bacabb4e21c7b99086b537768b9df5b8ebafbeba49b6a7f675f216904f2f790a0d90ebf52f9af807ca4ba4fde03334e500d21e419967a16f7d1300a15a76424279985d17286ff240ac9364c2623009f05c75f545bfba3e8baf202a79752fb46e21f792d96c75374a13fa33bdd787731835c83ec50be62dcf01e1cef40b75c12e728067f0d8c79f86bdbec7e63d8b157ec51a8d7876b5690efd30b98d76a073bfbe9485347582533dd4ea6ee29342876d1c53a45783ef7cad664090b57d866ccf481882059dbc98fe30e355a1684c1d3990ff36c555b85c0d52905d6a79b9ac60fae15d6902822f6f9a76572f46353e660d7da57abd11a71f3bc223dd3794e061e82130d9a7e452574838e49ef163cd19b0cad58fdf71b186875112af6f9281fd729e25f7314dad5f71321ac1b64936efd29253abe195e91d08b118bba2dd001287b9ddb54184cb7f20a77161599dd5b606bea6753b403de896716081afb6ca45dd1c8304df51be9b4fe53d2866c7318ad79db7bdb45ba6052e919cff7b5a11c4c274f1e7d6355034b909767bea8cf7de1ce7196d29eb5bc95a908283dda4b0fde95822093cc1ab63a35e1d40c1f4b231cac8c0359db70fc48f522ad94da4eaf4bfd93793ea2f383e80d14d15af940696c219c1bd8cc568d0abd11398622c040d733c5f81aad86abca86cd3f300a93832cb76fe0b5058492bf382675f859089e42831c53d1b8c675d36678979d1093f4728bb25cee192d3847db029864880302591ebdea08f4f6fe7e9ddfee962c008199a7ab5c38e093adf4411f62c0e3e8f4ec3d9f96ce4dc500ebdb2798f0bafae904da10fc171f69f13d7318516b67a1c414709e87bb4b981cff45d98cae9ae00a04fa5d6385f1fda248f126b46a97aae540020825d888c310c3f92176a9d279c8a044809726f9c6d3f86114f9317b53aacdfcdd75b0ce2aeac842b0b92ab8b7feb057a43c63b4a485967929496a8af1c25fff6e8564430facf472466d2476784ab74df515c150fa1be99d13fe388f77a03cb39148a389ef0036f89d9fa8845acfe5e481a7e4fe13b4d8160df767c37d1c36737b18df2cb0ddf710238c36e9a99b2d6162ea76410276416c1e2ffd4f11e221db4c5629cb516ee415e1524353092846220a5ccbd3f277f822039fa4197b3b5431f8ce16c2e60cfa14b36ebded00b859098f62a85106852ab1231ec238d4a775ed1dd411cf2c69ffd0550672490c6bffb504b28b014a354cae0a7a1cf1a50877d89c8f927a01270ce12e2842e058dd9d1ec8f1a8ef6177082014c6e1da7c984549c5405e1f02d0ddc1338ddaaf1424a1faafe3bb37dc1c60d51e29dccc27c1ac3240382e98555c265df7fe38d8a169adfae9b8e43aebc68bf3027af160f84ce4f5dd25a0c01af01d20b6c28bdf0870141b755050d4b848163613fcd1107536e6edbb270565f688b9a316c9be1cb78cf45b2b015f22425cf83d1413cb6685900b5daccecec7db62c3a444c89174fafe2484c042de9bbb9e64462f7625d117ef05575a1fa62d9e32f7423fc64e6ab89073ff2d64b382e1718cd10bffd54589b6a7d70eb86210858b84e64648450071c30213a0fe8739618fbd450d0198103d9087861566f436459362b131fe5138dec463b2596181b30df0e97d1b54c0e8f65e2367c45d41c7147b92603aa49174e96493238420fd739c8b473f1a6c805e1261159df95b55b0f25a0e1cef428912e19fed7d985152fcda7c71161273745a2f156839a1e0cdbcaeb1d830017bf30e89e12286e405a5ab3ffff3b6249eea7e5576a3d0b755193cf5de3be6739bcbde111746fa7509a98560f4b32a1207c5704e49ed9082181a85da3524fa0cd9182f20486abb7edf5e55cdda4e9d1544865b4f6d1623bd5b10037d2774bec886400abde4efe406439757c69333111234eb078dbcda1dfaf5799eb565f1ce2f74a19141f3deb171e82d2f07354d8d0a75afa7c749ace21add83097ef9ea0f43a8b46ebfe35abdd2c4346d8a7fbf3c2310e85b02b9665436bfba3b2f5913aefc1aefcf483464acc705c7bae16e3c0f1331e3e99164c0bfc273ff0aab9c9a290442ce82c1f7b59c47c52dc1dea65aea19c31df3d83e6cda11275929f992a3bf65da098a15462c7802c798085aa6da531755f409584f67e4e1fa3a8a1c49e8f9738e059da7773bc4764412155ce7695a06de4f09d47eaab8410674490f0f7e753c2de2728bfdead8365d01c0dc35a2e21da26c3f00a662c03d1fad9582802aa492de146a5cf7b33ae19c6c0888f7b3f10382783475be591fb2cb4413f10bba5ab8a3ca700ae606ab10023f3955c6e6db2d489c0396b3547b84e5dc9e3e93b43540c33ea6434772169e43110ed5e13cd4041a4b364800e252f48987f787cd25c49f8e8a541692de892522da6e4e96e64d267ea0c5efddb96fd3b98789005ef051d7448f1294ba417834ade96bd21f621e15cff207bc6b81b90b722d2914ac9a4419348b1c7c066b45af6a325e056cfb1703ca4768f96522dbcf8ea19c0509edc60b9987d845168dcec1c6cb6ef85169420baefc83e3689e63e456ebb533aa5b6c9b6d90cc033ee9c14a90c3ac7dda6ed2205c946255cc7d340c7a35687fb4ef5de1829bd3461e5a2faab8349276bda13d1462d49dcd5e6f9a36368520b6c7c1752b06ae0a0506565bc1a7d8c4c59e139b0d330d82aa9c303eea7cbb7fc2224d9f95b2287a681922425bf6ef7f743f71ed2fa2842381c1b2949a651e1bb299dac8e29ac9315b992664e5ba8a9b3ca3906b196efadbc05ca4657dd0ef2cfec897b01e70f868f1305e8b75da5bd94e47c1632094c7a36aca33f4d3372b2c50e55d2d15902fbd05a4e8fc9bb5d0f48e74acb2035095fdf5e00b5b74816f7a5147bd2209e1f86fd6e7380b6c7a0f21072aead00543fd3fa0d91069492434b5ec156790d9771280de3da610aee08984cfc1f81c066539a009ca24c0ad28eb8fd8d308648f00b4570c5b21f396d43d904b2cf1fd2707efd6b88459b769bbe2780a0e40eab6f75fbbd78a8ce2e314cc172913d58727400972c7f660514196631adf83977f9061a134732fdd95ef8d5d3947a8a01f0533d9f7ef930f5949c1c1c653c9fb90081643a100aa4ec653163313ac57aaaa1a61989b08984503a728d78353e244cf9808732c64cfc96aeb9e819bf10c0a671ef93e145144c4bf20f953671ab48d3f59bd3a25bb11813d634aa75a694919b6159de4f1e2baaa6c167aa26eaa59db15138e13329e5b1934f8955dde5366245d0247aded5fd1b78c249ce2b2f1e78687dc28da8854ecda9c496a1d4c19a0ede52513e61a5b394a643dcd6f130de3cdbd2db36030a7791baf39576bd2f54605a317833a6872380699eec46280a26304bc49a0f68c79db2fe44abd20b5bee690d92708963fec33f845e5720c7a25b7fce90e9102899172fab1ea26e6cc845c437f0e5ce793ec5d3948acb35f573c31e78e75b68fc76895e1583c710aef41aae5c90481424c02846be8668ae730f76587968037e34e522ebffeedcef3e81e0941e415c618ed6b810867c20facbfed55ca333d3a50c482609698f5b95d21e2279795295ce0e98d92fe1d2b43c42ed5559c6f4971ffae3329491473121aa7cb794ca00b7c3e7863507e82276fd36ffcf3c43d23e47ce5536bc76aa398368eeb579e7f11853b53920633bf35a45b9dc1152e54da494bbfc93077ee3099dd9a881850b02e4343e6772cb70201912893f7718cf3d9b7e0d0fe2e1ba6f1fdb2557db6f1f50a98771e423932f665fb9147123b4fba2853d93eb14152ccaf7aca70a2004ffd2457af6073b63c0efb1bbeb3940a0c916850fc1b63af7dff2b8650c02feff9be090222667949c1ffa8914d84037352820a9217cc6d8b5b86f52885eab7d6d47e267c0e4f355115ef7bec78eb1bb9b07f03537ac96e07a32024da4f61e32c6e1a0e6ca58ef7dabc94adf7f49167a946e1773314bd07caedea2b68ad18f9f0fc0d0cf941c6e7859e25aaaaeaee65f741bcbe44c1cc1f96ba18af294aee5af9593d04d4a99b71e196c51238d7dba3c1b70781a3f006e16e4f7c9a0dac3b453787c40f48eeae19d2c0a72762fa9a8e6b5aaddeb87c68c69e64f441d5ba7645531b431a787ffbecac74ed1de159491427b9b03e42b10fe1f98e8a4313f264d4fe067a05a3111e9f466f1738f37512359547155116970b042fc44b8ad98a7cf5dd00cb02e1524ff398f782f37222bdcb828b5dcfb4ea040ed8c8f3be119ed63625fb2e2a77380c84d607566fced8b0d3d8e909d9ff83f01527b190e40015a3d7def6d70f22fc9b4d8f06fdeee2c95c699ea8f8ecee88fa13c27cdf9c9fed4d38843f0019569ff7b02007bc0027f0a57c6b0b9e156f434a896d71b8b799efb621635a3512119ec00b23decb223eda213d5c2401dd1f14777cd6d612f0f7594d2cfc5701286f01cf5656ef4973e68f7df3e3b5215872b570d631eba74f4c55b27a4066f42fedf79141ddf98f44d7094dc562f62a590d88bd287429712f797d9658defa82229e4fd6f4f37b9f6b19b94470507f269b4a1473231d28e642946d95afd1923e082b35ea2f62f7f7b72cb5f365b08fc3bfe253d5247d6371ad6251a48eddb009177ae9cd062fbd258fbe07783d47fca0b290167da10536c29e4c86dc0877be242ca1b1ebba3fe8b526b6b5a6255f9aff4cecb7ece457020ddf108575355a98c2095bfce79a9b6a8d5c3ea400cbcf3f1c5228d4f14c4cb1c133e3ef9606daa69729bd87eede7202b55af964ec94c16100acf0045fceb020259acf5372628108a52dee8a37400289210ea94fc4d04320d0438e5446c61d9d015256d6afccb3cf1e1d92558cd7cebf73a10361e81e150ce610b06090910d19178cea3e3bc69cb9432958f2188617b97072f509c28a32a788615d4d591a752be53e55d89e80ec6bd72017be7dc9d99a3a2bca785c86ebc7539c6ddd18c1a0a37d8a918d316edddbf8238863eac1ffc39ff5017451e61d76e7d57a5c90a6e2b4964ce727c02bbe50e8104c0a87a31fc214b6f6a2e444fa3d5b96615262c059074eb41063bd0637c22c1591b5323e36d5acd765426b4f3b4fa2382a9cb45b9576207f780e82c5a4a3a204d4ffe24ac6810abbab362beb90ca637c6311a44ad31d6e3a868308e432feef18b2965854badd7ad634057f2e60258fabcd3c83d18de3031c4601783e6c8240085eabca90cec30013d00138685cc104c58e6d48e80b5582f4735a389cc3b781175b46dec8cb5249ffa38c7d54ceecd23ae4d3887cdd21b27d14982596004b9d17c6c69f79ad3cc750aa3b78bfd218170d5878c9332d345ba6d353cfc01edd2e133cf75325c79370a28117fa6ad8dbf04a0058a925c5f2ca117374c853e193c62355120f880ce444c81ac3d6558a8e9a345bf92aa75ea7d285e67bf01331d1561559f8502fe8d1c4b9150b91c5fefa5cb1159dd4ebdc25e9360a7d4e868a370b0fba1eda0da1014808d4e2ade1a5c0166f9076d99f36525000a638e03cc5936528b9341ff914e119da78f92a7355f42db985376707aff5609be44973ba5b926e2405c4715e456b9effc0fa6f000437a0bd37c04439958c647708f5b0d89acc5e245dc396aae5e61a3167916efeca48299130de7a9197ac5c91f41a2f8134a8d10ec0adca399237bfb1da227fc2ce8f6ee5a7b8707ca6cffed2a5bb6eeb8fbde91311dcc425d7a1c594a129963fbd366b34291492736a3e761f977c27e8140056287b33ac0a5d6cb9d5515c988fcfdf46b046e3800be37b85bd88877c4bb9e8ad1f1c2f1ce028010d6dddad64f7ec693353f5a69f86fb61b4fdeb60befc012fbf17250a793576c282e64ce1f1a8ae1b7cc1ca582f07dfb8a62a7f32d005592d1c18136f8ac60d1e0a0368da85a34337cb8fb61f9b6c93b945f0b694f8fd5c17dd004063c77a4235b56485926549450ce67d53211b80d8e22659d8ec39adc1533730387aa61745575304a44cdf5e46ff334bc1b5739f44e22955ea6cbf59ebe6450720a67099b41e4d3806a19b88b863742b1305c4fc80307f271380aeba22e949213da42eb6836f8a2477f025170295f4762ad8afc70384a829eafb2c330c0eef02e7b4a10f1880bb9a52d1b283d7ca2314450ff2974b5fb4246c2623531ccdab1a77c3b232427bc299b76f7380cde37298c1a684ac0e6349128a15afb4ee5fcb0524a99c8f4ce54a1c005e257baf2e31c4442d1cb1f42c336ee7d7d6b2c19625034a1ee973a46b2bebe839e776a66cebee1a741f094b7f10b58812d28508cf442c14e9e633d6e2640f79ff203d77caea167e7b7efe21ea52d2317df3898a0ebb0d3316f16bf92e3570d022b9bd5cbb277b709469ab03dc8981002862d232703c151039d50e1721e3f4b5e53bcc0dbee038c8f443ad769773ca6c5b8f0475dbad0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aedd39c45321954a31a94232c1514fc2d632d6f1fe182f7f127654038a76d8f2e1cc07f39c70c7a87d578756db548ebbd6c17bb0319b5b490e2d39fe55652ea0d9e9b4cfc7cdfc182a0b5514d0e3a583b499ab342b4cc5d2536073110aa7cb50741f0550dec2646df9f3e68cc6ef12bb85f34e1e74ff59bfa84806e4e4f419f2192a4130a392ab8d1caea80ac2fc4b39d125fc1ef06374e463ae27414040c962ed1dc5d1756a69e788374dd3716c1efe9e40f48547ff520dddf440a12881aef1c28d881ae2867c83c54c39c0143afc5e89cbf4f921577f6fc3151729ff846d728b1e554b1f115db276ad487936bd19d596c7bf889237e3ec4e4c4ee72af76730a586f58b60b9ab4e8c87ca91344a7ca8c81171a1d291e6c7d9d6c3d31dc18c61ed6d19da10b0eeceb81f44426ddffba12d1135825ed3908cc0fd1648a04544e29cd60006839f4586321e6f3eeb651e71223a0e4f1cc3d7a921e78382210d00b23950215b85405f7a7c5381bc3bc4dd6a953cb37a74e20305c416d411ba1c9130829350bfa6aa9998057922d982b2ac617b501372b988de766b247b4e156fadc08b813a22d284a4b5b7b6974fd7fbf0f694155bb82010de7e31c079f2ae027772d09a35579c1fdbfeb9a6f97716a936cd98988291ac5ee87b7a719e371ddc52b0e06e29be3bad2ff8d82b19db6a5003d4defbeffca6ed2b4c85fb234f7e5e3ee1dc2e039ce4ea34aed5500432bfba4e67117442b46302b0b17cc8fec23c3983b076be47f23284da334b5d02a4a60124482dc41d70492cce003c854334bc988cf0a1129a2c793c04576e2011c4e39ccb6dca3df91755e24763597ba336ac3c2a000c897ac8508951277d9578d2838c0865ad9340bbb3fe17e00155ff6e5c172880c48fa15afb7eb2d2d45e987d8f930b389fa97d2a577f453ccc509d7cbf89c9a084bab654462daad3297491ad6005a08dcc5bfc71f27e86a7898c9d1971337890c6ea6968ed79a9eb09eacdb1f1b300b887eec3d79dad17899b959be2e2934c608cf74eb496ee77f75e21ec80abf489f18685aed9cfa5a65988d678f3a10b68f0385bfa953fa078638a4d0bb697e2f347323c1b7582f6512826a3ce6d437b4e50e44ca62869e9d1b4dd8e7cf50fae25136050ea4642142fb537485989ad3aea121992bf4432347ca2fdb606a89340a9fe2612c5644e0004b84207b92abca88702b3f75bc130abed3704261e0e0c7bc6172d315554f60b7be40ebd250f89ac7e4087e943193c8bccbb159cab9ef52af004c36575d4b33b50bd580a26363ea5c5023a3a37df568dede4fb1d2ee6000a8e1ce9c5377ab5861193c029db7661d01652dc1c49ee330a65454b2eede670e39cab7b88128c6c707605cddae6a2059abb924d73ca2402e2d2d09affe5f3cf580c40bcbfaca0216069ef0ed2455657a63921818c2a8fcd6ae7d39e8ebc4984b875f3820224b1bdbcfb17e4a89aa8fef4a88225426beca80ea5140730767b2d12dcbfe4c84971efc2cc1db4536244ef31ae2066ee0901d265ba71a95a5ebcf960b9ad56a91e125483048b1c6fc49939fa68d1f2cb09b504e77ffa01264c004d2973a5c30c2dfe6c8b20818d5ba4eee65e0362648eb1a5a239f933cb9b579d4e432d2d0f85ee0bf7fc223103fff7bf3f3c5410d4cfbe1f878e512b3f5ce26a09921f0df624b7105ef4b80095151d0480e421610fbb3b11018866d6a7c1e55fffee264d04cab0fd467157b452685717298fd57159b78d3457ebf31e21758e7443639bd1d102cfd70a40ade42024ab06a38f8c41cef250cbc560e0ba83ee7e92dbbd6166ca711736cd65146bba4bb11a4a13dbe0bbf839aee17e01a2566a72e383bb2c64737b264121468f6dbd60d4e229511dd026c8a44ee3f4fee5c3059c05a660ba74f407142c9a39ced7dad8e3ce277e518166aaf5d1c680e8ebe44da44f7cfc9d140de342682bd049cc4e1ec4243874a740000000000000000000000000000002d544172ff58c414aec63e4d5f1dfef43300000000000000000000000000000000001f9355535c4011f0431fd5d78385d70000000000000000000000000000005b0861b99d5f160f6230f9f890be2ebed600000000000000000000000000000000002712498aa068f693d353be01177e76000000000000000000000000000000a3ed814facb4a323ecf496cfe22111b98e000000000000000000000000000000000000453eacc387f1fc8f03e972e861df000000000000000000000000000000b562fd6643d726082f602da446b919e91b00000000000000000000000000000000002904ac9aa8f8c73d5d123d0daf07f500000000000000000000000000000024b01c7f327c090af5aabcc61b7744c5f600000000000000000000000000000000001af13e910c661bdaed7c407ef22832000000000000000000000000000000c5f99bec72c68505b53689e505ba5073a4000000000000000000000000000000000025d37be6c22499780a76ddead2c0f4000000000000000000000000000000bc96ca0405b11887d07dae6d902fc1af8000000000000000000000000000000000000570316b99804a92474966927a1eeb000000000000000000000000000000c51afbeae269b1d50b50ce246e56e43d710000000000000000000000000000000000112b77cb044c9747fe69f93a39abef000000000000000000000000000000cfeaaf59760d5bbc4655db3aadfea1a0aa000000000000000000000000000000000022c10efe9bc26d9b4ee0b1611e135a0000000000000000000000000000007582cf1ec23d97ba97718f2188b1c93371000000000000000000000000000000000013d8c781f121595d4e5598a4030a6c0000000000000000000000000000000a03577c6032b26766c15e3e08700548690000000000000000000000000000000000087663ffc7008c874adb639af87158000000000000000000000000000000a686745b827bfece761fe5806e76e35251000000000000000000000000000000000009e390b295946c5ac093093d77f7e1000000000000000000000000000000f48ac2601aa03434eb86554c6a549ed3e700000000000000000000000000000000001df06322fcd37d73fb3f73c03527310000000000000000000000000000001a6b37aabc3d0edb4464b9b50f0389ed38000000000000000000000000000000000022b9f7519f313e4fd502b95694aed90000000000000000000000000000001be2abb66660e897fe5f82fb08fc27315e00000000000000000000000000000000001f8501ccdd5f18745aa2201802eb02000000000000000000000000000000351aeb7da4935c09161342c7d209a8afaa0000000000000000000000000000000000225a2e0d33b9be5bb96ff1eb92d98f0000000000000000000000000000008cf2f296c72458fee42619fd34f507075500000000000000000000000000000000001e8fff6a9ae9dd278cd2aadf0e4594000000000000000000000000000000449ea257819728f0f882fa5d2e396d71a200000000000000000000000000000000000977abd3552057a0ea284e4663b71d000000000000000000000000000000065e14c6475e60e9d4861a4464e61f254700000000000000000000000000000000000c6c2d75a8189325b7db63beb40e1700000000000000000000000000000031bed94f48899760d0298269afec303b050000000000000000000000000000000000080a945d72442ba1d6e21ab43a96c1000000000000000000000000000000ed0fac5f4c80e355c0be8e66047830f59f000000000000000000000000000000000024e5c1a9a6375cc88f61d419b84c53000000000000000000000000000000f1ba93b6a3e6ef60273914a2ecfc75e8cf0000000000000000000000000000000000028e915ebf8361451cdeeb95c3abcc000000000000000000000000000000db1215215845e883a175c9a4a6f7263f4f00000000000000000000000000000000001bc43ab52f8e49eedd73ab963220fc000000000000000000000000000000b05cb9b046f7334960c1a24ca41f3d495000000000000000000000000000000000000b549fae7a60f066880d9168c3cde0000000000000000000000000000000e76311a28721dbb951d7c059ce215233a2000000000000000000000000000000000021e9da757f03a2b5aece398c8f1931000000000000000000000000000000c5c4ec6e9154a076fedc74df770919800f00000000000000000000000000000000002753f03c77aa03bb83ebc39588300f0000000000000000000000000000003406e0dd884841bca4da43bea6ed4d4bde000000000000000000000000000000000021e99f09e1ae95843b7164b7b096750000000000000000000000000000005ba04fffc36b84ebf25a49db84fe1bd39b00000000000000000000000000000000001e3e8423aa44343e612bb8ba4a089600000000000000000000000000000018cc12d2403c522eac722d7509ffce8f1b00000000000000000000000000000000001c6679071a1163f2ed8f2cdc8774500000000000000000000000000000009ec6c30ee33ffb79e6acceedb714f2544700000000000000000000000000000000000f58de5c741ddb445d68e0b103dd2e000000000000000000000000000000e11c50687be4e1f1604ee9049fe590f04d00000000000000000000000000000000001bf4b5bcfc56dc5d4e110fbdd2caec00000000000000000000000000000050564da2d316b539c0b90e67051b3aae240000000000000000000000000000000000024f95673a0d92228aea675d32ec1400000000000000000000000000000078e152955aec020cc07d64080144cd4e2100000000000000000000000000000000000bc4d829ac3c1e7e343fba8ce93546000000000000000000000000000000c2116b1ea40649ccff4f52eaaae87705d200000000000000000000000000000000000ec5de6da728c2e9464ff06bea8a2a000000000000000000000000000000dc164daa4520700db4a7ccd0da9dc15d0a00000000000000000000000000000000001ccc9883777dd2b7823c81951414cb000000000000000000000000000000a8e5dda2672f008588528bdf5755e82ff3000000000000000000000000000000000005b91f947e4eef5e064820f30652d30000000000000000000000000000003abdfc9d089300d182a4ee2a3de0ea5233000000000000000000000000000000000019c08834d097f3cab625642decf921000000000000000000000000000000e2f74bdf88bdc17abecf32d8f6b373ef08000000000000000000000000000000000015f00c84e4a25a8296891934c039820000000000000000000000000000009fbebd8a792cf6c8052ba4294d92698ae10000000000000000000000000000000000152eb9b5244862d97216b89e225ff000000000000000000000000000000022d241f5abdc2d4e66aa8d42553294dbef0000000000000000000000000000000000010bde456e78f9667d4a3626e3b979000000000000000000000000000000620da80ba4347c36225e7aeebb0931385300000000000000000000000000000000000708e7820ee84707ab5f837adf3dbd000000000000000000000000000000670890818a869ce4a038075ea47797966500000000000000000000000000000000000b084ac3e55bec281cfb12fd482407000000000000000000000000000000d1d5b9d7e75041e7ea386045e8119df15200000000000000000000000000000000001b24df43985e524d880ac83e3b903f0000000000000000000000000000001f90a496153cc5018f349982d7867d028700000000000000000000000000000000002329d015ee424259e29b2f0c9104de000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e287483358a5b7c81992f3fca50c2b348a00000000000000000000000000000000000ca6f20f3b3593f453aff040297f3200000000000000000000000000000063afa6e0000bd6381aa541e8116f7a71bd000000000000000000000000000000000015782cd812d4be6f6a93a0e3c4ec4b00000000000000000000000000000096877293c61a6caf329e011bc0fc2c9a4300000000000000000000000000000000000ed171e13cf1f16dc7c2abfe2712350000000000000000000000000000005f21b82d180b91c46a74d3f35bd0ed1e8c000000000000000000000000000000000003c25739dc307a886612157f3a34ec0000006b","aggregationObject":["0x000000000000000000000000000000000000000000000002e3059a3a513ebd21","0x00000000000000000000000000000000000000000000000acb31b01e879a4669","0x00000000000000000000000000000000000000000000000e4156ce7e68f8d05b","0x000000000000000000000000000000000000000000000000000207a06299fa9a","0x00000000000000000000000000000000000000000000000165ad30b03ce3eb2d","0x00000000000000000000000000000000000000000000000a3a7147ddb3b9458c","0x0000000000000000000000000000000000000000000000016e5155e9178c65c8","0x000000000000000000000000000000000000000000000000000068b23602e6fe","0x00000000000000000000000000000000000000000000000f6efd39e580a3e610","0x0000000000000000000000000000000000000000000000082e36b8da2b12bc49","0x000000000000000000000000000000000000000000000008ed75e19a27997c59","0x00000000000000000000000000000000000000000000000000017cef742b6868","0x000000000000000000000000000000000000000000000000fd810553a9e73f34","0x00000000000000000000000000000000000000000000000b145e0a2ee85822fc","0x000000000000000000000000000000000000000000000003b13582511a99c994","0x0000000000000000000000000000000000000000000000000002865bf9f95be9"]} \ No newline at end of file diff --git a/yarn-project/end-to-end/src/fixtures/dumps/epoch_proof_result.json b/yarn-project/end-to-end/src/fixtures/dumps/epoch_proof_result.json new file mode 100644 index 00000000000..eced8abdbfe --- /dev/null +++ b/yarn-project/end-to-end/src/fixtures/dumps/epoch_proof_result.json @@ -0,0 +1,4 @@ +{ + "proof": "00003f84000001fc0000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000005b00000000000000000000000000000000000000000000000000000000000000011200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e00000000000000000000000000000000000000000000000000000000000000012d5773cb9b23c4ed50f9fba054aceace67637c56c492104bd6cd85ccdb3ded6000000000000000000000000000000000000000000000000000000000000000030fd77c2a44e9430a2e6196ff4ed74eb832169caf335c122899deb80b805570c30cb534cd47c98f9e2b5ec2c125eddff184d68a2d8375bedb2c586db12130b8c6000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020007638bb56b6dda2b64b8f76841114ac3a87a1820030e2e16772c4d294879c3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014210b92326dd0c8842f2a4f79e2b806a75d62d98de66215bfbc4938d6aaed34000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d287c83a2e429d0940000000000000000000000000000000000000000000000042dff630f5c5325b700000000000000000000000000000000000000000000000cf2838e7f9b887b4a00000000000000000000000000000000000000000000000000005c5644fa4c8600000000000000000000000000000000000000000000000a9a62618ac5b7ea66000000000000000000000000000000000000000000000001eb973bc56be097db000000000000000000000000000000000000000000000002698bd6fcd08c82b0000000000000000000000000000000000000000000000000000079b39932309a00000000000000000000000000000000000000000000000ecc24daf882c84b4c000000000000000000000000000000000000000000000008e3cb57ab2f5fb565000000000000000000000000000000000000000000000005a9b4b319e5ff99cd00000000000000000000000000000000000000000000000000016fac74ebdab800000000000000000000000000000000000000000000000dc29b05e5f81abf7600000000000000000000000000000000000000000000000e10bdb052bcc6f47000000000000000000000000000000000000000000000000096b01b166d3ebf540000000000000000000000000000000000000000000000000000027f3e7466e7000000000000000000000000000000c715d73b672d8154c7f2d66bb3acd285bf00000000000000000000000000000000001187a5eb31a289d09cda667f4b15b90000000000000000000000000000000431839d401332b45305569e7ec18d23c500000000000000000000000000000000001c7f6ce65c950449e8ee971849404f00000000000000000000000000000037e6de1b9d468c43a2743c1b35b0786f8d00000000000000000000000000000000002bcc59db00ba4cef317c02644ef196000000000000000000000000000000129adaa925d426817339b5d852213c3087000000000000000000000000000000000003b3c3df1c3219173974211916b4140000000000000000000000000000003a693398df272c7fd539cb9c2d35636713000000000000000000000000000000000004478ca4018d3534c64b831751ceca00000000000000000000000000000076f0f54e5787b8703505531fabc7516899000000000000000000000000000000000023832b439690c8216ee2e1c282ddb5000000000000000000000000000000f367ef12bf942e511bc004b117f221de8f000000000000000000000000000000000017c4ab714573d549169b7cc15f8788000000000000000000000000000000477bab618ca21296c3aa697d87d36a75ab00000000000000000000000000000000002d1a771acc9628202e5063eed75c25000000000000000000000000000000e5e2da926b49804d37e9eadfe03b685e6f000000000000000000000000000000000017cfb773536cd88e90d84f970b964f000000000000000000000000000000dd30545e389b4c93ba7a424b240274e8b800000000000000000000000000000000000c030be9da22451073b3b2e1b40b1700000000000000000000000000000077fc3535bc4493188dd59d73eca664f6d3000000000000000000000000000000000028e2005aa9a4e503421d668f9b5ff70000000000000000000000000000005b50a6c3835186ac89ed782652496b911900000000000000000000000000000000001f41c8083b49fc05251fd7a216ed490000000000000000000000000000007255caa06eb633a1cf73ce5183c256433a000000000000000000000000000000000019b9995c4a28caa885548ab88fd33500000000000000000000000000000018c42884878b122b2b4ea980c59006b51800000000000000000000000000000000002f11050bc92d3b07e4e0327190b1dd0000000000000000000000000000003c834fc8e1c39d6079e8b051468deda93100000000000000000000000000000000002502dc747448ef16549ea6da1fedb60000000000000000000000000000003dd7019c39749a8da3e2d55febaf79edb000000000000000000000000000000000001f140041fba58b4748d20dacb6fc040e835715f6ab60393ed109b638e99e152668be99acab5018743da6a19f06018d21e0f75cea863ff0797f3c004897ba4801cb29aecd0e2078cfa44ef250f9fe7404812a79d4a32e019643db45679498a3bc06565b70c72bf441b9eb4532ecb2bb2766baf41b64818fb5cee16f7bdb2ce9774157b98d2a7ce90bdb833677ad7ab71fdce50c14792af8f51b748e8ecc25a36c750e20bdfaa69e71e87c16213a26cc13f9d3f05652e97ad5407eaf070b9c9a0d4f3e6880980f417a41818af4421cc228a6bd5dc06d9aaf7cc071e43fb103009bf97e084344c3711caa7029a4c489600f574c7d10db3f68a23844533cb0308237f04009f72ff8a15b9c07f8e742f76216063195cbdf5c415829b805b876e21c1cdb5ab974812b473a4dff5f8c7bf1eb11ee2e81bf5df8174e66bd0ae0e8eea9212882e3dc8b1bcf7c796c8d23bfd0f81525be06bd78b5cdb0a6eeb604b13f2517f27bd2d1a7404680f5e771cf9af8e41aee6211e9d23202700003872726969cd15115c566ab08b0fee05208ad550edb243243c05fc0812283d832f778d7d210bb4db7867079aa0efa2a4de0ac2b26ec0f6cccce084068dca69c5fcbe2f7b968f351981a725b7fc238f9b0b64c9203aa0679dd3a66e08d807a157e8c2dc9743590c0cb56e0df3dfb156263d992477c210b2534769bd7f974676cf06c683fe8a8037fce8f5631c915d4b5228ec5d728b21fbb66b37330f16b58ddad068e2a53414ba11f113aa3ce245fc5a3f68faf12b00f6cebfdecfe094dc4af1b2c2962addc95a502b55b52ba02f79447d03097c9ff1a6f61d297c522084a681ae8513d817b48f36d490877e700fc81eec545362cc62bc973f0685297754138cccc1c43fa8ff5cc7d358b45b0a1e656deec6a55415d1918b6d78d7965c59f0219c4a09706af3e22044e8ad5655271f854d3537373bf0913d3739252487b3d144050cffb49956d7b5b94924d4d4f8a5ff6e9ee940c6310bf8b224ec8c19fd048fea5864ad5aef8eccfd39128687611a303de6ec1744022341693723924980e5c7eeae8baa7c1068956a6eeccdc837ed36308d0758c9116b1b1c87f2b67dcde7da2fe9f63b935e9d96e77c0912dda15383e735c759c811ed7e43f32bee63ff0dc8254e0af4d0f9bbbf379f1b760a4d8dd3c37e08d48e30bd6675640bca75b8c1a3e5864bdf1292336f83187e69add9b65a5ccc8eefba8246bda343416fd0653759b4e6c5439320f26c5e4cda7c975a3b11d85099e5f642269760ca610738dd87d87399b076a9b6b6155a321c183493814493885b26dab270bd672cd7cca81d94cff4d742cd958abea7e1db38c9552f68101c7bd9cf56821a8c18f1e199bafa9658e94a401cd7fe7880ce787d6e03ed8695fa194073d92195f185f0f2217d9de8a661b2d251651d9bad8a63a833f9dd5afef8b6111c96f28aa0d0c842126655a48f20b74ec8057076e12680aa84fc41bc1f47d94da03eb201a0de141045e2724092bb0ecb3cd2a5cc5210d2ea752056f5e4571327fbbfd21ffd375225498f01f56506349c502992fd43733790f74197f2e13467983de6902828a05b4c481473c2970b08474f320707cb524cb727685a02f43e034ae16e31165ac117ac0c14cbe22829c546065e2936de6906159007735b3a74e66d4a4aa1519c4da911d739d86372ad03f9ba7a56cb1570baabf0e1606d254173e4c8ccd0f6530b48aa65747103ae44490e74ead8627d267a9ca7670bf0bcd00653bb0942411855718178b7a9013b83347c1ac991dd1600c85076cbe64e8f5c968e33405209b2b50e497f3f76bcb84145a30c0e5671009dcf9e934b385344dd372a1603d27e98ab2fa04b8fa959f08cf4ba8d66ffa8647f20fd199f1079c5bbe0dee4ee917c0e6dc2deffcf464dc4ec6cbb9bd619965730981949c5acb83069cf69e998c0443ec80dc0149a6212a7b15fc04ae21f46614dfd92cee00327f489b07f883f9176d57d603b63ec5b7ee56c7ffca540b69a4db20a782d7a812fe86e480b4664c1cfea11495dbfe3bea19d7882c9fb802191a4be26d0cbfe4d53e5ae4fa14d8891f3a7bd985c6e248fed69937983142aeb9e9c900c9e1442b569405c549c24a92013a8cab3653a53ac11ee501b06de8d7e34d1a936b80f3ba5aa59c770bdf5184083f80e9bd1c4703cea35ab4a22851f25548f6236beb5eb179c3ba7d1213167602165a3788df896869ad1f395dd2e43ad7845c904f04040436b5f93e785aeb3e0598dce07ca7b8119f09d08d899863d4c795564503a65092e973fc6b6329ffca2a320d843dae5f0d9b14f22ae930f2f20876061dcf47ded93124c2dd5f4cd2150a0fa19474fabb9635aa3b2c2e0e047b55173343056692fd2642502a30bc01e42a59b34e8906c5c9ff0a69777fb50373303b0c481c11fde33434bfa51c76c5b20e90f1b2357dbf29633965224aa4e58ad87d2120fbcb6145659377df9a330aa404f2858133ca7c08b9e06eb8d4f9790dd46caa2f32c2129e059d96d32d1bc5c12a63aec96f2cd76ef30d8fd78584295f280a6907c4652f88570c4f0e6a3731541d20aaf4de6d8b255457b0e88520bb9e373ac10eeecfe7efa42e74b6a4c5aa402ed9246ae43dbafd8544d56f4ad83657a06304397e8b55233ec1fcc0fd604c6f183f29283e90d86500f4a35bc23890d52795c8035c95ebbd30ca6481f21dd6c80a17c8893fd16fe1bca27f83877e42cc7e3e579a2c5935e5905af638d43d24fb2a30f3ed391fb9f5101ea514ff58d9e901c7c703d5d5beea01d109ccf0837844211d0c4e91d68a13395b8959b367a130dc197c9b6a0b9e0043b6ebd9cfa468491a50b15989746483991e27ecebdf5a051f5fb0a8b37792c89ff4a958aa85c09918f696cd2d8cb73de87560055f7e87535e7efdb348a37da26ecfa1a398094ad20da8ba75157c4db506030a73fe30980811c4a4959bdf1798af992e87b47ada532613449ec92b077e4f8be8a5712645883a5f29ac8281b89788eaeca4fbacf88c0ce6689c834b850e50aafe02f2e92998cb9560904e5bbf1a7be94ef8e49a2afb136fc234d04670db5abe2eeca56e8d4e01e36d313bb37feb019a65f07df40a061ecea8ded7348dc88d337ea6266c3ea8e8df6d8849295bdc525758eef4334dd016c6c0b29e822757f2a946429492f36fe255f3d748b08a58221db869972066ff111e22176449df0a126a02a57359041c9e12950a3b1319000f8602480290c17d054269f7cea31835569a9c71afc84c50fc2cf8d7c21ba474284632971b27c4632ae5c4993ebb43823e831809880d29c1718a0dac27e2bea45b6882902a4fb7aa0e15a72d699f63176e8458525643d50562adf048fc14011b9a175418fa10f87e2ca5b18301eaeef6d650f170e29d6c500a2d19ee7cc07c6550586d2cd586e2da1dd662288f23e0b0b320d6171048e9afd49c82ba7c64a3d70bf230d6cd3b81c40f2d6e3beb8376fabadc28451103d416ce41f61d2e48b8d84565ddddd314c74c25209d69b13d4555708abdb13cd69ae4218df134484a0374c2a2a7521819806429b48ad45191aee67eec9cfa77b92d65109a2a5c21f35ddf538f049a06813b2f0e8866fb912b26d7ef2591470115a0b376a8d7b93615c10800fd6d03763f628d21f5e2df3c112bef7c2c47a1e2101540080cf6e0fb83ca34a67008ecafd644db2019f4754d8cfe731b65c5f73e248ea9430479f80a0346524c6dff10de7bff340dda66989fbd9c2e3c4c267bf40d12481ea728a2d7b176ee42209db3f6bfe0cb1d0d2085f47e68848c81c1f293bc489a666581224a2460f4e9858f8afe10c96a2ed5d01d9ec5505ccbe8b00e5ffe9bf99f72bb6f5c61c5dd5efe3fd6e11d632312a056317ff59cdb2d5447ccf3079b0e7ae2314d0f2346de3bf5584a078e28161ea426bb7dbfd8dcbe5ae182831cd021c565b58d29fcfa1455b8f4a5b370908028b341c3779836df765d001680e897ed75011ff8b12d16acdcf83d86de1adcea0d7ec37f24721bbbee7573a0dcbbb506f75f0be0bfda1aee346a49765132978b196472b509d868d7a24679c635edcc90c18cdcb639c7a93a18b5f178ca95dd9822b3c29c5066f3f5949c70f07e51d4104ac4e4cef5ef77b6a2cf32eb06f135002e486c2ce3ee6b396a04b71c66707b3d0c8322d00636729769e0247b3bae4266231ec9b6d5347dfcbfd6ef8372223b8105195450adc2b4de85efae65d61f0aa61059498be7c67da604870bd72937b6a82f9902b7495df0a3ddcf83deae6d3edc10a0077cb0beb542521416428867cabdc7766c097a61f0a6160237d406c3ae870706f647d8baf74a8afa86d826d7e3d555b597ef5a436e7f27c520670e4478322e062aaf273062d09e923a2ab966153c8587490f43905cb6205c3be7e1900aa524320853569e81bc09da68f57c13d40506915f986befd273951380af4d2144ee1889307af8d694d6d28867ff7ab9c72985709136ba81ee49f5a82c34485bae291adbb20b92418a956009067cf5f3fb7b8c7a1334f4aaf80e9d659a44d91bcf1b2d4ff891410afb260b97141f1d0c7b8355fee93b2e4003dad80294383575db65287bab6a8f2a7c98d94e464ad3f66d564135271eac4706f542aad94fab60c2551430fb8af01a04fc3c88631c6e8d98cf2f863a08e8df4d44475b9e52d8a8d52407f800604b10fb84cf7d85a8ede2f88d5a1c89fcdce36c98ce56fd7ada0990e21658b44c5b8278dbfb7d25cc2ac2dac6fc8764fb4c215b23be20612d40b330ee1692a7833b6fcac6ea9cb29f6ce7126de70598af6656061e17d4ab49d3c0a90624ae420e929af0e84e6f1f969123f0718f2fe931af0ec59035d901ddf54712160853beb75312d28a729633dadf589d3990f4dda32000c1a8f7dca26c3efeab290b58f43c6e0b98f2561fca3ffd4b3f83fdd4ab6746e3e3491be917c6ab51ad1b13b0852710fdedebb67b3ddb1eec0943e53206dd5dd55834a0a80847cfaf836007dfa84710e56a1cbed7865218133ca960cb4584343a124196f689d8a38531db075acedc3ce23d64a71b360b1847d052ec1ad76aafc630073888eeaba7a68b0526f7c0fc371d44f289f29f750e6845388d2a3db98e649af987d9e75f8d8996ba125e23628f4df554486676ec161e317bc3ac434df3fa26af9d54d7b148e3c02000622a03d100170fab668dc9ff05f1c3504201a72f94697c9389eaafc0988d46129a92c30ddb988c5150ad2f3a34b2cda0dcd266074a77bdec2f7c6464c3803f08da4bd236b77c96200e11313d90a0eb6a8478b3c71c61755ed33ef44f97ca8113a533e2ec25aec322af506e5fddc051b5fcd96dafbefeefdcead3cf1c31d3c117ab1516181ae3caefa5b83b2193de02e19936a7c6671b4116f713f6e008d5af2870bf725c6a51d49484a02b740b344faed2032cca5a725d7760b67fdc0bb3682a40a3e6e450ba0f68e32715612c71d99e7ede2beeed56ffb00e9fa75364bfc20d87fa8412ef8fcb5a7e2d8fa5358a4fa5ddc48a3752f8138b1349196d39e24504c8f5e101a4874f662de6b1b08e74d9754b1208747f50f9e7d5666c686c3bb10a4f17d0836ab3912a78a87f5c3a4a11573b5917d9df66e645d73b4e965c1c79003bb46f7e4f984fa5c6fed60053ed9de1d8db0698bea3edef1b4365be42a1e614ea0ef415dab3dea62553e9fb2f861652420d3efbd932363dec1f494f9b73ee104da69938e7675a9c1d5141e1f1c6587c471c82ffcf30063fa92d0eab1a30a713e711dfd33e6302b8aa6e697c58ad2ecf09805e8b55ebe825b5e2dff43c3fdb0c07ff066fe92168d8f3213ced8885c02ebea43138d91bb176d16c79de09d0201ac7aa842cd5fa9e6583430ade4fbce07c354c99dbe81cee37517f96d3d24cae2e8b6056005fff9ab9f77b3c877b98f2e2cc3d7c258d5f040aaad64d9b1a2b7a0795d45f181274582eac44164099b4ff760769e73ae2e62789390bf8147d553a02020f35cf3386668a335e9c459a6d8ea7909d600b828412ca6a468a17894ba014c4cf83e5b94db03ac6839192661e929d2a2292bd2204f598d5bf6d0849c4490cbc2403a882373cd4a151a1d426506bca20a15c1879e4ae33db8cbc7a6978062e9ec06a6dfe471ccebab4df6be492386cb661f814b81947892fe0a9dc8d38742e553cc4cf1166ab0a66a5147974d94c35d8d8299e75eaf6fa6f0d5b65b5773b103f13117afb5650ba0b56c7719bfd58946dab85d9e3890cda2d44e02707d1c2053daf4dac2563007e3804668dcb50e047f9aa5f15bd1391c0c02f070e2b474c067cb247f2887364e7feac00a5275353cb7fbac4e1a710c279703d9592df646409446fc4603e0543cce0d84debe9322cd746897d378558b9a88c88d9ffbce3b91e040c579cd5d1ef62536994e4698d0658cb1605427c68445bac8f46d9cc156213efb15a0a171aea91031b3d31a2ef091926ba40b667c91808b48fb3059fd9ee062de3da771675d3ede1c1640bdfd4a1b76fcfb10fca275038d51c4d577f006c17e18023072dc9c2f966d74322d0aade0de962d845011818e4982bb9a97ff5c7076141abae84cad26080cce8b33ede3f4f59067f6dc024a103dd6022884b0fed1753191792c49504b21f5f4f5df855cfd1db0bc9abf0063532d0f95ff1ca5dd620c7606d02ca726522c7d701e7003fb619e02ffa510d4dce5f474f4d94131afa2392d190a53341b35039a2f651b24d3de4753e61165c00daf5540807a74aff5a17a9ce8d058ac3f2cc50e8ef53dec271be401026aec1b15ce10900f0a7b99b1718e8d7962b410af0f691778cbfb3242ab62eec7d745aa6f4a5b405150db51ea62d091462b7a2100d0947c87218364dfceaf70f8cf0aea0df13206470cce19c03290b87b8cf48cbcbb20a56c390ee6d73a72b65c8cf72e2160c2c8abb5498d4301e9929bc09344bb1cce361fbc6fe845e2870f4781846f2fea036463a7cc385f82dcde3305f931de79f5801b41a8d3b9b4d5a26bdabde4375aeb3afaaea0681bc0d3a7ed5712347c79ab6387dffe674aef6157ad83887975d86e3d9bc0c9a771316197ed31e1c31008d55a722c554c75d91255c664b2b791b115749cac0597c67188e7189c901c1bbce453b5091b5deeb44149b3cd8678ad73ed83e2f083b4e1d1b8d21bbe490494b623416fa6e12736aec603a246c88584d3ea19e6f1c8e9a962b54f4a0368f1c8b91f24012110cfcf939984d76f377e524015aad15626debac224b5899a20fa56b7dbbb39958face74b368a6f01f7a67af32f1ba69115df020283b9b7a1329395db79332849016da0d82d373c3e08b1c4608d3c2525b7e692503945ba579006b96dd8a39af16a7dcd28576b14f0a5d340344be843809bbfb301a6e7450ba46193e45ceec9a3b3187c699317660e4571da3c5f0589f41503fa6218987c613334d4babd4ec473d83ada7dc4c490b17ccac1490c9bc7ac4d6b68800b3b38b872a751f6dfdf420fde376af1940e360749628875a916d9c34bbba1013f36b18b82e79822486508d5f1b48043e85b4783019ad43ba5bedb30f572f252ee9515df6e5fed254f8ae1b14afb2c0bcb2dd9af2fc6dea3f2be7337763bfb903268ca75c686ec2bc4c520acf211a51c13b17b8e40797dbfc9a85aa92d69cc02405eb6cb721fd727f63488e1b1d2f4d552494eabfcdc55e7004adfe2be61a9325d88bdd94b9206667bbecfaa41b08d0b5d1dce2d6f355c25df7da325e53d66b0a845b15155ee551097cb127c65c191b5462dd7ea9b7d5ac1a016a0c77d350ad0366a28132bd9b3dc3be6f1be89929f90caac86758bfaa45011d74702edb915d0d19ebd94ac00bcbf9eeddf36e7af56c589e7fe4c95d6a3966073a73d2cff37c1563927a96d501ed638af8cbac9e3ea057b44dfc674a10ac903cb8344811c3660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4b8b2d194e22ff58a7b73fdc2c3a3a56b2dc6762d670e55373b1e6f4263e1c13a9597a062935e5f28b3cf84fdc19f8e465579adfaca48a2de89186e58895611e91d1fd14ca6bf645eda775818dcacd7ff027ac065414fca82e79142826c8af0b6d33456cecb12c27bed8ced0f526f353b6f53ef40e7085b2bec5146e591f152a626a0176105fb87579725d44a1f02804288545407d9f18e92109624eb7ae00147e4e900de1f3a4eb3c0a0171e46c99baf4dbbac97f9544d004945235ac3d8924e1dfc4a053dfc0d5adf28268ea41a7447ebcdc1e022d3ac08912449c35a63f2fee5256fec2c6434195e7398b105582465c285a5f0b643179db66ef7245941418f25bbd94a1775c5bab6b30a9c0ea98722a63e979fdd8b1b96b7a6f89908d652496117d21b6ccb928d3e4c37401bc2dfbe6044f523ecdd969499e35bf02f3742d7fe7154ff3d8f8627d3fbc827493d25aed3debec977ef01cc40853a57b1abe2563bc9d9e13307db8d292433819751d2192093c047f8fee29a194664299802400106687505736baf0577016d6e5aa72f4830c7508b27801cff262ac5a8f5d9319b3c566695b0f05ed81b0cf089ae609eb2ea894df989491f47359b19122182f0633bddb1d2eb5b4ec98ffd906d9add90afa17d7433769cb1669840373fb00d82d1171f24c91b34dfb0c9d86c932211f6d77095adaf42fcfe254a1c2f3e5b55d11695bb3bf7de32b02c6ef759547df8c3711e73064b9103bd847838dac8109cb111240074fed13fad8671251d45157648059d0f7755e0b6f265605a69b43473419c760af99fb0ef4a31c385ce70362934d1c7f4814c23341f6e85697fc58809808a49ca621a9001d23246500c6296dc4ca0c67eaed63a9b96e31628d3f73ae2820773978cb1cfe830c9612a508e141272617a67aaea7096689599133a0bacc33081cd767126ebbab80641b0df9b9144a6addc6e0adccc6be384a4dd0d4ea5f7013312f0c8c3154eacb1f465464cb0a9e48064747520f8640af60958293386dda10d051e1c93a32cac397b0db17bc0abc2b4f0a2bfa589441b794ec32e634e9c703073de9a27f586a1f78a23cf52e2f7fd319ff244ed3c250e8b05803ebd9c0931aad389f50192637d08257524bba2182aacb3a4822fcb471cf0211266fd36e2a0ea6bb2f72a9b969ced0c96684bde403e603d15f468190d2db8ea9db4d1ef1db25feb4d7db6bfc95e369c75bfb43e24c2b3f3830ed0aa2b57580f96cad45e31b111cadad342f58190c7c3c4daa3dc496c5bb48950f19d3e61786a76f4de78ac71db57fdbc14f812acf803380fba7d06ab43c987668a5cec73ccd3adf041dc9f920b277ab107d33dcefa25048c948fd10061df3e4bfe6dbf984ad49ed36fc30e425855c3815abfc80b87920e231c22891bf666e97186efaada77408122cb2c412118759aa97b8a333a6acbceb3526e70a54586421248aef3affef84769717ab130a5facaec3ed438f30dfff9445423664c4972ffc6f0d9466e497d369157853632515d12c6a7fafed5ceb18b126d2993feefee6d3bb10dcb2b30e8b25677c8cdf2eaf0b530a1ccbb1b8dd103e13a6d9ddcb61c058e06166d598c0d8fab13d34400edad8a622f23e28dae81f4f24ce4dc254e7b0cb1212a430bf31b14151200aa80999ca63e9e0412aab7c54edfc9db83aed5e80b6f560c202e0ae6457110516ba0ff593e4ed1bc9f0ee428151422a98c627abb44379b1c17bf0bedebf886d4a6309f6465de8527934543097938052bf9485bac1870d4f8530427ab40d326516ff2b7d14450d444bb3cfd0fc6803bd96d19d0e551df4d4c74ff5040f9419fa052e17a396c9398760694c34dfb056f8461d22b010267b3e9e75b64c404ecd1209b527b7dc11b3326711bf8098cc378499b488363e45a16e03b4c28198e4e32f9f682cdc8a32b58fcdd3cc6f7cfec1dc40c5bf25ebb415ce585519c4a24c25f15d6a000000000000000000000000000000b69ec720916bb33d509e8c078cd3971d24000000000000000000000000000000000003af32c4bff6f494e1c9d07d3d44370000000000000000000000000000004f24ec9cf59472c0f1756a45c60eac1484000000000000000000000000000000000013d9222abfe151905874812b77011200000000000000000000000000000081a13258076a05207291c060b43170cba3000000000000000000000000000000000002d14e8077701f061e024acb8a432e000000000000000000000000000000cca98116917cadfbc3061958892c2cd57700000000000000000000000000000000001491aa7defbb262d4bd39454b845640000000000000000000000000000001fbf47968feace896fb105c90936a3746f00000000000000000000000000000000001f20e2094abc5d3f29080e7bc9e3820000000000000000000000000000007952a28967d0c5da1be08ad9845d57116700000000000000000000000000000000002390dc0a3cbaed00f56d334a2a1ed4000000000000000000000000000000febf97aecb8ad808cca5cc10b7d1955a2800000000000000000000000000000000002a5ba117a8c9e885cf484975128ea6000000000000000000000000000000f2022c25c33188d1176bb024208908f446000000000000000000000000000000000027864c740580b3f29b52172c2010a30000000000000000000000000000008ed3464dea633012cf05f0ed70c3a6378500000000000000000000000000000000002be0bd9a7bb2deb59bcd1f411dfe3b000000000000000000000000000000df26c045a8cbb0f252e98bf0849cbbd14600000000000000000000000000000000001f686cb1274319148824149be5005c000000000000000000000000000000fa045060d1e0fd9e33c1b2bbbe37196682000000000000000000000000000000000017ad7ce37bff3ad6b174b9e9dce81b000000000000000000000000000000fae7bdd16454f1805673f01ddebf93ddcb00000000000000000000000000000000001eabc17fd90d700ea989310d08900c000000000000000000000000000000ac6e57fb6c95ebc06d2f10f114b555f8ff0000000000000000000000000000000000017910028ecf2ea03abe0325fbc5e2000000000000000000000000000000d04e388a13bed596bf1ccd77c8cc672b4200000000000000000000000000000000002e8cf8a87b0c85555c32ea2c9378a800000000000000000000000000000028e03c159f3d9e1d4fb9dac0c0e28b40330000000000000000000000000000000000302721ff7a472348d110d0ea8af47b0000000000000000000000000000004d1275b8fc38955038ae5a30bf2a51401300000000000000000000000000000000000018dc648727c2f6255ab3b65324010000000000000000000000000000008a17b8afe770359d90e414f1941dd9715600000000000000000000000000000000001e335c23b56329f96d30def7e0d5fb000000000000000000000000000000b8c4ff348d88d44f23013727cf67194f550000000000000000000000000000000000259725d9ca9915160779ec14c60c070000000000000000000000000000005cb1aedbe02c9fdeec371a2d384bff339800000000000000000000000000000000001296e18f8091a9af4fcc8c43c88a6b00000000000000000000000000000000e28a094346cdc32abff6c8edc12e815a00000000000000000000000000000000000d5fe2c676bb8a2248c0c862be879200000000000000000000000000000037ba320f4e9fcd78ec747c10fd5ea788cb00000000000000000000000000000000002e61f74a97f33522a25da625fe5a0d000000000000000000000000000000af38818c423a88054c9249f7d975e268270000000000000000000000000000000000172fa88963f9a8afcb447276bd61b700000000000000000000000000000073b5c815253f526b0e006957ee9800ceba000000000000000000000000000000000024df7e03e0e06f05eda23ce414fdab000000000000000000000000000000c35d9c91e1bfc0484c1142fa00aa0278d100000000000000000000000000000000002e0b1cd890b57535f23b0a0307f35d00000000000000000000000000000068cf808715321d135c1280ac8bfd8f26c5000000000000000000000000000000000010d7bc17130a641d0cd98adb7d563a000000000000000000000000000000b1962cadc7850f2d76101946c0634c82690000000000000000000000000000000000219c196d7f8d511e73b18741e556d90000000000000000000000000000000db21a307e79de4c54d7a47ab3af8c9e410000000000000000000000000000000000135ca2d2c5be2ebe163619ad05f58d00000000000000000000000000000076d7e4db7f1697e3aa5cf79183566b193000000000000000000000000000000000000c84d9689f9a96f870c798b0ed1d50000000000000000000000000000000d725ff8b8fefa0ca951111fa3dc9be904600000000000000000000000000000000000d8dabf2ce1f7e07ebc88110e1f11b0000000000000000000000000000005f349c7bb2cd50c4fc0a6de879052704d000000000000000000000000000000000002aa09c72729a29c1c4a2bcddb2b0d800000000000000000000000000000094b29bd11221ecefdf851596d26bd1ef890000000000000000000000000000000000013c30027679bafc916dbf73d86c9d00000000000000000000000000000061bfae304b0f0b35563654902d8f96108d00000000000000000000000000000000002cefe84f2a195dda74603da4a083b900000000000000000000000000000094d0e58c078e2906e62be92ec5c43017130000000000000000000000000000000000089cf2a6d458939348ac7746edbe68000000000000000000000000000000f450b3ab0378f7a7b897dc8c217a13b901000000000000000000000000000000000000c9d11eff73bdfade30f20b6f0442000000000000000000000000000000fecc618e1692c29c933dc25676210eab0000000000000000000000000000000000000f0e10a9823c7d21a13bd9c690474500000000000000000000000000000044b2db469dc5e9d4659d4ea81b29374058000000000000000000000000000000000013191f8ef0ccf3228376f7381dd0f90000000000000000000000000000000677f0b8483922c4773ba0e9a1e67d4aac000000000000000000000000000000000007828f033d61ad49e6e27edeeb40560000000000000000000000000000008efc84f9620f21a21d124f87530a6a472100000000000000000000000000000000002b97ae0a8daa8507cf5461229600d3000000000000000000000000000000ab428214cd4d24ce1c58aaf039c3d0183800000000000000000000000000000000000448a9144f2321fc9b56887c6390c900000000000000000000000000000011180c35a7d871096f60fa54d3812274c300000000000000000000000000000000001d71acd36298d956f06e5b8414042c000000000000000000000000000000c7f501c7bae8388652f05bf07431b45ff10000000000000000000000000000000000248b33d62a790b8d4aa631ab22f5fa00000000000000000000000000000084a901919a9f90a813a132c8a7c997668d00000000000000000000000000000000001c580b64521e3eea1df3ed83d1026e17ed0c238256265fffa4045db9c7891c470e9ae966d9dd6f17f57b0f319f6a4d11500525010e80966078c6f1560377c66b1e63c026246226717d6707363205d4171e78047ea0305e68218dc772d144ddd7bba06dd93a3b21a31c8ea4290315db0d9911ecc14e50ad48f9efa777ac089cccf9c12eb95189dc9975af0cbddfc9501bd7e69945fcc67fa507d5c2b8116d0bf53e55dc8266d38524d3519b3579d9731159ba6c9415aee9368cbbc4a071ed547e8c68138c74eee38cc37ab4eb71c8cd1a17de13e5732b312247cb6a9e0ac0b2ee17175dc834ad6f22094d8940276d5d270b417eda28681fac99c679f04524ab961ec2e322ba52748c224aa3389715221a77e34f0ffd8cf5e5dbeaf80801a70a2f51f8a9403311f779d4d0a7b544abc11502cad74703476407d838e8eda1c975fd967c49bd3948f141b3e856fb65d0640474648a654ac07b9fa69061f639bdd9431cbf5fb44d17165313232e8c44659b2776c09eee367190a4d290215d144977d9f739b2034425274071f03a2271cdc5241bb5330e8bb50ec3c494980e2af3c17b7b04e1d11a721d00c0cfe77ca7a7b62dbf40901b93a1d071d951c64b2ff75f3b5d3c1dac326a6524ff861b46e97f522b182199f26bf0387b0176eb128beaae1feba260892cb71a81c24c2708486d6d0af6a17c3677d6a60d845a7118ddb2021bf190f1efdcdca13803c64e1175af1a11cd45f886dac908e0c6f81ee361f646dd99512e7169b8957f7f270a30cb7fae0eb63efb505e5aa0a4cfd3d7ff723f3c04db7f116c6a5dc4af98fb86033327f02a1cd09132d2d37831aa2a855b6dae4fe44cf71cc4def79f45ef7fa5448236f5189a15ac188c88084d693720eccb2bb58a01b084cf9dc89470dcf9faec34ff0113a0402faac26ee3c3557d2ec36f65ae4169eeac65730ad7b373f9902dd9b3ad2ddb1cde1125ab257b99a4b17e85a5d70439a78cac10d985c2d623d682a8a8f7000000000000000000000000000000778fc803b231bbfb9522e0c517d283d9bf00000000000000000000000000000000002a99a700ed586f5a2ffc15a1b63c05000000000000000000000000000000c432d9210ba5fc7158201320bf930d0d41000000000000000000000000000000000018370a060b0ceaf4408c900f04a57f000000000000000000000000000000589db5f3c9cd2e5dd3e0206938f8a1700b000000000000000000000000000000000016be090cd2b3ce4a57e6fbf368ea4c0000000000000000000000000000008e7b6afe53baac20636cb21ad0149ed57b00000000000000000000000000000000001cfaed13a0d3db327e0fce9f461bbb0000005b", + "publicInputs": "1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e000000012d5773cb9b23c4ed50f9fba054aceace67637c56c492104bd6cd85ccdb3ded60000000030fd77c2a44e9430a2e6196ff4ed74eb832169caf335c122899deb80b805570c30cb534cd47c98f9e2b5ec2c125eddff184d68a2d8375bedb2c586db12130b8c6000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020007638bb56b6dda2b64b8f76841114ac3a87a1820030e2e16772c4d294879c3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014210b92326dd0c8842f2a4f79e2b806a75d62d98de66215bfbc4938d6aaed340000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts index 4a30b4859f0..d426142de11 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -33,6 +33,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; import { copySync, removeSync } from 'fs-extra/esm'; import getPort from 'get-port'; import { join } from 'path'; +import { type Hex } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; import { MNEMONIC } from './fixtures.js'; @@ -444,9 +445,10 @@ async function setupFromState(statePath: string, logger: Logger): Promise + (numberOfAccounts: number, logger: DebugLogger, waitUntilProven = false) => async ({ pxe }: { pxe: PXE }) => { // Generate account keys. const accountKeys: [Fr, GrumpkinScalar][] = Array.from({ length: numberOfAccounts }).map(_ => [ diff --git a/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts b/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts index 27a86df2787..08636379f64 100644 --- a/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts +++ b/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts @@ -60,12 +60,7 @@ describe(`deploys and transfers a private only token`, () => { logger.info(`Deploying accounts.`); - const accounts = await createAccounts(pxe, 2, [secretKey1, secretKey2], { - interval: 0.1, - proven: true, - provenTimeout: 600, - timeout: 300, - }); + const accounts = await createAccounts(pxe, 2, [secretKey1, secretKey2], { interval: 0.1, timeout: 300 }); logger.info(`Accounts deployed, deploying token.`); @@ -84,18 +79,14 @@ describe(`deploys and transfers a private only token`, () => { skipInitialization: false, skipPublicSimulation: true, }) - .deployed({ - proven: true, - provenTimeout: 600, - timeout: 300, - }); + .deployed({ timeout: 300 }); logger.info(`Performing transfer.`); await token.methods .transfer(transferValue, deployerWallet.getAddress(), recipientWallet.getAddress(), deployerWallet.getAddress()) .send() - .wait({ proven: true, provenTimeout: 600, timeout: 300 }); + .wait({ timeout: 300 }); logger.info(`Transfer completed`); diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index 455dcbbc0a7..511c9369c52 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -378,6 +378,50 @@ class L1Deployer { } } +/** + * Compiles a contract source code using the provided solc compiler. + * @param fileName - Contract file name (eg UltraHonkVerifier.sol) + * @param contractName - Contract name within the file (eg HonkVerifier) + * @param source - Source code to compile + * @param solc - Solc instance + * @returns ABI and bytecode of the compiled contract + */ +export function compileContract( + fileName: string, + contractName: string, + source: string, + solc: { compile: (source: string) => string }, +): { abi: Narrow; bytecode: Hex } { + const input = { + language: 'Solidity', + sources: { + [fileName]: { + content: source, + }, + }, + settings: { + // we require the optimizer + optimizer: { + enabled: true, + runs: 200, + }, + evmVersion: 'paris', + outputSelection: { + '*': { + '*': ['evm.bytecode.object', 'abi'], + }, + }, + }, + }; + + const output = JSON.parse(solc.compile(JSON.stringify(input))); + + const abi = output.contracts[fileName][contractName].abi; + const bytecode: `0x${string}` = `0x${output.contracts[fileName][contractName].evm.bytecode.object}`; + + return { abi, bytecode }; +} + // docs:start:deployL1Contract /** * Helper function to deploy ETH contracts. diff --git a/yarn-project/foundation/src/collection/array.ts b/yarn-project/foundation/src/collection/array.ts index 6f2262a1af3..b703e66a119 100644 --- a/yarn-project/foundation/src/collection/array.ts +++ b/yarn-project/foundation/src/collection/array.ts @@ -100,3 +100,18 @@ export function unique(arr: T[]): T[] { export function compactArray(arr: (T | undefined)[]): T[] { return arr.filter((x: T | undefined): x is T => x !== undefined); } + +/** + * Returns whether two arrays are equal. The arrays are equal if they have the same length and all elements are equal. + */ +export function areArraysEqual(a: T[], b: T[], eq: (a: T, b: T) => boolean = (a: T, b: T) => a === b): boolean { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (!eq(a[i], b[i])) { + return false; + } + } + return true; +} diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 5183e2847ed..a526c1b9eb4 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -126,6 +126,7 @@ export type EnvVar = | 'VALIDATOR_ATTESTATIONS_POOLING_INTERVAL_MS' | 'PROVER_NODE_DISABLE_AUTOMATIC_PROVING' | 'PROVER_NODE_MAX_PENDING_JOBS' + | 'PROVER_NODE_EPOCH_SIZE' | 'PROOF_VERIFIER_POLL_INTERVAL_MS' | 'PROOF_VERIFIER_L1_START_BLOCK' | 'LOG_LEVEL' diff --git a/yarn-project/foundation/src/testing/test_data.ts b/yarn-project/foundation/src/testing/test_data.ts index 18018c7de2b..79961ae988e 100644 --- a/yarn-project/foundation/src/testing/test_data.ts +++ b/yarn-project/foundation/src/testing/test_data.ts @@ -8,7 +8,7 @@ const testData: { [key: string]: unknown[] } = {}; /** Returns whether test data generation is enabled */ export function isGenerateTestDataEnabled() { - return process.env.AZTEC_GENERATE_TEST_DATA === '1' && typeof expect !== 'undefined'; + return ['1', 'true'].includes(process.env.AZTEC_GENERATE_TEST_DATA ?? '') && typeof expect !== 'undefined'; } /** Pushes test data with the given name, only if test data generation is enabled. */ diff --git a/yarn-project/foundation/src/types/index.ts b/yarn-project/foundation/src/types/index.ts index 71872c1d99f..05b4a76a785 100644 --- a/yarn-project/foundation/src/types/index.ts +++ b/yarn-project/foundation/src/types/index.ts @@ -15,3 +15,8 @@ export type PartialBy = Omit & Partial>; /** Removes readonly modifiers for a type. */ export type Writeable = { -readonly [P in keyof T]: T[P] }; + +/** Removes readonly modifiers for an object. */ +export function unfreeze(obj: T): Writeable { + return obj as Writeable; +} diff --git a/yarn-project/l1-artifacts/scripts/generate-artifacts.sh b/yarn-project/l1-artifacts/scripts/generate-artifacts.sh index 1d82db63c06..ef9c640c9fa 100755 --- a/yarn-project/l1-artifacts/scripts/generate-artifacts.sh +++ b/yarn-project/l1-artifacts/scripts/generate-artifacts.sh @@ -20,6 +20,7 @@ CONTRACTS=( "l1-contracts:IERC20" "l1-contracts:FeeJuicePortal" "l1-contracts:MockVerifier" + "l1-contracts:IVerifier" ) diff --git a/yarn-project/noir-protocol-circuits-types/src/index.ts b/yarn-project/noir-protocol-circuits-types/src/index.ts index d6ef3108fa5..042b1b92b13 100644 --- a/yarn-project/noir-protocol-circuits-types/src/index.ts +++ b/yarn-project/noir-protocol-circuits-types/src/index.ts @@ -84,6 +84,7 @@ import { type PublicKernelInnerReturnType, type PublicKernelMergeReturnType, type PrivateKernelResetReturnType as ResetReturnType, + type RollupBlockRootEmptyReturnType, type ParityRootReturnType as RootParityReturnType, type RollupRootReturnType as RootRollupReturnType, type PrivateKernelTailReturnType as TailReturnType, @@ -602,6 +603,23 @@ export function convertMergeRollupOutputsFromWitnessMap(outputs: WitnessMap): Ba return mapBaseOrMergeRollupPublicInputsFromNoir(returnType); } +/** + * Converts the outputs of the empty block root rollup circuit from a witness map. + * @param outputs - The block root rollup outputs as a witness map. + * @returns The public inputs. + */ +export function convertEmptyBlockRootRollupOutputsFromWitnessMap( + outputs: WitnessMap, +): BlockRootOrBlockMergePublicInputs { + // Decode the witness map into two fields, the return values and the inputs + const decodedInputs: DecodedInputs = abiDecode(ServerCircuitArtifacts.EmptyBlockRootRollupArtifact.abi, outputs); + + // Cast the inputs as the return type + const returnType = decodedInputs.return_value as RollupBlockRootEmptyReturnType; + + return mapBlockRootOrBlockMergePublicInputsFromNoir(returnType); +} + /** * Converts the outputs of the block root rollup circuit from a witness map. * @param outputs - The block root rollup outputs as a witness map. diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index 616b3228b5a..f03a7af62b2 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -2334,7 +2334,6 @@ export function mapEmptyBlockRootRollupInputsToNoir( archive: mapAppendOnlyTreeSnapshotToNoir(rootRollupInputs.archive), block_hash: mapFieldToNoir(rootRollupInputs.blockHash), global_variables: mapGlobalVariablesToNoir(rootRollupInputs.globalVariables), - out_hash: mapFieldToNoir(rootRollupInputs.outHash), vk_tree_root: mapFieldToNoir(rootRollupInputs.vkTreeRoot), prover_id: mapFieldToNoir(rootRollupInputs.proverId), }; diff --git a/yarn-project/prover-client/src/index.ts b/yarn-project/prover-client/src/index.ts index 1945a792047..36affdfba2a 100644 --- a/yarn-project/prover-client/src/index.ts +++ b/yarn-project/prover-client/src/index.ts @@ -1,4 +1,4 @@ -export { ProverClient } from '@aztec/circuit-types'; +export { EpochProverManager } from '@aztec/circuit-types'; export * from './tx-prover/tx-prover.js'; export * from './config.js'; diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index c3e709fb6d3..8ccd8bba506 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -8,7 +8,7 @@ import { type Tx, type TxValidator, } from '@aztec/circuit-types'; -import { type Gas, GlobalVariables, Header, type Nullifier, type TxContext } from '@aztec/circuits.js'; +import { type Gas, type GlobalVariables, Header, type Nullifier, type TxContext } from '@aztec/circuits.js'; import { type Fr } from '@aztec/foundation/fields'; import { type DebugLogger } from '@aztec/foundation/log'; import { openTmpStore } from '@aztec/kv-store/utils'; @@ -89,7 +89,7 @@ export class TestContext { actualDb, publicExecutor, publicKernel, - GlobalVariables.empty(), + globalVariables, Header.empty(), worldStateDB, telemetry, diff --git a/yarn-project/prover-client/src/orchestrator/epoch-proving-state.ts b/yarn-project/prover-client/src/orchestrator/epoch-proving-state.ts index 350240242b8..03d32a5e392 100644 --- a/yarn-project/prover-client/src/orchestrator/epoch-proving-state.ts +++ b/yarn-project/prover-client/src/orchestrator/epoch-proving-state.ts @@ -55,22 +55,26 @@ export class EpochProvingState { public readonly totalNumBlocks: number, private completionCallback: (result: ProvingResult) => void, private rejectionCallback: (reason: string) => void, + /** Whether to prove the epoch. Temporary while we still care about proving blocks. */ + public readonly proveEpoch: boolean, ) {} /** Returns the current block proving state */ public get currentBlock(): BlockProvingState | undefined { - return this.blocks[this.blocks.length - 1]; + return this.blocks.at(-1); } // Returns the number of levels of merge rollups public get numMergeLevels() { - return BigInt(Math.ceil(Math.log2(this.totalNumBlocks)) - 1); + const totalLeaves = Math.max(2, this.totalNumBlocks); + return BigInt(Math.ceil(Math.log2(totalLeaves)) - 1); } // Calculates the index and level of the parent rollup circuit // Based on tree implementation in unbalanced_tree.ts -> batchInsert() // REFACTOR: This is repeated from the block orchestrator public findMergeLevel(currentLevel: bigint, currentIndex: bigint) { + const totalLeaves = Math.max(2, this.totalNumBlocks); const moveUpMergeLevel = (levelSize: number, index: bigint, nodeToShift: boolean) => { levelSize /= 2; if (levelSize & 1) { @@ -79,8 +83,7 @@ export class EpochProvingState { index >>= 1n; return { thisLevelSize: levelSize, thisIndex: index, shiftUp: nodeToShift }; }; - let [thisLevelSize, shiftUp] = - this.totalNumBlocks & 1 ? [this.totalNumBlocks - 1, true] : [this.totalNumBlocks, false]; + let [thisLevelSize, shiftUp] = totalLeaves & 1 ? [totalLeaves - 1, true] : [totalLeaves, false]; const maxLevel = this.numMergeLevels + 1n; let placeholder = currentIndex; for (let i = 0; i < maxLevel - currentLevel; i++) { diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 63cf099f51a..11a28c16e7a 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -31,6 +31,7 @@ import { type BaseRollupInputs, type BlockRootOrBlockMergePublicInputs, BlockRootRollupInputs, + EmptyBlockRootRollupInputs, Fr, type GlobalVariables, type KernelCircuitPublicInputs, @@ -134,20 +135,14 @@ export class ProvingOrchestrator implements EpochProver { this.paddingTx = undefined; } - @trackSpan('ProvingOrchestrator.startNewEpoch', (epochNumber, totalNumBlocks) => ({ - [Attributes.EPOCH_SIZE]: totalNumBlocks, - [Attributes.EPOCH_NUMBER]: epochNumber, - })) - public startNewEpoch(epochNumber: number, totalNumBlocks: number): ProvingTicket { + public startNewEpoch(epochNumber: number, totalNumBlocks: number, proveEpoch = true): ProvingTicket { const { promise: _promise, resolve, reject } = promiseWithResolvers(); - const promise = _promise.catch( - (reason): ProvingResult => ({ - status: PROVING_STATUS.FAILURE, - reason, - }), - ); - - this.provingState = new EpochProvingState(epochNumber, totalNumBlocks, resolve, reject); + const promise = _promise.catch((reason): ProvingResult => ({ status: PROVING_STATUS.FAILURE, reason })); + if (totalNumBlocks <= 0) { + throw new Error(`Invalid number of blocks for epoch: ${totalNumBlocks}`); + } + logger.info(`Starting epoch ${epochNumber} with ${totalNumBlocks} blocks`); + this.provingState = new EpochProvingState(epochNumber, totalNumBlocks, resolve, reject, proveEpoch); return { provingPromise: promise }; } @@ -169,8 +164,9 @@ export class ProvingOrchestrator implements EpochProver { l1ToL2Messages: Fr[], ): Promise { // If no proving state, assume we only care about proving this block and initialize a 1-block epoch + // TODO(palla/prover): Remove this flow once we drop block-only proving if (!this.provingState) { - this.startNewEpoch(globalVariables.blockNumber.toNumber(), 1); + this.startNewEpoch(globalVariables.blockNumber.toNumber(), 1, false); } if (!this.provingState?.isAcceptingBlocks()) { @@ -357,6 +353,69 @@ export class ProvingOrchestrator implements EpochProver { await this.buildBlockHeader(provingState); } + @trackSpan('ProvingOrchestrator.setEpochCompleted', function () { + if (!this.provingState) { + return {}; + } + return { + [Attributes.EPOCH_NUMBER]: this.provingState.epochNumber, + [Attributes.EPOCH_SIZE]: this.provingState.totalNumBlocks, + }; + }) + public setEpochCompleted() { + const provingState = this.provingState; + if (!provingState) { + throw new Error(`Invalid proving state, call startNewEpoch first`); + } + + const lastBlock = provingState.currentBlock?.block; + if (!lastBlock) { + throw new Error(`Epoch needs at least one completed block in order to be marked as completed`); + } + + const paddingBlockCount = Math.max(2, provingState.totalNumBlocks) - provingState.blocks.length; + if (paddingBlockCount === 0) { + return; + } + + logger.debug(`Padding epoch proof with ${paddingBlockCount} empty block proofs`); + + const inputs = EmptyBlockRootRollupInputs.from({ + archive: lastBlock.archive, + blockHash: lastBlock.header.hash(), + globalVariables: lastBlock.header.globalVariables, + vkTreeRoot: getVKTreeRoot(), + proverId: this.proverId, + }); + + logger.debug(`Enqueuing deferred proving for padding block to enqueue ${paddingBlockCount} paddings`); + this.deferredProving( + provingState, + wrapCallbackInSpan( + this.tracer, + 'ProvingOrchestrator.prover.getEmptyBlockRootRollupProof', + { + [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server', + [Attributes.PROTOCOL_CIRCUIT_NAME]: 'empty-block-root-rollup' satisfies CircuitName, + }, + signal => this.prover.getEmptyBlockRootRollupProof(inputs, signal, provingState.epochNumber), + ), + result => { + logger.debug(`Completed proof for padding block`); + const currentLevel = provingState.numMergeLevels + 1n; + for (let i = 0; i < paddingBlockCount; i++) { + logger.debug(`Enqueuing padding block with index ${provingState.blocks.length + i}`); + const index = BigInt(provingState.blocks.length + i); + this.storeAndExecuteNextBlockMergeLevel(provingState, currentLevel, index, [ + result.inputs, + result.proof, + result.verificationKey.keyAsFields, + ]); + } + }, + ); + } + private async buildBlockHeader(provingState: BlockProvingState) { // Collect all new nullifiers, commitments, and contracts from all txs in this block to build body const gasFees = provingState.globalVariables.gasFees; @@ -517,14 +576,6 @@ export class ProvingOrchestrator implements EpochProver { block: block.block!, }; - pushTestData('blockResults', { - proverId: this.proverId.toString(), - vkTreeRoot: getVKTreeRoot().toString(), - block: blockResult.block.toString(), - proof: blockResult.proof.toString(), - aggregationObject: blockResult.aggregationObject.map(x => x.toString()), - }); - return Promise.resolve(blockResult); } catch (err) { throw new BlockProofError( @@ -545,6 +596,11 @@ export class ProvingOrchestrator implements EpochProver { throw new Error(`Invalid proving state, an epoch must be proven before it can be finalised`); } + pushTestData('epochProofResult', { + proof: this.provingState.finalProof.toString(), + publicInputs: this.provingState.rootRollupPublicInputs.toString(), + }); + return { proof: this.provingState.finalProof, publicInputs: this.provingState.rootRollupPublicInputs }; } @@ -868,7 +924,7 @@ export class ProvingOrchestrator implements EpochProver { proverId: this.proverId, }); - const shouldProveEpoch = this.provingState!.totalNumBlocks > 1; + const shouldProveEpoch = this.provingState!.proveEpoch; this.deferredProving( provingState, @@ -1032,6 +1088,7 @@ export class ProvingOrchestrator implements EpochProver { signal => this.prover.getRootRollupProof(inputs, signal, provingState.epochNumber), ), result => { + logger.verbose(`Orchestrator completed root rollup for epoch ${provingState.epochNumber}`); provingState.rootRollupPublicInputs = result.inputs; provingState.finalProof = result.proof.binaryProof; provingState.resolve({ status: PROVING_STATUS.SUCCESS }); diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_multiple_blocks.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_multiple_blocks.test.ts index 7795c423e77..533b8ecb62d 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_multiple_blocks.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_multiple_blocks.test.ts @@ -19,7 +19,7 @@ describe('prover/orchestrator/multi-block', () => { }); describe('multiple blocks', () => { - it.each([4, 5])('builds an epoch with %s blocks in sequence', async (numBlocks: number) => { + it.each([1, 4, 5])('builds an epoch with %s blocks in sequence', async (numBlocks: number) => { const provingTicket = context.orchestrator.startNewEpoch(1, numBlocks); let header = context.actualDb.getInitialHeader(); @@ -48,6 +48,9 @@ describe('prover/orchestrator/multi-block', () => { header = finalisedBlock.block.header; } + logger.info('Setting epoch as completed'); + context.orchestrator.setEpochCompleted(); + logger.info('Awaiting epoch ticket'); const result = await provingTicket.provingPromise; expect(result).toEqual({ status: PROVING_STATUS.SUCCESS }); diff --git a/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts b/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts index fdecf4270ca..a95ee46bc9c 100644 --- a/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_full_rollup.test.ts @@ -4,70 +4,85 @@ import { Fr, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js'; import { makeTuple } from '@aztec/foundation/array'; import { times } from '@aztec/foundation/collection'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; +import { getTestData, isGenerateTestDataEnabled, writeTestData } from '@aztec/foundation/testing'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; +import { makeGlobals } from '../mocks/fixtures.js'; import { TestContext } from '../mocks/test_context.js'; describe('prover/bb_prover/full-rollup', () => { let context: TestContext; let prover: BBNativeRollupProver; - let logger: DebugLogger; + let log: DebugLogger; beforeAll(async () => { const buildProver = async (bbConfig: BBProverConfig) => { prover = await BBNativeRollupProver.new(bbConfig, new NoopTelemetryClient()); return prover; }; - logger = createDebugLogger('aztec:bb-prover-full-rollup'); - context = await TestContext.new(logger, 'legacy', 1, buildProver); + log = createDebugLogger('aztec:bb-prover-full-rollup'); + context = await TestContext.new(log, 'legacy', 1, buildProver); }); afterAll(async () => { await context.cleanup(); }); - it('proves a private-only rollup full of empty txs', async () => { - const totalTxs = 2; - const nonEmptyTxs = 0; - - logger.info(`Proving a private-only full rollup with ${nonEmptyTxs}/${totalTxs} non-empty transactions`); - const initialHeader = context.actualDb.getInitialHeader(); - const txs = times(nonEmptyTxs, (i: number) => { - const tx = mockTx(1000 * (i + 1), { - numberOfNonRevertiblePublicCallRequests: 0, - numberOfRevertiblePublicCallRequests: 0, - }); - tx.data.constants.historicalHeader = initialHeader; - tx.data.constants.vkTreeRoot = getVKTreeRoot(); - return tx; - }); - - const l1ToL2Messages = makeTuple( - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - Fr.random, - ); - - logger.info(`Starting new block`); - const provingTicket = await context.orchestrator.startNewBlock(totalTxs, context.globalVariables, l1ToL2Messages); - - logger.info(`Processing public functions`); - const [processed, failed] = await context.processPublicFunctions(txs, nonEmptyTxs, context.blockProver); - expect(processed.length).toBe(nonEmptyTxs); - expect(failed.length).toBe(0); - - logger.info(`Setting block as completed`); - await context.orchestrator.setBlockCompleted(); - - const provingResult = await provingTicket.provingPromise; - - expect(provingResult.status).toBe(PROVING_STATUS.SUCCESS); - - logger.info(`Finalising block`); - const blockResult = await context.orchestrator.finaliseBlock(); - - await expect(prover.verifyProof('BlockRootRollupFinalArtifact', blockResult.proof)).resolves.not.toThrow(); - }); + it.each([ + [1, 1, 0, 2], // Epoch with a single block, requires one padding block proof + [2, 2, 0, 2], // Full epoch with two blocks + [2, 3, 0, 2], // Epoch with two blocks but the block merge tree was assembled as with 3 leaves, requires one padding block proof + ])( + 'proves a private-only epoch with %i/%i blocks with %i/%i non-empty txs each', + async (blockCount, totalBlocks, nonEmptyTxs, totalTxs) => { + log.info(`Proving epoch with ${blockCount}/${totalBlocks} blocks with ${nonEmptyTxs}/${totalTxs} non-empty txs`); + + const initialHeader = context.actualDb.getInitialHeader(); + const provingTicket = context.orchestrator.startNewEpoch(1, totalBlocks); + + for (let blockNum = 1; blockNum <= blockCount; blockNum++) { + const globals = makeGlobals(blockNum); + const l1ToL2Messages = makeTuple(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, Fr.random); + const txs = times(nonEmptyTxs, (i: number) => { + const txOpts = { numberOfNonRevertiblePublicCallRequests: 0, numberOfRevertiblePublicCallRequests: 0 }; + const tx = mockTx(blockNum * 100_000 + 1000 * (i + 1), txOpts); + tx.data.constants.historicalHeader = initialHeader; + tx.data.constants.vkTreeRoot = getVKTreeRoot(); + return tx; + }); + + log.info(`Starting new block #${blockNum}`); + await context.orchestrator.startNewBlock(totalTxs, globals, l1ToL2Messages); + log.info(`Processing public functions`); + const [processed, failed] = await context.processPublicFunctions(txs, nonEmptyTxs, context.blockProver); + expect(processed.length).toBe(nonEmptyTxs); + expect(failed.length).toBe(0); + + log.info(`Setting block as completed`); + await context.orchestrator.setBlockCompleted(); + } + + log.info(`Setting epoch as completed`); + context.orchestrator.setEpochCompleted(); + + log.info(`Awaiting proofs`); + const provingResult = await provingTicket.provingPromise; + expect(provingResult.status).toBe(PROVING_STATUS.SUCCESS); + const epochResult = context.orchestrator.finaliseEpoch(); + + await expect(prover.verifyProof('RootRollupArtifact', epochResult.proof)).resolves.not.toThrow(); + + // Generate test data for the 2/2 blocks epoch scenario + if (blockCount === 2 && totalBlocks === 2 && isGenerateTestDataEnabled()) { + const epochProof = getTestData('epochProofResult').at(-1); + writeTestData( + 'yarn-project/end-to-end/src/fixtures/dumps/epoch_proof_result.json', + JSON.stringify(epochProof!), + ); + } + }, + ); // TODO(@PhilWindle): Remove public functions and re-enable once we can handle empty tx slots it.skip('proves all circuits', async () => { diff --git a/yarn-project/prover-client/src/tx-prover/tx-prover.ts b/yarn-project/prover-client/src/tx-prover/tx-prover.ts index a6dc55feb8b..23a10f54f83 100644 --- a/yarn-project/prover-client/src/tx-prover/tx-prover.ts +++ b/yarn-project/prover-client/src/tx-prover/tx-prover.ts @@ -1,7 +1,7 @@ import { BBNativeRollupProver, TestCircuitProver } from '@aztec/bb-prover'; import { - type BlockProver, - type ProverClient, + type EpochProver, + type EpochProverManager, type ProvingJobSource, type ServerCircuitProver, } from '@aztec/circuit-types/interfaces'; @@ -19,7 +19,7 @@ import { ProverAgent } from '../prover-agent/prover-agent.js'; * A prover factory. * TODO(palla/prover-node): Rename this class */ -export class TxProver implements ProverClient { +export class TxProver implements EpochProverManager { private queue: MemoryProvingQueue; private running = false; @@ -33,7 +33,7 @@ export class TxProver implements ProverClient { this.queue = new MemoryProvingQueue(telemetry, config.proverJobTimeoutMs, config.proverJobPollIntervalMs); } - public createBlockProver(db: MerkleTreeOperations): BlockProver { + public createEpochProver(db: MerkleTreeOperations): EpochProver { return new ProvingOrchestrator(db, this.queue, this.telemetry, this.config.proverId); } diff --git a/yarn-project/prover-node/src/config.ts b/yarn-project/prover-node/src/config.ts index d7229c80afe..9ac8ecb7b0d 100644 --- a/yarn-project/prover-node/src/config.ts +++ b/yarn-project/prover-node/src/config.ts @@ -26,10 +26,11 @@ export type ProverNodeConfig = ArchiverConfig & TxProviderConfig & { proverNodeDisableAutomaticProving?: boolean; proverNodeMaxPendingJobs?: number; + proverNodeEpochSize?: number; }; const specificProverNodeConfigMappings: ConfigMappingsType< - Pick + Pick > = { proverNodeDisableAutomaticProving: { env: 'PROVER_NODE_DISABLE_AUTOMATIC_PROVING', @@ -41,6 +42,11 @@ const specificProverNodeConfigMappings: ConfigMappingsType< description: 'The maximum number of pending jobs for the prover node', ...numberConfigHelper(100), }, + proverNodeEpochSize: { + env: 'PROVER_NODE_EPOCH_SIZE', + description: 'The number of blocks to prove in a single epoch', + ...numberConfigHelper(2), + }, }; export const proverNodeConfigMappings: ConfigMappingsType = { diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index 7e9c31e8cbf..e6dd1e3524b 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -56,6 +56,7 @@ export async function createProverNode( { disableAutomaticProving: config.proverNodeDisableAutomaticProving, maxPendingJobs: config.proverNodeMaxPendingJobs, + epochSize: config.proverNodeEpochSize, }, ); } diff --git a/yarn-project/prover-node/src/job/block-proving-job.ts b/yarn-project/prover-node/src/job/epoch-proving-job.ts similarity index 67% rename from yarn-project/prover-node/src/job/block-proving-job.ts rename to yarn-project/prover-node/src/job/epoch-proving-job.ts index 4c11f152210..d104bfc52dc 100644 --- a/yarn-project/prover-node/src/job/block-proving-job.ts +++ b/yarn-project/prover-node/src/job/epoch-proving-job.ts @@ -1,6 +1,6 @@ import { - type BlockProver, EmptyTxValidator, + type EpochProver, type L1ToL2MessageSource, type L2Block, type L2BlockSource, @@ -24,20 +24,20 @@ import { type ProverNodeMetrics } from '../metrics.js'; * re-executes their public calls, generates a rollup proof, and submits it to L1. This job will update the * world state as part of public call execution via the public processor. */ -export class BlockProvingJob { - private state: BlockProvingJobState = 'initialized'; - private log = createDebugLogger('aztec:block-proving-job'); +export class EpochProvingJob { + private state: EpochProvingJobState = 'initialized'; + private log = createDebugLogger('aztec:epoch-proving-job'); private uuid: string; constructor( - private prover: BlockProver, + private prover: EpochProver, private publicProcessorFactory: PublicProcessorFactory, private publisher: L1Publisher, private l2BlockSource: L2BlockSource, private l1ToL2MessageSource: L1ToL2MessageSource, private txProvider: TxProvider, private metrics: ProverNodeMetrics, - private cleanUp: (job: BlockProvingJob) => Promise = () => Promise.resolve(), + private cleanUp: (job: EpochProvingJob) => Promise = () => Promise.resolve(), ) { this.uuid = crypto.randomUUID(); } @@ -46,26 +46,38 @@ export class BlockProvingJob { return this.uuid; } - public getState(): BlockProvingJobState { + public getState(): EpochProvingJobState { return this.state; } + /** + * Proves the given block range and submits the proof to L1. + * @param fromBlock - Start block. + * @param toBlock - Last block (inclusive). + */ public async run(fromBlock: number, toBlock: number) { - if (fromBlock !== toBlock) { - throw new Error(`Block ranges are not yet supported`); + if (fromBlock > toBlock) { + throw new Error(`Invalid block range: ${fromBlock} to ${toBlock}`); } - this.log.info(`Starting block proving job`, { fromBlock, toBlock, uuid: this.uuid }); + const epochNumber = fromBlock; // Use starting block number as epoch number + const epochSize = toBlock - fromBlock + 1; + this.log.info(`Starting epoch proving job`, { fromBlock, toBlock, epochNumber, uuid: this.uuid }); this.state = 'processing'; const timer = new Timer(); + try { - let historicalHeader = (await this.l2BlockSource.getBlock(fromBlock - 1))?.header; + const provingTicket = this.prover.startNewEpoch(epochNumber, epochSize); + let previousHeader = (await this.l2BlockSource.getBlock(fromBlock - 1))?.header; + for (let blockNumber = fromBlock; blockNumber <= toBlock; blockNumber++) { + // Gather all data to prove this block const block = await this.getBlock(blockNumber); const globalVariables = block.header.globalVariables; const txHashes = block.body.txEffects.map(tx => tx.txHash); const txCount = block.body.numberOfTxsIncludingPadded; const l1ToL2Messages = await this.getL1ToL2Messages(block); + const txs = await this.getTxs(txHashes); this.log.verbose(`Starting block processing`, { number: block.number, @@ -74,54 +86,48 @@ export class BlockProvingJob { noteHashTreeRoot: block.header.state.partial.noteHashTree.root, nullifierTreeRoot: block.header.state.partial.nullifierTree.root, publicDataTreeRoot: block.header.state.partial.publicDataTree.root, - historicalHeader: historicalHeader?.hash(), + previousHeader: previousHeader?.hash(), uuid: this.uuid, ...globalVariables, }); - // When we move to proving epochs, this should change into a startNewEpoch and be lifted outside the loop. - const provingTicket = await this.prover.startNewBlock(txCount, globalVariables, l1ToL2Messages); - - const publicProcessor = this.publicProcessorFactory.create(historicalHeader, globalVariables); + // Start block proving + await this.prover.startNewBlock(txCount, globalVariables, l1ToL2Messages); - const txs = await this.getTxs(txHashes); + // Process public fns + const publicProcessor = this.publicProcessorFactory.create(previousHeader, globalVariables); await this.processTxs(publicProcessor, txs, txCount); - this.log.verbose(`Processed all txs for block`, { blockNumber: block.number, blockHash: block.hash().toString(), uuid: this.uuid, }); + // Mark block as completed and update archive tree await this.prover.setBlockCompleted(); + previousHeader = block.header; + } - // This should be moved outside the loop to match the creation of the proving ticket when we move to epochs. - this.state = 'awaiting-prover'; - const result = await provingTicket.provingPromise; - if (result.status === PROVING_STATUS.FAILURE) { - throw new Error(`Block proving failed: ${result.reason}`); - } + // Pad epoch with empty block proofs if needed + this.prover.setEpochCompleted(); - historicalHeader = block.header; + this.state = 'awaiting-prover'; + const result = await provingTicket.provingPromise; + if (result.status === PROVING_STATUS.FAILURE) { + throw new Error(`Epoch proving failed: ${result.reason}`); } - const { block, aggregationObject, proof } = await this.prover.finaliseBlock(); - this.log.info(`Finalised proof for block range`, { fromBlock, toBlock, uuid: this.uuid }); + const { publicInputs, proof } = this.prover.finaliseEpoch(); + this.log.info(`Finalised proof for epoch`, { epochNumber, fromBlock, toBlock, uuid: this.uuid }); this.state = 'publishing-proof'; - await this.publisher.submitBlockProof( - block.header, - block.archive.root, - this.prover.getProverId(), - aggregationObject, - proof, - ); - this.log.info(`Submitted proof for block range`, { fromBlock, toBlock, uuid: this.uuid }); + await this.publisher.submitEpochProof({ epochNumber, fromBlock, toBlock, publicInputs, proof }); + this.log.info(`Submitted proof for epoch`, { epochNumber, fromBlock, toBlock, uuid: this.uuid }); this.state = 'completed'; this.metrics.recordProvingJob(timer); } catch (err) { - this.log.error(`Error running block prover job`, err, { uuid: this.uuid }); + this.log.error(`Error running epoch prover job`, err, { uuid: this.uuid }); this.state = 'failed'; } finally { await this.cleanUp(this); @@ -177,7 +183,7 @@ export class BlockProvingJob { } } -export type BlockProvingJobState = +export type EpochProvingJobState = | 'initialized' | 'processing' | 'awaiting-prover' diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index e5cd74cfa32..6e52f77a0d4 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -1,8 +1,8 @@ import { + type EpochProverManager, type L1ToL2MessageSource, type L2BlockSource, type MerkleTreeAdminOperations, - type ProverClient, type TxProvider, WorldStateRunningState, type WorldStateSynchronizer, @@ -14,11 +14,11 @@ import { type ContractDataSource } from '@aztec/types/contracts'; import { type MockProxy, mock } from 'jest-mock-extended'; -import { type BlockProvingJob } from './job/block-proving-job.js'; +import { type EpochProvingJob } from './job/epoch-proving-job.js'; import { ProverNode } from './prover-node.js'; describe('prover-node', () => { - let prover: MockProxy; + let prover: MockProxy; let publisher: MockProxy; let l2BlockSource: MockProxy; let l1ToL2MessageSource: MockProxy; @@ -31,13 +31,13 @@ describe('prover-node', () => { // List of all jobs ever created by the test prover node and their dependencies let jobs: { - job: MockProxy; - cleanUp: (job: BlockProvingJob) => Promise; + job: MockProxy; + cleanUp: (job: EpochProvingJob) => Promise; db: MerkleTreeAdminOperations; }[]; beforeEach(() => { - prover = mock(); + prover = mock(); publisher = mock(); l2BlockSource = mock(); l1ToL2MessageSource = mock(); @@ -61,7 +61,7 @@ describe('prover-node', () => { txProvider, simulator, telemetryClient, - { maxPendingJobs: 3, pollingIntervalMs: 10 }, + { maxPendingJobs: 3, pollingIntervalMs: 10, epochSize: 2 }, ); }); @@ -82,13 +82,12 @@ describe('prover-node', () => { await proverNode.work(); await proverNode.work(); - expect(jobs.length).toEqual(2); - expect(jobs[0].job.run).toHaveBeenCalledWith(4, 4); - expect(jobs[1].job.run).toHaveBeenCalledWith(5, 5); + expect(jobs.length).toEqual(1); + expect(jobs[0].job.run).toHaveBeenCalledWith(4, 5); }); it('stops proving when maximum jobs are reached', async () => { - setBlockNumbers(10, 3); + setBlockNumbers(20, 3); await proverNode.work(); await proverNode.work(); @@ -96,13 +95,13 @@ describe('prover-node', () => { await proverNode.work(); expect(jobs.length).toEqual(3); - expect(jobs[0].job.run).toHaveBeenCalledWith(4, 4); - expect(jobs[1].job.run).toHaveBeenCalledWith(5, 5); - expect(jobs[2].job.run).toHaveBeenCalledWith(6, 6); + expect(jobs[0].job.run).toHaveBeenCalledWith(4, 5); + expect(jobs[1].job.run).toHaveBeenCalledWith(6, 7); + expect(jobs[2].job.run).toHaveBeenCalledWith(8, 9); }); it('reports on pending jobs', async () => { - setBlockNumbers(5, 3); + setBlockNumbers(8, 3); await proverNode.work(); await proverNode.work(); @@ -116,7 +115,7 @@ describe('prover-node', () => { }); it('cleans up jobs when completed', async () => { - setBlockNumbers(10, 3); + setBlockNumbers(20, 3); await proverNode.work(); await proverNode.work(); @@ -124,10 +123,6 @@ describe('prover-node', () => { await proverNode.work(); expect(jobs.length).toEqual(3); - expect(jobs[0].job.run).toHaveBeenCalledWith(4, 4); - expect(jobs[1].job.run).toHaveBeenCalledWith(5, 5); - expect(jobs[2].job.run).toHaveBeenCalledWith(6, 6); - expect(proverNode.getJobs().length).toEqual(3); // Clean up the first job @@ -138,7 +133,7 @@ describe('prover-node', () => { // Request another job to run and ensure it gets pushed await proverNode.work(); expect(jobs.length).toEqual(4); - expect(jobs[3].job.run).toHaveBeenCalledWith(7, 7); + expect(jobs[3].job.run).toHaveBeenCalledWith(10, 11); expect(proverNode.getJobs().length).toEqual(3); expect(proverNode.getJobs().map(({ uuid }) => uuid)).toEqual(['1', '2', '3']); }); @@ -147,7 +142,7 @@ describe('prover-node', () => { setBlockNumbers(10, 3); // We trigger an error by setting world state past the block that the prover node will try proving - worldState.status.mockResolvedValue({ syncedToL2Block: 5, state: WorldStateRunningState.RUNNING }); + worldState.status.mockResolvedValue({ syncedToL2Block: 7, state: WorldStateRunningState.RUNNING }); // These two calls should return in failures await proverNode.work(); @@ -157,16 +152,16 @@ describe('prover-node', () => { // But now the prover node should move forward await proverNode.work(); expect(jobs.length).toEqual(1); - expect(jobs[0].job.run).toHaveBeenCalledWith(6, 6); + expect(jobs[0].job.run).toHaveBeenCalledWith(8, 9); }); class TestProverNode extends ProverNode { - protected override doCreateBlockProvingJob( + protected override doCreateEpochProvingJob( db: MerkleTreeAdminOperations, _publicProcessorFactory: PublicProcessorFactory, - cleanUp: (job: BlockProvingJob) => Promise, - ): BlockProvingJob { - const job = mock({ getState: () => 'processing' }); + cleanUp: (job: EpochProvingJob) => Promise, + ): EpochProvingJob { + const job = mock({ getState: () => 'processing' }); job.getId.mockReturnValue(jobs.length.toString()); jobs.push({ job, cleanUp, db }); return job; diff --git a/yarn-project/prover-node/src/prover-node.ts b/yarn-project/prover-node/src/prover-node.ts index 02b3a1b4448..d31e05b215a 100644 --- a/yarn-project/prover-node/src/prover-node.ts +++ b/yarn-project/prover-node/src/prover-node.ts @@ -1,11 +1,12 @@ import { + type EpochProverManager, type L1ToL2MessageSource, type L2BlockSource, type MerkleTreeOperations, - type ProverClient, type TxProvider, type WorldStateSynchronizer, } from '@aztec/circuit-types'; +import { compact } from '@aztec/foundation/collection'; import { createDebugLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; import { type L1Publisher } from '@aztec/sequencer-client'; @@ -13,9 +14,16 @@ import { PublicProcessorFactory, type SimulationProvider } from '@aztec/simulato import { type TelemetryClient } from '@aztec/telemetry-client'; import { type ContractDataSource } from '@aztec/types/contracts'; -import { BlockProvingJob, type BlockProvingJobState } from './job/block-proving-job.js'; +import { EpochProvingJob, type EpochProvingJobState } from './job/epoch-proving-job.js'; import { ProverNodeMetrics } from './metrics.js'; +type ProverNodeOptions = { + pollingIntervalMs: number; + disableAutomaticProving: boolean; + maxPendingJobs: number; + epochSize: number; +}; + /** * An Aztec Prover Node is a standalone process that monitors the unfinalised chain on L1 for unproven blocks, * fetches their txs from a tx source in the p2p network or an external node, re-executes their public functions, @@ -25,12 +33,12 @@ export class ProverNode { private log = createDebugLogger('aztec:prover-node'); private runningPromise: RunningPromise | undefined; private latestBlockWeAreProving: number | undefined; - private jobs: Map = new Map(); - private options: { pollingIntervalMs: number; disableAutomaticProving: boolean; maxPendingJobs: number }; + private jobs: Map = new Map(); + private options: ProverNodeOptions; private metrics: ProverNodeMetrics; constructor( - private prover: ProverClient, + private prover: EpochProverManager, private publisher: L1Publisher, private l2BlockSource: L2BlockSource, private l1ToL2MessageSource: L1ToL2MessageSource, @@ -39,13 +47,14 @@ export class ProverNode { private txProvider: TxProvider, private simulator: SimulationProvider, private telemetryClient: TelemetryClient, - options: { pollingIntervalMs?: number; disableAutomaticProving?: boolean; maxPendingJobs?: number } = {}, + options: Partial = {}, ) { this.options = { pollingIntervalMs: 1_000, disableAutomaticProving: false, maxPendingJobs: 100, - ...options, + epochSize: 2, + ...compact(options), }; this.metrics = new ProverNodeMetrics(telemetryClient, 'ProverNode'); @@ -58,7 +67,7 @@ export class ProverNode { start() { this.runningPromise = new RunningPromise(this.work.bind(this), this.options.pollingIntervalMs); this.runningPromise.start(); - this.log.info('Started ProverNode'); + this.log.info('Started ProverNode', this.options); } /** @@ -101,8 +110,8 @@ export class ProverNode { // Consider both the latest block we are proving and the last block proven on the chain const latestBlockBeingProven = this.latestBlockWeAreProving ?? 0; const latestProven = Math.max(latestBlockBeingProven, latestProvenBlockNumber); - if (latestProven >= latestBlockNumber) { - this.log.debug(`No new blocks to prove`, { + if (latestBlockNumber - latestProven < this.options.epochSize) { + this.log.debug(`No epoch to prove`, { latestBlockNumber, latestProvenBlockNumber, latestBlockBeingProven, @@ -111,7 +120,7 @@ export class ProverNode { } const fromBlock = latestProven + 1; - const toBlock = fromBlock; // We only prove one block at a time for now + const toBlock = fromBlock + this.options.epochSize - 1; try { await this.startProof(fromBlock, toBlock); @@ -151,7 +160,7 @@ export class ProverNode { /** * Returns an array of jobs being processed. */ - public getJobs(): { uuid: string; status: BlockProvingJobState }[] { + public getJobs(): { uuid: string; status: EpochProvingJobState }[] { return Array.from(this.jobs.entries()).map(([uuid, job]) => ({ uuid, status: job.getState() })); } @@ -186,19 +195,19 @@ export class ProverNode { this.jobs.delete(job.getId()); }; - const job = this.doCreateBlockProvingJob(db, publicProcessorFactory, cleanUp); + const job = this.doCreateEpochProvingJob(db, publicProcessorFactory, cleanUp); this.jobs.set(job.getId(), job); return job; } /** Extracted for testing purposes. */ - protected doCreateBlockProvingJob( + protected doCreateEpochProvingJob( db: MerkleTreeOperations, publicProcessorFactory: PublicProcessorFactory, cleanUp: () => Promise, ) { - return new BlockProvingJob( - this.prover.createBlockProver(db), + return new EpochProvingJob( + this.prover.createEpochProver(db), publicProcessorFactory, this.publisher, this.l2BlockSource, diff --git a/yarn-project/sequencer-client/src/publisher/index.ts b/yarn-project/sequencer-client/src/publisher/index.ts index e51b4d3cdea..97e14e96262 100644 --- a/yarn-project/sequencer-client/src/publisher/index.ts +++ b/yarn-project/sequencer-client/src/publisher/index.ts @@ -1,2 +1,2 @@ -export { L1Publisher } from './l1-publisher.js'; +export { L1Publisher, L1SubmitEpochProofArgs } from './l1-publisher.js'; export * from './config.js'; diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index 447e42b3dcf..d2abdef3927 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -1,10 +1,19 @@ import { ConsensusPayload, type L2Block, type TxHash, getHashedSignaturePayload } from '@aztec/circuit-types'; import { type L1PublishBlockStats, type L1PublishProofStats } from '@aztec/circuit-types/stats'; -import { ETHEREUM_SLOT_DURATION, EthAddress, type FeeRecipient, type Header, type Proof } from '@aztec/circuits.js'; +import { + AGGREGATION_OBJECT_LENGTH, + ETHEREUM_SLOT_DURATION, + EthAddress, + type FeeRecipient, + type Header, + type Proof, + type RootRollupPublicInputs, +} from '@aztec/circuits.js'; import { createEthereumChain } from '@aztec/ethereum'; import { makeTuple } from '@aztec/foundation/array'; +import { areArraysEqual, times } from '@aztec/foundation/collection'; import { type Signature } from '@aztec/foundation/eth-signature'; -import { type Fr } from '@aztec/foundation/fields'; +import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; import { InterruptibleSleep } from '@aztec/foundation/sleep'; @@ -95,7 +104,7 @@ type L1SubmitBlockProofArgs = { }; /** Arguments to the submitEpochProof method of the rollup contract */ -type L1SubmitEpochProofArgs = { +export type L1SubmitEpochProofArgs = { epochSize: number; previousArchive: Fr; endArchive: Fr; @@ -106,7 +115,6 @@ type L1SubmitEpochProofArgs = { proverId: Fr; fees: Tuple; proof: Proof; - aggregationObject: Fr[]; }; /** @@ -306,8 +314,10 @@ export class L1Publisher { } this.metrics.recordFailedTx('process'); - - this.log.error(`Rollup.process tx status failed: ${receipt.transactionHash}`, ctx); + this.log.error(`Rollup.process tx status failed ${receipt.transactionHash}`, { + ...ctx, + ...receipt, + }); await this.sleepOrInterrupted(); } @@ -367,13 +377,21 @@ export class L1Publisher { return false; } - public async submitEpochProof( - args: L1SubmitEpochProofArgs, - ctx: { blockNumber: number; slotNumber: number }, - ): Promise { - // Process block + public async submitEpochProof(args: { + epochNumber: number; + fromBlock: number; + toBlock: number; + publicInputs: RootRollupPublicInputs; + proof: Proof; + }): Promise { + const { epochNumber, fromBlock, toBlock } = args; + const ctx = { epochNumber, fromBlock, toBlock }; if (!this.interrupted) { const timer = new Timer(); + + // Validate epoch proof range and hashes are correct before submitting + await this.validateEpochProofSubmission(args); + const txHash = await this.sendSubmitEpochProofTx(args); if (!txHash) { return false; @@ -406,6 +424,63 @@ export class L1Publisher { return false; } + private async validateEpochProofSubmission(args: { + fromBlock: number; + toBlock: number; + publicInputs: RootRollupPublicInputs; + proof: Proof; + }) { + const { fromBlock, toBlock, publicInputs, proof } = args; + + // Check that the block numbers match the expected epoch to be proven + const [pending, proven] = await this.rollupContract.read.tips(); + if (proven !== BigInt(fromBlock) - 1n) { + throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as proven block is ${proven}`); + } + if (toBlock > pending) { + throw new Error(`Cannot submit epoch proof for ${fromBlock}-${toBlock} as pending block is ${pending}`); + } + + // Check the block hash and archive for the immediate block before the epoch + const [previousArchive, previousBlockHash] = await this.rollupContract.read.blocks([proven]); + if (publicInputs.previousArchive.root.toString() !== previousArchive) { + throw new Error( + `Previous archive root mismatch: ${publicInputs.previousArchive.root.toString()} !== ${previousArchive}`, + ); + } + // TODO: Remove zero check once we inject the proper zero blockhash + if (previousBlockHash !== Fr.ZERO.toString() && publicInputs.previousBlockHash.toString() !== previousBlockHash) { + throw new Error( + `Previous block hash mismatch: ${publicInputs.previousBlockHash.toString()} !== ${previousBlockHash}`, + ); + } + + // Check the block hash and archive for the last block in the epoch + const [endArchive, endBlockHash] = await this.rollupContract.read.blocks([BigInt(toBlock)]); + if (publicInputs.endArchive.root.toString() !== endArchive) { + throw new Error(`End archive root mismatch: ${publicInputs.endArchive.root.toString()} !== ${endArchive}`); + } + if (publicInputs.endBlockHash.toString() !== endBlockHash) { + throw new Error(`End block hash mismatch: ${publicInputs.endBlockHash.toString()} !== ${endBlockHash}`); + } + + // Compare the public inputs computed by the contract with the ones injected + const rollupPublicInputs = await this.rollupContract.read.getEpochProofPublicInputs( + this.getSubmitEpochProofArgs(args), + ); + const aggregationObject = proof.isEmpty() + ? times(AGGREGATION_OBJECT_LENGTH, Fr.zero) + : proof.extractAggregationObject(); + const argsPublicInputs = [...publicInputs.toFields(), ...aggregationObject]; + + if (!areArraysEqual(rollupPublicInputs.map(Fr.fromString), argsPublicInputs, (a, b) => a.equals(b))) { + const fmt = (inputs: Fr[] | readonly string[]) => inputs.map(x => x.toString()).join(', '); + throw new Error( + `Root rollup public inputs mismatch:\nRollup: ${fmt(rollupPublicInputs)}\nComputed:${fmt(argsPublicInputs)}`, + ); + } + } + /** * Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap. * Be warned, the call may return false even if the tx subsequently gets successfully mined. @@ -449,26 +524,15 @@ export class L1Publisher { } } - private async sendSubmitEpochProofTx(args: L1SubmitEpochProofArgs): Promise { + private async sendSubmitEpochProofTx(args: { + fromBlock: number; + toBlock: number; + publicInputs: RootRollupPublicInputs; + proof: Proof; + }): Promise { try { - const txArgs = [ - BigInt(args.epochSize), - [ - args.previousArchive.toString(), - args.endArchive.toString(), - args.previousBlockHash.toString(), - args.endBlockHash.toString(), - args.endTimestamp.toString(), - args.outHash.toString(), - args.proverId.toString(), - ], - makeTuple(64, i => - i % 2 === 0 ? args.fees[i / 2].recipient.toString() : args.fees[(i - 1) / 2].value.toString(), - ), - `0x${serializeToBuffer(args.aggregationObject).toString('hex')}`, - `0x${args.proof.withoutPublicInputs().toString('hex')}`, - ] as const; - + const proofHex: Hex = `0x${args.proof.withoutPublicInputs().toString('hex')}`; + const txArgs = [...this.getSubmitEpochProofArgs(args), proofHex] as const; this.log.info(`SubmitEpochProof proofSize=${args.proof.withoutPublicInputs().length} bytes`); await this.rollupContract.simulate.submitEpochRootProof(txArgs, { account: this.account }); return await this.rollupContract.write.submitEpochRootProof(txArgs, { account: this.account }); @@ -478,6 +542,32 @@ export class L1Publisher { } } + private getSubmitEpochProofArgs(args: { + fromBlock: number; + toBlock: number; + publicInputs: RootRollupPublicInputs; + proof: Proof; + }) { + return [ + BigInt(args.toBlock - args.fromBlock + 1), + [ + args.publicInputs.previousArchive.root.toString(), + args.publicInputs.endArchive.root.toString(), + args.publicInputs.previousBlockHash.toString(), + args.publicInputs.endBlockHash.toString(), + args.publicInputs.endTimestamp.toString(), + args.publicInputs.outHash.toString(), + args.publicInputs.proverId.toString(), + ], + makeTuple(64, i => + i % 2 === 0 + ? args.publicInputs.fees[i / 2].recipient.toField().toString() + : args.publicInputs.fees[(i - 1) / 2].value.toString(), + ), + `0x${serializeToBuffer(args.proof.extractAggregationObject()).toString('hex')}`, + ] as const; + } + private async sendProposeTx(encodedData: L1ProcessArgs): Promise { if (!this.interrupted) { try {