From a1cf14ca1553df7e40e4a2e4e6ecd78842a17954 Mon Sep 17 00:00:00 2001 From: sklppy88 Date: Thu, 5 Dec 2024 18:42:23 +0000 Subject: [PATCH] init --- .../aztec-nr/authwit/src/cheatcodes.nr | 4 +- .../contracts/router_contract/src/test.nr | 4 +- yarn-project/txe/src/oracle/txe_oracle.ts | 276 ++++++++++++------ .../txe/src/txe_service/txe_service.ts | 55 ++-- .../txe/src/util/txe_world_state_db.ts | 12 +- 5 files changed, 214 insertions(+), 137 deletions(-) diff --git a/noir-projects/aztec-nr/authwit/src/cheatcodes.nr b/noir-projects/aztec-nr/authwit/src/cheatcodes.nr index 21347c52316..b828aeb0fff 100644 --- a/noir-projects/aztec-nr/authwit/src/cheatcodes.nr +++ b/noir-projects/aztec-nr/authwit/src/cheatcodes.nr @@ -17,7 +17,7 @@ where C: CallInterface, { let target = call_interface.get_contract_address(); - let inputs = cheatcodes::get_private_context_inputs(get_block_number()); + let inputs = cheatcodes::get_private_context_inputs(get_block_number() - 1); let chain_id = inputs.tx_context.chain_id; let version = inputs.tx_context.version; let args_hash = hash_args(call_interface.get_args()); @@ -39,7 +39,7 @@ where let current_contract = get_contract_address(); cheatcodes::set_contract_address(on_behalf_of); let target = call_interface.get_contract_address(); - let inputs = cheatcodes::get_private_context_inputs(get_block_number()); + let inputs = cheatcodes::get_private_context_inputs(get_block_number() - 1); let chain_id = inputs.tx_context.chain_id; let version = inputs.tx_context.version; let args_hash = hash_args(call_interface.get_args()); diff --git a/noir-projects/noir-contracts/contracts/router_contract/src/test.nr b/noir-projects/noir-contracts/contracts/router_contract/src/test.nr index a708a71ad11..300b958e36d 100644 --- a/noir-projects/noir-contracts/contracts/router_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/router_contract/src/test.nr @@ -10,7 +10,7 @@ unconstrained fn test_check_block_number() { let router_contract_address = router_contract.to_address(); let router = Router::at(router_contract_address); - env.advance_block_by(9); + env.advance_block_by(8); // First we sanity-check that current block number is as expected let current_block_number = env.block_number(); @@ -28,7 +28,7 @@ unconstrained fn test_fail_check_block_number() { let router_contract_address = router_contract.to_address(); let router = Router::at(router_contract_address); - env.advance_block_by(9); + env.advance_block_by(8); // First we sanity-check that current block number is as expected let current_block_number = env.block_number(); diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 35340639972..b83ab56a07e 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -1,6 +1,10 @@ import { AuthWitness, + Body, + L2Block, MerkleTreeId, + MerkleTreeReadOperations, + type MerkleTreeWriteOperations, Note, type NoteStatus, NullifierMembershipWitness, @@ -14,6 +18,7 @@ import { import { type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats'; import { BlockHeader, + AppendOnlyTreeSnapshot, CallContext, type ContractInstance, type ContractInstanceWithAddress, @@ -24,6 +29,8 @@ import { IndexedTaggingSecret, type KeyValidationRequest, type L1_TO_L2_MSG_TREE_HEIGHT, + MAX_NOTE_HASHES_PER_TX, + MAX_NULLIFIERS_PER_TX, NULLIFIER_SUBTREE_HEIGHT, type NULLIFIER_TREE_HEIGHT, type NullifierLeafPreimage, @@ -34,7 +41,7 @@ import { type PrivateLog, PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, - type PublicDataWrite, + PublicDataWrite, computeContractClassId, computeTaggingSecret, deriveKeys, @@ -48,6 +55,7 @@ import { siloNoteHash, siloNullifier, } from '@aztec/circuits.js/hash'; +import { makeAppendOnlyTreeSnapshot, makeHeader } from '@aztec/circuits.js/testing'; import { type ContractArtifact, type FunctionAbi, @@ -56,6 +64,7 @@ import { countArgumentsSize, } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { padArrayEnd } from '@aztec/foundation/collection'; import { poseidon2Hash } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; import { type Logger, applyStringFormatting } from '@aztec/foundation/log'; @@ -83,7 +92,7 @@ import { } from '@aztec/simulator'; import { createTxForPublicCall } from '@aztec/simulator/public/fixtures'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; -import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; +import { type MerkleTrees, type NativeWorldStateService } from '@aztec/world-state'; import { TXENode } from '../node/txe_node.js'; import { type TXEDatabase } from '../util/txe_database.js'; @@ -91,7 +100,7 @@ import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_so import { TXEWorldStateDB } from '../util/txe_world_state_db.js'; export class TXE implements TypedOracle { - private blockNumber = 0; + private blockNumber = 1; private sideEffectCounter = 0; private contractAddress: AztecAddress; private msgSender: AztecAddress; @@ -108,6 +117,11 @@ export class TXE implements TypedOracle { private siloedNoteHashesFromPublic: Fr[] = []; private siloedNullifiersFromPublic: Fr[] = []; + + private manuallyCreatedNoteHashes: Fr[] = []; + private manuallyCreatedNullifiers: Fr[] = []; + + private publicDataWrites: PublicDataWrite[] = []; private privateLogs: PrivateLog[] = []; private publicLogs: UnencryptedL2Log[] = []; @@ -122,6 +136,8 @@ export class TXE implements TypedOracle { private noteCache: ExecutionNoteCache, private keyStore: KeyStore, private txeDatabase: TXEDatabase, + private nativeWorldStateService: NativeWorldStateService, + private baseFork: MerkleTreeWriteOperations, ) { this.contractDataOracle = new ContractDataOracle(txeDatabase); this.contractAddress = AztecAddress.random(); @@ -132,12 +148,12 @@ export class TXE implements TypedOracle { // Utils - async #getTreesAt(blockNumber: number) { - const db = - blockNumber === (await this.getBlockNumber()) - ? await this.trees.getLatest() - : new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber); - return db; + getNativeWorldStateService() { + return this.nativeWorldStateService; + } + + getBaseFork() { + return this.baseFork; } getChainId() { @@ -211,10 +227,10 @@ export class TXE implements TypedOracle { sideEffectsCounter = this.sideEffectCounter, isStaticCall = false, ) { - const db = await this.#getTreesAt(blockNumber); - const previousBlockState = await this.#getTreesAt(blockNumber - 1); + const snap = this.nativeWorldStateService.getSnapshot(blockNumber); + const previousBlockState = this.nativeWorldStateService.getSnapshot(blockNumber - 1); - const stateReference = await db.getStateReference(); + const stateReference = await snap.getStateReference(); const inputs = PrivateContextInputs.empty(); inputs.txContext.chainId = this.chainId; inputs.txContext.version = this.version; @@ -242,48 +258,30 @@ export class TXE implements TypedOracle { } async addPublicDataWrites(writes: PublicDataWrite[]) { - const db = await this.trees.getLatest(); - await db.batchInsert( + this.publicDataWrites.push(...writes); + + await this.baseFork.sequentialInsert( MerkleTreeId.PUBLIC_DATA_TREE, writes.map(w => new PublicDataTreeLeaf(w.leafSlot, w.value).toBuffer()), - 0, - ); - } - - async addSiloedNullifiers(siloedNullifiers: Fr[]) { - const db = await this.trees.getLatest(); - await db.batchInsert( - MerkleTreeId.NULLIFIER_TREE, - siloedNullifiers.map(n => n.toBuffer()), - NULLIFIER_SUBTREE_HEIGHT, ); } - async addSiloedNullifiersFromPublic(siloedNullifiers: Fr[]) { - this.siloedNullifiersFromPublic.push(...siloedNullifiers); - - await this.addSiloedNullifiers(siloedNullifiers); + addManuallyCreatedNoteHashes(contractAddress: AztecAddress, noteHashes: Fr[]) { + const siloedNoteHashes = noteHashes.map(noteHash => siloNoteHash(contractAddress, noteHash)); + this.manuallyCreatedNoteHashes.push(...siloedNoteHashes); } - async addNullifiers(contractAddress: AztecAddress, nullifiers: Fr[]) { + addManuallyCreatedNullifiers(contractAddress: AztecAddress, nullifiers: Fr[]) { const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier)); - await this.addSiloedNullifiers(siloedNullifiers); + this.manuallyCreatedNullifiers.push(...siloedNullifiers); } - async addSiloedNoteHashes(siloedNoteHashes: Fr[]) { - const db = await this.trees.getLatest(); - await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, siloedNoteHashes); + addSiloedNullifiersFromPublic(siloedNullifiers: Fr[]) { + this.siloedNullifiersFromPublic.push(...siloedNullifiers); } - async addSiloedNoteHashesFromPublic(siloedNoteHashes: Fr[]) { + addSiloedNoteHashesFromPublic(siloedNoteHashes: Fr[]) { this.siloedNoteHashesFromPublic.push(...siloedNoteHashes); - await this.addSiloedNoteHashes(siloedNoteHashes); - } - - async addNoteHashes(contractAddress: AztecAddress, noteHashes: Fr[]) { - const siloedNoteHashes = noteHashes.map(noteHash => siloNoteHash(contractAddress, noteHash)); - - await this.addSiloedNoteHashes(siloedNoteHashes); } addPrivateLogs(contractAddress: AztecAddress, privateLogs: PrivateLog[]) { @@ -368,20 +366,21 @@ export class TXE implements TypedOracle { } async getMembershipWitness(blockNumber: number, treeId: MerkleTreeId, leafValue: Fr): Promise { - const db = await this.#getTreesAt(blockNumber); + const snap = this.nativeWorldStateService.getSnapshot(blockNumber); - const index = await db.findLeafIndex(treeId, leafValue.toBuffer()); + const index = await snap.findLeafIndex(treeId, leafValue.toBuffer()); if (index === undefined) { throw new Error(`Leaf value: ${leafValue} not found in ${MerkleTreeId[treeId]} at block ${blockNumber}`); } - const siblingPath = await db.getSiblingPath(treeId, index); + const siblingPath = await snap.getSiblingPath(treeId, index); return [new Fr(index), ...siblingPath.toFields()]; } async getSiblingPath(blockNumber: number, treeId: MerkleTreeId, leafIndex: Fr) { - const committedDb = new MerkleTreeSnapshotOperationsFacade(this.trees, blockNumber); - const result = await committedDb.getSiblingPath(treeId, leafIndex.toBigInt()); + const snap = this.nativeWorldStateService.getSnapshot(blockNumber); + + const result = await snap.getSiblingPath(treeId, leafIndex.toBigInt()); return result.toFields(); } @@ -389,14 +388,15 @@ export class TXE implements TypedOracle { blockNumber: number, nullifier: Fr, ): Promise { - const db = await this.#getTreesAt(blockNumber); - const index = await db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); + const snap = this.nativeWorldStateService.getSnapshot(blockNumber); + + const index = await snap.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); if (!index) { return undefined; } - const leafPreimagePromise = db.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index); - const siblingPathPromise = db.getSiblingPath( + const leafPreimagePromise = snap.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index); + const siblingPathPromise = snap.getSiblingPath( MerkleTreeId.NULLIFIER_TREE, BigInt(index), ); @@ -411,16 +411,17 @@ export class TXE implements TypedOracle { } async getPublicDataTreeWitness(blockNumber: number, leafSlot: Fr): Promise { - const db = await this.#getTreesAt(blockNumber); - const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); + const snap = this.nativeWorldStateService.getSnapshot(blockNumber); + + const lowLeafResult = await snap.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); if (!lowLeafResult) { return undefined; } else { - const preimage = (await db.getLeafPreimage( + const preimage = (await snap.getLeafPreimage( MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index, )) as PublicDataTreeLeafPreimage; - const path = await db.getSiblingPath( + const path = await snap.getSiblingPath( MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index, ); @@ -432,8 +433,9 @@ export class TXE implements TypedOracle { blockNumber: number, nullifier: Fr, ): Promise { - const committedDb = await this.#getTreesAt(blockNumber); - const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); + const snap = this.nativeWorldStateService.getSnapshot(blockNumber); + + const findResult = await snap.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); if (!findResult) { return undefined; } @@ -441,9 +443,9 @@ export class TXE implements TypedOracle { if (alreadyPresent) { this.logger.warn(`Nullifier ${nullifier.toBigInt()} already exists in the tree`); } - const preimageData = (await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!; + const preimageData = (await snap.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index))!; - const siblingPath = await committedDb.getSiblingPath( + const siblingPath = await snap.getSiblingPath( MerkleTreeId.NULLIFIER_TREE, BigInt(index), ); @@ -451,11 +453,16 @@ export class TXE implements TypedOracle { } async getBlockHeader(blockNumber: number): Promise { + const snap = this.nativeWorldStateService.getSnapshot(blockNumber); + + const previousBlockState = this.nativeWorldStateService.getSnapshot(blockNumber - 1); + const header = BlockHeader.empty(); - const db = await this.#getTreesAt(blockNumber); - header.state = await db.getStateReference(); header.globalVariables.blockNumber = new Fr(blockNumber); - return header; + header.state = await snap.getStateReference(); + header.lastArchive.root = Fr.fromBuffer((await previousBlockState.getTreeInfo(MerkleTreeId.ARCHIVE)).root); + + return Promise.resolve(header); } getCompleteAddress(account: AztecAddress) { @@ -538,9 +545,10 @@ export class TXE implements TypedOracle { } async checkNullifierExists(innerNullifier: Fr): Promise { + const snap = this.nativeWorldStateService.getSnapshot(this.blockNumber - 1); + const nullifier = siloNullifier(this.contractAddress, innerNullifier!); - const db = await this.trees.getLatest(); - const index = await db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); + const index = await snap.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); return index !== undefined; } @@ -558,7 +566,13 @@ export class TXE implements TypedOracle { blockNumber: number, numberOfElements: number, ): Promise { - const db = await this.#getTreesAt(blockNumber); + let db: MerkleTreeReadOperations; + if (blockNumber === this.blockNumber) { + db = this.baseFork; + } else { + db = this.nativeWorldStateService.getSnapshot(blockNumber); + } + const values = []; for (let i = 0n; i < numberOfElements; i++) { const storageSlot = startStorageSlot.add(new Fr(i)); @@ -581,18 +595,13 @@ export class TXE implements TypedOracle { } async storageWrite(startStorageSlot: Fr, values: Fr[]): Promise { - const db = await this.trees.getLatest(); - const publicDataWrites = values.map((value, i) => { const storageSlot = startStorageSlot.add(new Fr(i)); this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); - return new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(this.contractAddress, storageSlot), value); + return new PublicDataWrite(computePublicDataTreeLeafSlot(this.contractAddress, storageSlot), value); }); - await db.batchInsert( - MerkleTreeId.PUBLIC_DATA_TREE, - publicDataWrites.map(write => write.toBuffer()), - 0, - ); + + await this.addPublicDataWrites(publicDataWrites); return publicDataWrites.map(write => write.value); } @@ -604,6 +613,8 @@ export class TXE implements TypedOracle { this.committedBlocks.add(blockNumber); } + const fork = this.baseFork; + const txEffect = TxEffect.empty(); let i = 0; @@ -619,9 +630,26 @@ export class TXE implements TypedOracle { ), ), ), + ...this.manuallyCreatedNoteHashes, ...this.siloedNoteHashesFromPublic, ]; - txEffect.nullifiers = [new Fr(blockNumber + 6969), ...this.noteCache.getAllNullifiers()]; + txEffect.nullifiers = [ + new Fr(blockNumber + 6969), + ...this.noteCache.getAllNullifiers(), + ...this.manuallyCreatedNullifiers, + ...this.siloedNullifiersFromPublic, + ]; + txEffect.publicDataWrites = this.publicDataWrites; + + txEffect.noteHashes = txEffect.noteHashes.filter((noteHash, i) => { + const foundNoteHashIndex = txEffect.noteHashes.findIndex(otherNoteHash => otherNoteHash.equals(noteHash)); + return i === foundNoteHashIndex; + }); + + txEffect.nullifiers = txEffect.nullifiers.filter((nullifier, i) => { + const foundNullifierIndex = txEffect.nullifiers.findIndex(otherNullifier => otherNullifier.equals(nullifier)); + return i === foundNullifierIndex; + }); // Using block number itself, (without adding 6969) gets killed at 1 as it says the slot is already used, // it seems like we commit a 1 there to the trees before ? To see what I mean, uncomment these lines below @@ -630,18 +658,70 @@ export class TXE implements TypedOracle { // index = await (await this.trees.getLatest()).findLeafIndex(MerkleTreeId.NULLIFIER_TREE, Fr.random().toBuffer()); // console.log('INDEX OF RANDOM', index); + const body = new Body([txEffect]); + + const txsEffectsHash = body.getTxsEffectsHash(); + + const l2Block = new L2Block( + makeAppendOnlyTreeSnapshot(blockNumber + 1), + makeHeader(0, blockNumber, blockNumber, txsEffectsHash), + body, + ); + + const paddedTxEffects = padArrayEnd( + l2Block.body.txEffects, + TxEffect.empty(), + l2Block.body.numberOfTxsIncludingPadded, + ); + + const l1ToL2Messages = Array(16).fill(0).map(Fr.zero); + + { + const noteHashesPadded = paddedTxEffects.flatMap(txEffect => + padArrayEnd(txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX), + ); + await fork.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, noteHashesPadded); + + await fork.appendLeaves(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l1ToL2Messages); + } + + { + for (const txEffect of paddedTxEffects) { + // We do not need to add public data writes because we apply them as we go. We use the sequentialInsert because + // the batchInsert was not working when updating a previously updated slot. + + const nullifiersPadded = padArrayEnd(txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX); + + await fork.batchInsert( + MerkleTreeId.NULLIFIER_TREE, + nullifiersPadded.map(nullifier => nullifier.toBuffer()), + NULLIFIER_SUBTREE_HEIGHT, + ); + } + } + this.node.setTxEffect(blockNumber, new TxHash(new Fr(blockNumber).toBuffer()), txEffect); this.node.setNullifiersIndexesWithBlock(blockNumber, txEffect.nullifiers); this.node.addNoteLogsByTags(this.blockNumber, this.privateLogs); this.node.addPublicLogsByTags(this.blockNumber, this.publicLogs); - await this.addSiloedNoteHashes(txEffect.noteHashes); - await this.addSiloedNullifiers(txEffect.nullifiers); + const state = await fork.getStateReference(); + l2Block.header.state = state; + await fork.updateArchive(l2Block.header); + + const archiveState = await fork.getTreeInfo(MerkleTreeId.ARCHIVE); + + l2Block.archive = new AppendOnlyTreeSnapshot(Fr.fromBuffer(archiveState.root), Number(archiveState.size)); + + await this.nativeWorldStateService.handleL2BlockAndMessages(l2Block, l1ToL2Messages); this.privateLogs = []; this.publicLogs = []; + this.manuallyCreatedNoteHashes = []; + this.manuallyCreatedNullifiers = []; this.siloedNoteHashesFromPublic = []; this.siloedNullifiersFromPublic = []; + this.publicDataWrites = []; this.noteCache = new ExecutionNoteCache(new Fr(1)); } @@ -714,6 +794,16 @@ export class TXE implements TypedOracle { publicInputs.privateLogs.filter(privateLog => !privateLog.isEmpty()).map(privateLog => privateLog.log), ); + this.addManuallyCreatedNoteHashes( + targetContractAddress, + publicInputs.noteHashes.filter(noteHash => !noteHash.isEmpty()).map(noteHash => noteHash.value), + ); + + this.addManuallyCreatedNullifiers( + targetContractAddress, + publicInputs.nullifiers.filter(nullifier => !nullifier.isEmpty()).map(nullifier => nullifier.value), + ); + this.setContractAddress(currentContractAddress); this.setMsgSender(currentMessageSender); this.setFunctionSelector(currentFunctionSelector); @@ -767,8 +857,8 @@ export class TXE implements TypedOracle { private async executePublicFunction(args: Fr[], callContext: CallContext, isTeardown: boolean = false) { const executionRequest = new PublicExecutionRequest(callContext, args); - const db = await this.trees.getLatest(); - const worldStateDb = new TXEWorldStateDB(db, new TXEPublicContractDataSource(this)); + const db = this.baseFork; + const worldStateDb = new TXEWorldStateDB(db, new TXEPublicContractDataSource(this), this); const globalVariables = GlobalVariables.empty(); globalVariables.chainId = this.chainId; @@ -790,7 +880,7 @@ export class TXE implements TypedOracle { const simulator = new PublicTxSimulator( db, - new TXEWorldStateDB(db, new TXEPublicContractDataSource(this)), + new TXEWorldStateDB(db, new TXEPublicContractDataSource(this), this), new NoopTelemetryClient(), globalVariables, ); @@ -856,8 +946,8 @@ export class TXE implements TypedOracle { const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty()); const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty()); await this.addPublicDataWrites(publicDataWrites); - await this.addSiloedNoteHashesFromPublic(noteHashes); - await this.addSiloedNullifiers(nullifiers); + this.addSiloedNoteHashesFromPublic(noteHashes); + this.addSiloedNullifiersFromPublic(nullifiers); this.setContractAddress(currentContractAddress); this.setMsgSender(currentMessageSender); @@ -955,8 +1045,8 @@ export class TXE implements TypedOracle { const noteHashes = sideEffects.noteHashes.filter(s => !s.isEmpty()); const nullifiers = sideEffects.nullifiers.filter(s => !s.isEmpty()); await this.addPublicDataWrites(publicDataWrites); - await this.addSiloedNoteHashes(noteHashes); - await this.addSiloedNullifiers(nullifiers); + this.addSiloedNoteHashesFromPublic(noteHashes); + this.addSiloedNullifiersFromPublic(nullifiers); } this.setContractAddress(currentContractAddress); @@ -973,38 +1063,36 @@ export class TXE implements TypedOracle { return this.nestedCallReturndata.slice(rdOffset, rdOffset + copySize); } + // Does this also get nullifiers created by a previous enqueued call ? async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise { + const snap = this.nativeWorldStateService.getSnapshot(this.blockNumber - 1); + const nullifier = siloNullifier(targetAddress, innerNullifier!); - const db = await this.trees.getLatest(); - const index = await db.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); + const index = await snap.findLeafIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBuffer()); return index !== undefined; } - async avmOpcodeEmitNullifier(nullifier: Fr) { - const db = await this.trees.getLatest(); + avmOpcodeEmitNullifier(nullifier: Fr) { const siloedNullifier = siloNullifier(this.contractAddress, nullifier); - await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT); + this.addSiloedNullifiersFromPublic([siloedNullifier]); return Promise.resolve(); } - async avmOpcodeEmitNoteHash(noteHash: Fr) { - const db = await this.trees.getLatest(); + avmOpcodeEmitNoteHash(noteHash: Fr) { const siloedNoteHash = siloNoteHash(this.contractAddress, noteHash); - await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [siloedNoteHash]); + this.addSiloedNoteHashesFromPublic([siloedNoteHash]); return Promise.resolve(); } async avmOpcodeStorageRead(slot: Fr) { - const db = await this.trees.getLatest(); - const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, slot); - const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); + const lowLeafResult = await this.baseFork.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); if (!lowLeafResult || !lowLeafResult.alreadyPresent) { return Fr.ZERO; } - const preimage = (await db.getLeafPreimage( + const preimage = (await this.baseFork.getLeafPreimage( MerkleTreeId.PUBLIC_DATA_TREE, lowLeafResult.index, )) as PublicDataTreeLeafPreimage; diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index de5c041d1c1..6ba1cf658ab 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -5,6 +5,7 @@ import { Fr, FunctionSelector, PublicDataTreeLeaf, + PublicDataWrite, PublicKeys, computePartialAddress, getContractInstanceFromDeployParams, @@ -19,7 +20,7 @@ import { getCanonicalProtocolContract, protocolContractNames } from '@aztec/prot import { enrichPublicSimulationError } from '@aztec/pxe'; import { ExecutionNoteCache, PackedValuesCache, type TypedOracle } from '@aztec/simulator'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; -import { MerkleTrees } from '@aztec/world-state'; +import { MerkleTrees, NativeWorldStateService } from '@aztec/world-state'; import { TXE } from '../oracle/txe_oracle.js'; import { @@ -41,6 +42,10 @@ export class TXEService { static async init(logger: Logger) { const store = openTmpStore(true); const trees = await MerkleTrees.new(store, new NoopTelemetryClient(), logger); + + const nativeWorldStateService = await NativeWorldStateService.tmp(); + const baseFork = await nativeWorldStateService.fork(); + const packedValuesCache = new PackedValuesCache(); const txHash = new Fr(1); // The txHash is used for computing the revertible nullifiers for non-revertible note hashes. It can be any value for testing. const noteCache = new ExecutionNoteCache(txHash); @@ -53,7 +58,16 @@ export class TXEService { await txeDatabase.addContractInstance(instance); } logger.debug(`TXE service initialized`); - const txe = new TXE(logger, trees, packedValuesCache, noteCache, keyStore, txeDatabase); + const txe = new TXE( + logger, + trees, + packedValuesCache, + noteCache, + keyStore, + txeDatabase, + nativeWorldStateService, + baseFork, + ); const service = new TXEService(logger, txe); await service.advanceBlocksBy(toSingle(new Fr(1n))); return service; @@ -69,21 +83,12 @@ export class TXEService { async advanceBlocksBy(blocks: ForeignCallSingle) { const nBlocks = fromSingle(blocks).toNumber(); this.logger.debug(`time traveling ${nBlocks} blocks`); - const trees = (this.typedOracle as TXE).getTrees(); - - await (this.typedOracle as TXE).commitState(); for (let i = 0; i < nBlocks; i++) { const blockNumber = await this.typedOracle.getBlockNumber(); - const header = BlockHeader.empty(); - const l2Block = L2Block.empty(); - header.state = await trees.getStateReference(true); - header.globalVariables.blockNumber = new Fr(blockNumber); - await trees.appendLeaves(MerkleTreeId.ARCHIVE, [header.hash()]); - l2Block.archive.root = Fr.fromBuffer((await trees.getTreeInfo(MerkleTreeId.ARCHIVE, true)).root); - l2Block.header = header; - this.logger.debug(`Block ${blockNumber} created, header hash ${header.hash().toString()}`); - await trees.handleL2BlockAndMessages(l2Block, []); + + await (this.typedOracle as TXE).commitState(); + (this.typedOracle as TXE).setBlockNumber(blockNumber + 1); } return toForeignCallResult([]); @@ -145,22 +150,18 @@ export class TXEService { startStorageSlot: ForeignCallSingle, values: ForeignCallArray, ) { - const trees = (this.typedOracle as TXE).getTrees(); const startStorageSlotFr = fromSingle(startStorageSlot); const valuesFr = fromArray(values); const contractAddressFr = addressFromSingle(contractAddress); - const db = await trees.getLatest(); const publicDataWrites = valuesFr.map((value, i) => { const storageSlot = startStorageSlotFr.add(new Fr(i)); this.logger.debug(`Oracle storage write: slot=${storageSlot.toString()} value=${value}`); - return new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(contractAddressFr, storageSlot), value); + return new PublicDataWrite(computePublicDataTreeLeafSlot(contractAddressFr, storageSlot), value); }); - await db.batchInsert( - MerkleTreeId.PUBLIC_DATA_TREE, - publicDataWrites.map(write => write.toBuffer()), - 0, - ); + + await (this.typedOracle as TXE).addPublicDataWrites(publicDataWrites); + return toForeignCallResult([toArray(publicDataWrites.map(write => write.value))]); } @@ -543,16 +544,6 @@ export class TXEService { return toForeignCallResult([toSingle(await this.typedOracle.getVersion())]); } - async addNullifiers(contractAddress: ForeignCallSingle, _length: ForeignCallSingle, nullifiers: ForeignCallArray) { - await (this.typedOracle as TXE).addNullifiers(addressFromSingle(contractAddress), fromArray(nullifiers)); - return toForeignCallResult([]); - } - - async addNoteHashes(contractAddress: ForeignCallSingle, _length: ForeignCallSingle, noteHashes: ForeignCallArray) { - await (this.typedOracle as TXE).addNoteHashes(addressFromSingle(contractAddress), fromArray(noteHashes)); - return toForeignCallResult([]); - } - async getBlockHeader(blockNumber: ForeignCallSingle) { const header = await this.typedOracle.getBlockHeader(fromSingle(blockNumber).toNumber()); if (!header) { diff --git a/yarn-project/txe/src/util/txe_world_state_db.ts b/yarn-project/txe/src/util/txe_world_state_db.ts index d1a2b6f1737..59138fecf88 100644 --- a/yarn-project/txe/src/util/txe_world_state_db.ts +++ b/yarn-project/txe/src/util/txe_world_state_db.ts @@ -3,14 +3,16 @@ import { type AztecAddress, type ContractDataSource, Fr, - PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, + PublicDataWrite, } from '@aztec/circuits.js'; import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; import { WorldStateDB } from '@aztec/simulator'; +import { type TXE } from '../oracle/txe_oracle.js'; + export class TXEWorldStateDB extends WorldStateDB { - constructor(private merkleDb: MerkleTreeWriteOperations, dataSource: ContractDataSource) { + constructor(private merkleDb: MerkleTreeWriteOperations, dataSource: ContractDataSource, private txe: TXE) { super(merkleDb, dataSource); } @@ -31,11 +33,7 @@ export class TXEWorldStateDB extends WorldStateDB { } override async storageWrite(contract: AztecAddress, slot: Fr, newValue: Fr): Promise { - await this.merkleDb.batchInsert( - MerkleTreeId.PUBLIC_DATA_TREE, - [new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(contract, slot), newValue).toBuffer()], - 0, - ); + await this.txe.addPublicDataWrites([new PublicDataWrite(computePublicDataTreeLeafSlot(contract, slot), newValue)]); return newValue.toBigInt(); }