From 2f61b63c23029559b49c630ae3bdf493b6760478 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Thu, 27 Apr 2023 11:12:36 -0300 Subject: [PATCH] Connect public kernel circuit and processor to sequencer --- .../circuit_block_builder.test.ts | 44 ++++++++++++++++--- .../block_builder/circuit_block_builder.ts | 10 +++-- .../src/client/sequencer-client.ts | 15 +++---- .../src/sequencer/public_processor.test.ts | 29 +++++++++++- .../src/sequencer/sequencer.ts | 4 +- .../src/simulator/mock_public_kernel.ts | 14 ------ .../src/simulator/public_kernel.ts | 22 ++++++++++ .../src/simulator/{wasm.ts => rollup.ts} | 2 +- 8 files changed, 102 insertions(+), 38 deletions(-) delete mode 100644 yarn-project/sequencer-client/src/simulator/mock_public_kernel.ts create mode 100644 yarn-project/sequencer-client/src/simulator/public_kernel.ts rename yarn-project/sequencer-client/src/simulator/{wasm.ts => rollup.ts} (93%) diff --git a/yarn-project/sequencer-client/src/block_builder/circuit_block_builder.test.ts b/yarn-project/sequencer-client/src/block_builder/circuit_block_builder.test.ts index 18828729573..f55806304c8 100644 --- a/yarn-project/sequencer-client/src/block_builder/circuit_block_builder.test.ts +++ b/yarn-project/sequencer-client/src/block_builder/circuit_block_builder.test.ts @@ -4,14 +4,18 @@ import { BaseRollupInputs, CircuitsWasm, Fr, + PublicDataRead, + PublicDataWrite, RootRollupPublicInputs, UInt8Vector, } from '@aztec/circuits.js'; import { computeContractLeaf } from '@aztec/circuits.js/abis'; import { + fr, makeBaseRollupPublicInputs, makeKernelPublicInputs, makeNewContractData, + makeProof, makeRootRollupPublicInputs, } from '@aztec/circuits.js/factories'; import { toBufferBE } from '@aztec/foundation'; @@ -22,13 +26,13 @@ import { default as levelup } from 'levelup'; import flatMap from 'lodash.flatmap'; import times from 'lodash.times'; import { default as memdown, type MemDown } from 'memdown'; -import { makeEmptyUnverifiedData } from '../mocks/tx.js'; +import { makeEmptyUnverifiedData, makePublicTx } from '../mocks/tx.js'; import { VerificationKeys, getVerificationKeys } from '../mocks/verification_keys.js'; import { EmptyRollupProver } from '../prover/empty.js'; import { RollupProver } from '../prover/index.js'; import { ProcessedTx, makeEmptyProcessedTx, makeProcessedTx } from '../sequencer/processed_tx.js'; import { RollupSimulator } from '../simulator/index.js'; -import { WasmCircuitSimulator } from '../simulator/wasm.js'; +import { WasmRollupCircuitSimulator } from '../simulator/rollup.js'; import { CircuitBlockBuilder } from './circuit_block_builder.js'; export const createMemDown = () => (memdown as any)() as MemDown; @@ -197,19 +201,28 @@ describe('sequencer/circuit_block_builder', () => { describe('circuits simulator', () => { beforeEach(async () => { - const simulator = await WasmCircuitSimulator.new(); + const simulator = await WasmRollupCircuitSimulator.new(); const prover = new EmptyRollupProver(); builder = new TestSubject(builderDb, vks, simulator, prover); await builder.updateRootTrees(); }); - const makeContractDeployTx = async (seed = 0x1) => { + const makeContractDeployProcessedTx = async (seed = 0x1) => { const tx = await makeEmptyProcessedTx(); await setTxHistoricTreeRoots(tx); tx.data.end.newContracts = [makeNewContractData(seed + 0x1000)]; return tx; }; + const makePublicCallProcessedTx = async (seed = 0x1) => { + const publicTx = makePublicTx(seed); + const tx = await makeProcessedTx(publicTx, makeKernelPublicInputs(seed), makeProof()); + await setTxHistoricTreeRoots(tx); + tx.data.end.stateReads[0] = new PublicDataRead(fr(1), fr(0)); + tx.data.end.stateTransitions[0] = new PublicDataWrite(fr(2), fr(0), fr(12)); + return tx; + }; + it.each([ [0, 4], [1, 4], @@ -222,7 +235,7 @@ describe('sequencer/circuit_block_builder', () => { const contractTreeBefore = await builderDb.getTreeInfo(MerkleTreeId.CONTRACT_TREE); const txs = [ - ...(await Promise.all(times(deployCount, makeContractDeployTx))), + ...(await Promise.all(times(deployCount, makeContractDeployProcessedTx))), ...(await Promise.all(times(totalCount - deployCount, makeEmptyProcessedTx))), ]; @@ -243,9 +256,26 @@ describe('sequencer/circuit_block_builder', () => { 10000, ); + it('builds an L2 block with private and public txs', async () => { + const txs = await Promise.all([ + makeContractDeployProcessedTx(), + makePublicCallProcessedTx(), + makeEmptyProcessedTx(), + makeEmptyProcessedTx(), + ]); + + const [l2Block] = await builder.buildL2Block(blockNumber, txs); + expect(l2Block.number).toEqual(blockNumber); + + // TODO: Check that l2 block got the new state transitions once we merge + // https://github.com/AztecProtocol/aztec3-packages/pull/360 + + await updateExpectedTreesFromTxs(txs); + }); + // This test specifically tests nullifier values which previously caused e2e_zk_token test to fail - it('e2e edge case - regression test', async () => { - const simulator = await WasmCircuitSimulator.new(); + it('e2e_zk_token edge case regression test on nullifier values', async () => { + const simulator = await WasmRollupCircuitSimulator.new(); const prover = new EmptyRollupProver(); builder = new TestSubject(builderDb, vks, simulator, prover); // update the starting tree diff --git a/yarn-project/sequencer-client/src/block_builder/circuit_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/circuit_block_builder.ts index ecd4818f133..953bca2abb9 100644 --- a/yarn-project/sequencer-client/src/block_builder/circuit_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/circuit_block_builder.ts @@ -17,7 +17,6 @@ import { RollupTypes, RootRollupInputs, RootRollupPublicInputs, - STATE_TRANSITIONS_LENGTH, UInt8Vector, VK_TREE_HEIGHT, VerificationKey, @@ -253,6 +252,7 @@ export class CircuitBlockBuilder implements BlockBuilder { this.validateTree(rollupOutput, MerkleTreeId.CONTRACT_TREE, 'Contract'), this.validateTree(rollupOutput, MerkleTreeId.PRIVATE_DATA_TREE, 'PrivateData'), this.validateTree(rollupOutput, MerkleTreeId.NULLIFIER_TREE, 'Nullifier'), + this.validateTree(rollupOutput, MerkleTreeId.PUBLIC_DATA_TREE, 'PublicData'), ]); } @@ -280,7 +280,7 @@ export class CircuitBlockBuilder implements BlockBuilder { protected async validateTree( output: BaseOrMergeRollupPublicInputs | RootRollupPublicInputs, treeId: MerkleTreeId, - name: 'PrivateData' | 'Contract' | 'Nullifier', + name: 'PrivateData' | 'Contract' | 'Nullifier' | 'PublicData', ) { const localTree = await this.getTreeSnapshot(treeId); const simulatedTree = output[`end${name}TreeSnapshot`]; @@ -291,13 +291,15 @@ export class CircuitBlockBuilder implements BlockBuilder { protected validateSimulatedTree( localTree: AppendOnlyTreeSnapshot, simulatedTree: AppendOnlyTreeSnapshot, - name: string, + name: 'PrivateData' | 'Contract' | 'Nullifier' | 'PublicData', label?: string, ) { if (!simulatedTree.root.toBuffer().equals(localTree.root.toBuffer())) { throw new Error(`${label ?? name} tree root mismatch (local ${localTree.root}, simulated ${simulatedTree.root})`); } - if (simulatedTree.nextAvailableLeafIndex !== localTree.nextAvailableLeafIndex) { + // Public data tree is sparse, so the "next available leaf index" doesn't make sense there + // We'll eventually drop it, see https://github.com/AztecProtocol/aztec3-packages/issues/379 + if (name !== 'PublicData' && simulatedTree.nextAvailableLeafIndex !== localTree.nextAvailableLeafIndex) { throw new Error( `${label ?? name} tree next available leaf index mismatch (local ${ localTree.nextAvailableLeafIndex diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index 9c3fc23d3f4..c6de52620ed 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -1,15 +1,15 @@ import { P2P } from '@aztec/p2p'; import { WorldStateSynchroniser } from '@aztec/world-state'; +import { ContractDataSource } from '@aztec/types'; import { CircuitBlockBuilder } from '../block_builder/circuit_block_builder.js'; import { SequencerClientConfig } from '../config.js'; import { getL1Publisher, getVerificationKeys, Sequencer } from '../index.js'; import { EmptyPublicProver, EmptyRollupProver } from '../prover/empty.js'; -import { MockPublicProcessor } from '../sequencer/public_processor.js'; +import { PublicProcessor } from '../sequencer/public_processor.js'; import { FakePublicCircuitSimulator } from '../simulator/fake_public.js'; -import { MockPublicKernelCircuitSimulator } from '../simulator/mock_public_kernel.js'; -import { WasmCircuitSimulator } from '../simulator/wasm.js'; -import { ContractDataSource } from '@aztec/types'; +import { WasmPublicKernelCircuitSimulator } from '../simulator/public_kernel.js'; +import { WasmRollupCircuitSimulator } from '../simulator/rollup.js'; /** * Encapsulates the full sequencer and publisher. @@ -29,15 +29,14 @@ export class SequencerClient { const blockBuilder = new CircuitBlockBuilder( merkleTreeDb, getVerificationKeys(), - await WasmCircuitSimulator.new(), + await WasmRollupCircuitSimulator.new(), new EmptyRollupProver(), ); - // TODO: Swap with actual processor once the integration is good to go - const publicProcessor = new MockPublicProcessor( + const publicProcessor = new PublicProcessor( merkleTreeDb, new FakePublicCircuitSimulator(merkleTreeDb), - new MockPublicKernelCircuitSimulator(), + new WasmPublicKernelCircuitSimulator(), new EmptyPublicProver(), contractDataSource, ); diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts index 6597e91678b..71238fb8e97 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -1,14 +1,16 @@ import { PUBLIC_DATA_TREE_HEIGHT, makeEmptyProof } from '@aztec/circuits.js'; import { makeKernelPublicInputs, makePublicCircuitPublicInputs } from '@aztec/circuits.js/factories'; import { SiblingPath } from '@aztec/merkle-tree'; -import { MerkleTreeOperations, TreeInfo } from '@aztec/world-state'; import { CompleteContractData, ContractData, ContractDataSource, EncodedContractFunction } from '@aztec/types'; +import { MerkleTreeOperations, TreeInfo } from '@aztec/world-state'; +import { jest } from '@jest/globals'; import { MockProxy, mock } from 'jest-mock-extended'; import pick from 'lodash.pick'; import times from 'lodash.times'; import { makePrivateTx, makePublicTx } from '../index.js'; import { Proof, PublicProver } from '../prover/index.js'; import { PublicCircuitSimulator, PublicKernelCircuitSimulator } from '../simulator/index.js'; +import { WasmPublicKernelCircuitSimulator } from '../simulator/public_kernel.js'; import { PublicProcessor } from './public_processor.js'; describe('public_processor', () => { @@ -65,7 +67,7 @@ describe('public_processor', () => { expect(failed).toEqual([tx]); }); - it('runs a public tx through the public and public kernel circuits', async function () { + it('runs a public tx through mock circuits', async function () { const publicCircuitOutput = makePublicCircuitPublicInputs(); publicCircuit.publicCircuit.mockResolvedValue(publicCircuitOutput); @@ -85,4 +87,27 @@ describe('public_processor', () => { expect(publicCircuit.publicCircuit).toHaveBeenCalled(); expect(publicKernel.publicKernelCircuitNoInput).toHaveBeenCalled(); }); + + it('runs a public tx through the actual public kernel circuit', async function () { + const publicKernel = new WasmPublicKernelCircuitSimulator(); + const publicKernelSpy = jest.spyOn(publicKernel, 'publicKernelCircuitNoInput'); + processor = new PublicProcessor(db, publicCircuit, publicKernel, publicProver, contractDataSource); + + const publicCircuitOutput = makePublicCircuitPublicInputs(); + publicCircuit.publicCircuit.mockResolvedValue(publicCircuitOutput); + + const path = times(PUBLIC_DATA_TREE_HEIGHT, i => Buffer.alloc(32, i)); + db.getSiblingPath.mockResolvedValue(new SiblingPath(path)); + + const tx = makePublicTx(); + const hash = await tx.getTxHash(); + const [processed, failed] = await processor.process([tx]); + + expect(processed[0].data.isPrivateKernel).toBeFalsy(); + expect(processed).toEqual([expect.objectContaining({ hash, proof, ...pick(tx, 'txRequest') })]); + expect(failed).toEqual([]); + + expect(publicCircuit.publicCircuit).toHaveBeenCalled(); + expect(publicKernelSpy).toHaveBeenCalled(); + }); }); diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 5c9db0d8791..c4b92263689 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -1,8 +1,8 @@ -import times from 'lodash.times'; import { RunningPromise, createDebugLogger } from '@aztec/foundation'; import { P2P } from '@aztec/p2p'; -import { CompleteContractData, ContractData, PrivateTx, PublicTx, Tx, UnverifiedData, isPrivateTx } from '@aztec/types'; +import { ContractData, PrivateTx, PublicTx, Tx, UnverifiedData, isPrivateTx } from '@aztec/types'; import { MerkleTreeId, WorldStateStatus, WorldStateSynchroniser } from '@aztec/world-state'; +import times from 'lodash.times'; import { BlockBuilder } from '../block_builder/index.js'; import { L1Publisher } from '../publisher/l1-publisher.js'; import { ceilPowerOfTwo } from '../utils.js'; diff --git a/yarn-project/sequencer-client/src/simulator/mock_public_kernel.ts b/yarn-project/sequencer-client/src/simulator/mock_public_kernel.ts deleted file mode 100644 index 7ec533f3de8..00000000000 --- a/yarn-project/sequencer-client/src/simulator/mock_public_kernel.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { PublicKernelInputsNoPreviousKernel, PublicKernelPublicInputs, PublicKernelInputs } from '@aztec/circuits.js'; -import { PublicKernelCircuitSimulator } from './index.js'; - -export class MockPublicKernelCircuitSimulator implements PublicKernelCircuitSimulator { - publicKernelCircuitNoInput(_inputs: PublicKernelInputsNoPreviousKernel): Promise { - return Promise.resolve(PublicKernelPublicInputs.empty()); - } - publicKernelCircuitPrivateInput(_inputs: PublicKernelInputs): Promise { - return Promise.resolve(PublicKernelPublicInputs.empty()); - } - publicKernelCircuitNonFirstIteration(_inputs: PublicKernelInputs): Promise { - return Promise.resolve(PublicKernelPublicInputs.empty()); - } -} diff --git a/yarn-project/sequencer-client/src/simulator/public_kernel.ts b/yarn-project/sequencer-client/src/simulator/public_kernel.ts new file mode 100644 index 00000000000..f8859fd2094 --- /dev/null +++ b/yarn-project/sequencer-client/src/simulator/public_kernel.ts @@ -0,0 +1,22 @@ +import { + PublicKernelInputs, + PublicKernelInputsNoPreviousKernel, + PublicKernelPublicInputs, + simulatePublicKernelCircuit, + simulatePublicKernelCircuitNoPreviousKernel, +} from '@aztec/circuits.js'; +import { PublicKernelCircuitSimulator } from './index.js'; + +export class WasmPublicKernelCircuitSimulator implements PublicKernelCircuitSimulator { + publicKernelCircuitNoInput(inputs: PublicKernelInputsNoPreviousKernel): Promise { + return simulatePublicKernelCircuitNoPreviousKernel(inputs); + } + publicKernelCircuitPrivateInput(inputs: PublicKernelInputs): Promise { + if (!inputs.previousKernel.publicInputs.isPrivateKernel) throw new Error(`Expected private kernel previous inputs`); + return simulatePublicKernelCircuit(inputs); + } + publicKernelCircuitNonFirstIteration(inputs: PublicKernelInputs): Promise { + if (inputs.previousKernel.publicInputs.isPrivateKernel) throw new Error(`Expected public kernel previous inputs`); + return simulatePublicKernelCircuit(inputs); + } +} diff --git a/yarn-project/sequencer-client/src/simulator/wasm.ts b/yarn-project/sequencer-client/src/simulator/rollup.ts similarity index 93% rename from yarn-project/sequencer-client/src/simulator/wasm.ts rename to yarn-project/sequencer-client/src/simulator/rollup.ts index 995d990d7fd..8f5688bc7ec 100644 --- a/yarn-project/sequencer-client/src/simulator/wasm.ts +++ b/yarn-project/sequencer-client/src/simulator/rollup.ts @@ -9,7 +9,7 @@ import { } from '@aztec/circuits.js'; import { RollupSimulator } from './index.js'; -export class WasmCircuitSimulator implements RollupSimulator { +export class WasmRollupCircuitSimulator implements RollupSimulator { private rollupWasmWrapper: RollupWasmWrapper; constructor(wasm: CircuitsWasm) {