From 6a72a6685210f36cd4f2a879f3bf6767c928d30d Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 3 Oct 2023 12:50:15 +0200 Subject: [PATCH] fix: call public fn in contract constructor (#2549) This PR aims to enable calling of public functions in Noir contract constructors. In order to fix this issue this PR does the following two changes: - previously the `PublicExecutor` only looked at deployed contracts. Now it also looks at the contracts being deployed in the current block - the public kernel simulator 'lost' the contract's address so when publishing the block it would have contract data as all zeros. This PR updates the common initialisation function to pass on `new_contracts`. Fix #2509 Related to #2249 # Checklist: Remove the checklist to signal you've completed it. Enable auto-merge if the PR is ready to merge. - [ ] If the pull request requires a cryptography review (e.g. cryptographic algorithm implementations) I have added the 'crypto' tag. - [x] I have reviewed my diff in github, line by line and removed unexpected formatting changes, testing logs, or commented-out code. - [x] Every change is related to the PR description. - [x] I have [linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) this pull request to relevant issues (if any exist). --- .../aztec3/circuits/kernel/public/common.cpp | 4 +- .../tutorials/writing_token_contract.md | 2 +- yarn-project/canary/src/utils.ts | 27 +++------ yarn-project/end-to-end/src/canary/browser.ts | 5 +- yarn-project/end-to-end/src/canary/cli.ts | 7 +-- .../end-to-end/src/e2e_2_pxes.test.ts | 3 +- .../end-to-end/src/e2e_block_building.test.ts | 59 +++++++++++++++--- .../src/e2e_escrow_contract.test.ts | 4 +- .../src/e2e_lending_contract.test.ts | 6 +- .../e2e_multiple_accounts_1_enc_key.test.ts | 4 +- .../src/e2e_sandbox_example.test.ts | 5 +- .../end-to-end/src/e2e_token_contract.test.ts | 3 +- yarn-project/end-to-end/src/fixtures/utils.ts | 26 ++------ .../src/guides/dapp_testing.test.ts | 12 ++-- .../end-to-end/src/guides/up_quick_start.sh | 17 ++---- .../src/guides/up_quick_start.test.ts | 2 +- .../writing_an_account_contract.test.ts | 3 +- .../end-to-end/src/sample-dapp/deploy.mjs | 3 +- .../end-to-end/src/sample-dapp/index.test.mjs | 3 +- .../token_bridge_contract/src/main.nr | 42 ++++++------- .../src/contracts/token_contract/src/main.nr | 20 +++---- .../pxe/src/pxe_service/pxe_service.ts | 5 +- .../src/client/sequencer-client.ts | 1 + .../src/sequencer/public_processor.test.ts | 27 ++------- .../src/sequencer/public_processor.ts | 17 ++++-- .../src/sequencer/sequencer.test.ts | 16 ++++- .../src/sequencer/sequencer.ts | 3 +- .../sequencer-client/src/simulator/index.ts | 2 - .../src/simulator/public_executor.ts | 60 ++++++++++++++++--- 29 files changed, 212 insertions(+), 176 deletions(-) diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/common.cpp b/circuits/cpp/src/aztec3/circuits/kernel/public/common.cpp index bd08b4c5bdbf..87e9df7d6dc9 100644 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/common.cpp +++ b/circuits/cpp/src/aztec3/circuits/kernel/public/common.cpp @@ -35,6 +35,8 @@ void common_initialise_end_values(PublicKernelInputs const& public_kernel_in // Public kernel does not modify encrypted logs values --> we just copy them to output end.encrypted_logs_hash = start.encrypted_logs_hash; end.encrypted_log_preimages_length = start.encrypted_log_preimages_length; + + end.new_contracts = start.new_contracts; } /** @@ -70,4 +72,4 @@ void validate_this_public_call_hash(DummyBuilder& builder, ") at the top of the call stack"), CircuitErrorCode::PUBLIC_KERNEL__CALCULATED_PUBLIC_CALL_HASH_AND_PROVIDED_PUBLIC_CALL_HASH_MISMATCH); }; -} // namespace aztec3::circuits::kernel::public_kernel \ No newline at end of file +} // namespace aztec3::circuits::kernel::public_kernel diff --git a/docs/docs/dev_docs/tutorials/writing_token_contract.md b/docs/docs/dev_docs/tutorials/writing_token_contract.md index 872fe819aba7..b5857dfc3051 100644 --- a/docs/docs/dev_docs/tutorials/writing_token_contract.md +++ b/docs/docs/dev_docs/tutorials/writing_token_contract.md @@ -416,7 +416,7 @@ Internal functions are functions that can only be called by this contract. The f #### `_initialize` -This function is called via the [constructor](#constructor). Note that it is not actually marked `internal` right now--this is because this functionality is still being worked on. +This function is called via the [constructor](#constructor). This function sets the creator of the contract (passed as `msg_sender` from the constructor) as the admin and makes them a minter. diff --git a/yarn-project/canary/src/utils.ts b/yarn-project/canary/src/utils.ts index 4d89e1a7b4ef..efb29ebb3288 100644 --- a/yarn-project/canary/src/utils.ts +++ b/yarn-project/canary/src/utils.ts @@ -66,40 +66,27 @@ export async function deployAndInitializeTokenAndBridgeContracts( }); // deploy l2 token - const deployTx = TokenContract.deploy(wallet).send(); - - // deploy l2 token bridge and attach to the portal - const bridgeTx = TokenBridgeContract.deploy(wallet).send({ - portalContract: tokenPortalAddress, - contractAddressSalt: Fr.random(), - }); + const deployTx = TokenContract.deploy(wallet, owner).send(); // now wait for the deploy txs to be mined. This way we send all tx in the same rollup. const deployReceipt = await deployTx.wait(); if (deployReceipt.status !== TxStatus.MINED) throw new Error(`Deploy token tx status is ${deployReceipt.status}`); const token = await TokenContract.at(deployReceipt.contractAddress!, wallet); + // deploy l2 token bridge and attach to the portal + const bridgeTx = TokenBridgeContract.deploy(wallet, token.address).send({ + portalContract: tokenPortalAddress, + contractAddressSalt: Fr.random(), + }); + const bridgeReceipt = await bridgeTx.wait(); - if (bridgeReceipt.status !== TxStatus.MINED) throw new Error(`Deploy bridge tx status is ${bridgeReceipt.status}`); const bridge = await TokenBridgeContract.at(bridgeReceipt.contractAddress!, wallet); await bridge.attach(tokenPortalAddress); const bridgeAddress = bridge.address.toString() as `0x${string}`; - // initialize l2 token - const initializeTx = token.methods._initialize(owner).send(); - - // initialize bridge - const initializeBridgeTx = bridge.methods._initialize(token.address).send(); - // now we wait for the txs to be mined. This way we send all tx in the same rollup. - const initializeReceipt = await initializeTx.wait(); - if (initializeReceipt.status !== TxStatus.MINED) - throw new Error(`Initialize token tx status is ${initializeReceipt.status}`); if ((await token.methods.admin().view()) !== owner.toBigInt()) throw new Error(`Token admin is not ${owner}`); - const initializeBridgeReceipt = await initializeBridgeTx.wait(); - if (initializeBridgeReceipt.status !== TxStatus.MINED) - throw new Error(`Initialize token bridge tx status is ${initializeBridgeReceipt.status}`); if ((await bridge.methods.token().view()) !== token.address.toBigInt()) throw new Error(`Bridge token is not ${token.address}`); diff --git a/yarn-project/end-to-end/src/canary/browser.ts b/yarn-project/end-to-end/src/canary/browser.ts index bf145149e994..c2b7a642cd01 100644 --- a/yarn-project/end-to-end/src/canary/browser.ts +++ b/yarn-project/end-to-end/src/canary/browser.ts @@ -176,13 +176,14 @@ export const browserTestSuite = (setup: () => Server, pageLogger: AztecJs.DebugL } const [owner] = await getSandboxAccountsWallets(pxe); const ownerAddress = owner.getAddress(); - const tx = new DeployMethod(accounts[0].publicKey, pxe, TokenContractAbi).send(); + const tx = new DeployMethod(accounts[0].publicKey, pxe, TokenContractAbi, [ + owner.getCompleteAddress(), + ]).send(); await tx.wait(); const receipt = await tx.getReceipt(); console.log(`Contract Deployed: ${receipt.contractAddress}`); const token = await Contract.at(receipt.contractAddress!, TokenContractAbi, owner); - await token.methods._initialize(ownerAddress).send().wait(); const secret = Fr.random(); const secretHash = await computeMessageSecretHash(secret); const mintPrivateReceipt = await token.methods.mint_private(initialBalance, secretHash).send().wait(); diff --git a/yarn-project/end-to-end/src/canary/cli.ts b/yarn-project/end-to-end/src/canary/cli.ts index 1974ee6573ef..2bac457365dd 100644 --- a/yarn-project/end-to-end/src/canary/cli.ts +++ b/yarn-project/end-to-end/src/canary/cli.ts @@ -118,7 +118,7 @@ export const cliTestSuite = ( const ownerAddress = AztecAddress.fromString(foundAddress!); debug('Deploy Token Contract using created account.'); - await run(`deploy TokenContractAbi --salt 0`); + await run(`deploy TokenContractAbi --salt 0 --args ${ownerAddress}`); const loggedAddress = findInLogs(/Contract\sdeployed\sat\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; expect(loggedAddress).toBeDefined(); contractAddress = AztecAddress.fromString(loggedAddress!); @@ -131,11 +131,6 @@ export const cliTestSuite = ( const checkResult = findInLogs(/Contract\sfound\sat\s+(?
0x[a-fA-F0-9]+)/)?.groups?.address; expect(checkResult).toEqual(deployedContract?.contractAddress.toString()); - debug('Initialize token contract.'); - await run( - `send _initialize --args ${ownerAddress} --contract-abi TokenContractAbi --contract-address ${contractAddress.toString()} --private-key ${privKey}`, - ); - const secret = Fr.random(); const secretHash = await computeMessageSecretHash(secret); diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index f19ee8aebca2..e3f8391ff524 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -85,8 +85,7 @@ describe('e2e_2_pxes', () => { const deployTokenContract = async (initialAdminBalance: bigint, admin: AztecAddress, pxe: PXE) => { logger(`Deploying Token contract...`); - const contract = await TokenContract.deploy(walletA).send().deployed(); - expect((await contract.methods._initialize(admin).send().wait()).status).toBe(TxStatus.MINED); + const contract = await TokenContract.deploy(walletA, admin).send().deployed(); if (initialAdminBalance > 0n) { await mintTokens(contract, admin, initialAdminBalance, pxe); diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index a7e589d08ffe..9336b1fc2641 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -1,9 +1,16 @@ -import { BatchCall, ContractDeployer, Fr, Wallet, isContractDeployed } from '@aztec/aztec.js'; +import { + BatchCall, + ContractDeployer, + ContractFunctionInteraction, + Fr, + Wallet, + isContractDeployed, +} from '@aztec/aztec.js'; import { CircuitsWasm } from '@aztec/circuits.js'; import { pedersenPlookupCommitInputs } from '@aztec/circuits.js/barretenberg'; import { DebugLogger } from '@aztec/foundation/log'; import { TestContractAbi } from '@aztec/noir-contracts/artifacts'; -import { TestContract } from '@aztec/noir-contracts/types'; +import { TestContract, TokenContract } from '@aztec/noir-contracts/types'; import { PXE, TxStatus } from '@aztec/types'; import times from 'lodash.times'; @@ -13,14 +20,20 @@ import { setup } from './fixtures/utils.js'; describe('e2e_block_building', () => { let pxe: PXE; let logger: DebugLogger; - let wallet: Wallet; + let owner: Wallet; + let minter: Wallet; let teardown: () => Promise; describe('multi-txs block', () => { const abi = TestContractAbi; beforeAll(async () => { - ({ teardown, pxe, logger, wallet } = await setup(1)); + ({ + teardown, + pxe, + logger, + wallets: [owner, minter], + } = await setup(2)); }, 100_000); afterAll(() => teardown()); @@ -29,7 +42,7 @@ describe('e2e_block_building', () => { // Assemble N contract deployment txs // We need to create them sequentially since we cannot have parallel calls to a circuit const TX_COUNT = 8; - const deployer = new ContractDeployer(abi, wallet); + const deployer = new ContractDeployer(abi, owner); const methods = times(TX_COUNT, () => deployer.deploy()); for (const i in methods) { @@ -51,6 +64,36 @@ describe('e2e_block_building', () => { const areDeployed = await Promise.all(receipts.map(r => isContractDeployed(pxe, r.contractAddress!))); expect(areDeployed).toEqual(times(TX_COUNT, () => true)); }, 60_000); + + it('can call public function from different tx in same block', async () => { + // Deploy a contract in the first transaction + // In the same block, call a public method on the contract + const deployer = TokenContract.deploy(owner, owner.getCompleteAddress()); + await deployer.create(); + + // We can't use `TokenContract.at` to call a function because it checks the contract is deployed + // but we are in the same block as the deployment transaction + const callInteraction = new ContractFunctionInteraction( + owner, + deployer.completeAddress!.address, + TokenContract.abi.functions.find(x => x.name === 'set_minter')!, + [minter.getCompleteAddress(), true], + ); + + await deployer.simulate({}); + await callInteraction.simulate({ + // we have to skip simulation of public calls simulation is done on individual transactions + // and the tx deploying the contract might go in the same block as this one + skipPublicSimulation: true, + }); + + const [deployTxReceipt, callTxReceipt] = await Promise.all([ + deployer.send().wait(), + callInteraction.send({ skipPublicSimulation: true }).wait(), + ]); + + expect(deployTxReceipt.blockNumber).toEqual(callTxReceipt.blockNumber); + }, 60_000); }); // Regressions for https://github.com/AztecProtocol/aztec-packages/issues/2502 @@ -59,8 +102,8 @@ describe('e2e_block_building', () => { let teardown: () => Promise; beforeAll(async () => { - ({ teardown, pxe, logger, wallet } = await setup(1)); - contract = await TestContract.deploy(wallet).send().deployed(); + ({ teardown, pxe, logger, wallet: owner } = await setup(1)); + contract = await TestContract.deploy(owner).send().deployed(); }, 100_000); afterAll(() => teardown()); @@ -86,7 +129,7 @@ describe('e2e_block_building', () => { it('drops tx with two equal nullifiers', async () => { const nullifier = Fr.random(); const calls = times(2, () => contract.methods.emit_nullifier(nullifier).request()); - await expect(new BatchCall(wallet, calls).send().wait()).rejects.toThrowError(/dropped/); + await expect(new BatchCall(owner, calls).send().wait()).rejects.toThrowError(/dropped/); }); it('drops tx with private nullifier already emitted from public on the same block', async () => { diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index ad0315c5fa08..77d9db582d28 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -57,9 +57,7 @@ describe('e2e_escrow_contract', () => { logger(`Escrow contract deployed at ${escrowContract.address}`); // Deploy Private Token contract and mint funds for the escrow contract - token = await TokenContract.deploy(wallet).send().deployed(); - - expect((await token.methods._initialize(owner).send().wait()).status).toBe(TxStatus.MINED); + token = await TokenContract.deploy(wallet, owner).send().deployed(); const mintAmount = 100n; const secret = Fr.random(); diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index 9ba8808215dc..2b9469b922bd 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -50,14 +50,14 @@ describe('e2e_lending_contract', () => { { logger(`Deploying collateral asset feed contract...`); - const receipt = await waitForSuccess(TokenContract.deploy(wallet).send()); + const receipt = await waitForSuccess(TokenContract.deploy(wallet, accounts[0]).send()); logger(`Collateral asset deployed to ${receipt.contractAddress}`); collateralAsset = await TokenContract.at(receipt.contractAddress!, wallet); } { logger(`Deploying stable coin contract...`); - const receipt = await waitForSuccess(TokenContract.deploy(wallet).send()); + const receipt = await waitForSuccess(TokenContract.deploy(wallet, accounts[0]).send()); logger(`Stable coin asset deployed to ${receipt.contractAddress}`); stableCoin = await TokenContract.at(receipt.contractAddress!, wallet); } @@ -69,9 +69,7 @@ describe('e2e_lending_contract', () => { lendingContract = await LendingContract.at(receipt.contractAddress!, wallet); } - await waitForSuccess(collateralAsset.methods._initialize(accounts[0]).send()); await waitForSuccess(collateralAsset.methods.set_minter(lendingContract.address, true).send()); - await waitForSuccess(stableCoin.methods._initialize(accounts[0]).send()); await waitForSuccess(stableCoin.methods.set_minter(lendingContract.address, true).send()); return { priceFeedContract, lendingContract, collateralAsset, stableCoin }; diff --git a/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts b/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts index b1069faedc07..61a50b53866d 100644 --- a/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts +++ b/yarn-project/end-to-end/src/e2e_multiple_accounts_1_enc_key.test.ts @@ -50,12 +50,10 @@ describe('e2e_multiple_accounts_1_enc_key', () => { } logger(`Deploying Token...`); - const token = await TokenContract.deploy(wallets[0]).send().deployed(); + const token = await TokenContract.deploy(wallets[0], accounts[0]).send().deployed(); tokenAddress = token.address; logger(`Token deployed at ${tokenAddress}`); - expect((await token.methods._initialize(accounts[0]).send().wait()).status).toBe(TxStatus.MINED); - const secret = Fr.random(); const secretHash = await computeMessageSecretHash(secret); diff --git a/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts b/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts index c3ddec3f42a2..665dc3714add 100644 --- a/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts +++ b/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts @@ -56,13 +56,12 @@ describe('e2e_sandbox_example', () => { const initialSupply = 1_000_000n; logger(`Deploying token contract minting an initial ${initialSupply} tokens to Alice...`); - const contract = await TokenContract.deploy(pxe).send().deployed(); + const contract = await TokenContract.deploy(pxe, alice).send().deployed(); // Create the contract abstraction and link to Alice's wallet for future signing const tokenContractAlice = await TokenContract.at(contract.address, accounts[0]); - // Initialize the contract and add Bob as a minter - await tokenContractAlice.methods._initialize(alice).send().wait(); + // add Bob as a minter await tokenContractAlice.methods.set_minter(bob, true).send().wait(); logger(`Contract successfully deployed at address ${contract.address.toShortString()}`); diff --git a/yarn-project/end-to-end/src/e2e_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_token_contract.test.ts index c0d6a38ef0e9..ad35a01bc684 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract.test.ts @@ -40,7 +40,7 @@ describe('e2e_token_contract', () => { beforeAll(async () => { ({ teardown, logger, wallets, accounts } = await setup(3)); - asset = await TokenContract.deploy(wallets[0]).send().deployed(); + asset = await TokenContract.deploy(wallets[0], accounts[0]).send().deployed(); logger(`Token deployed to ${asset.address}`); tokenSim = new TokenSimulator( asset, @@ -48,7 +48,6 @@ describe('e2e_token_contract', () => { accounts.map(a => a.address), ); - await asset.methods._initialize(accounts[0].address).send().wait(); expect(await asset.methods.admin().view()).toBe(accounts[0].address.toBigInt()); asset.abi.functions.forEach(fn => { diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 43c02e7bffc6..2955f2b2554b 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -400,40 +400,26 @@ export async function deployAndInitializeTokenAndBridgeContracts( }); // deploy l2 token - const deployTx = TokenContract.deploy(wallet).send(); - - // deploy l2 token bridge and attach to the portal - const bridgeTx = TokenBridgeContract.deploy(wallet).send({ - portalContract: tokenPortalAddress, - contractAddressSalt: Fr.random(), - }); + const deployTx = TokenContract.deploy(wallet, owner).send(); // now wait for the deploy txs to be mined. This way we send all tx in the same rollup. const deployReceipt = await deployTx.wait(); if (deployReceipt.status !== TxStatus.MINED) throw new Error(`Deploy token tx status is ${deployReceipt.status}`); const token = await TokenContract.at(deployReceipt.contractAddress!, wallet); + // deploy l2 token bridge and attach to the portal + const bridgeTx = TokenBridgeContract.deploy(wallet, token.address).send({ + portalContract: tokenPortalAddress, + contractAddressSalt: Fr.random(), + }); const bridgeReceipt = await bridgeTx.wait(); if (bridgeReceipt.status !== TxStatus.MINED) throw new Error(`Deploy bridge tx status is ${bridgeReceipt.status}`); const bridge = await TokenBridgeContract.at(bridgeReceipt.contractAddress!, wallet); await bridge.attach(tokenPortalAddress); const bridgeAddress = bridge.address.toString() as `0x${string}`; - // initialize l2 token - const initializeTx = token.methods._initialize(owner).send(); - - // initialize bridge - const initializeBridgeTx = bridge.methods._initialize(token.address).send(); - - // now we wait for the txs to be mined. This way we send all tx in the same rollup. - const initializeReceipt = await initializeTx.wait(); - if (initializeReceipt.status !== TxStatus.MINED) - throw new Error(`Initialize token tx status is ${initializeReceipt.status}`); if ((await token.methods.admin().view()) !== owner.toBigInt()) throw new Error(`Token admin is not ${owner}`); - const initializeBridgeReceipt = await initializeBridgeTx.wait(); - if (initializeBridgeReceipt.status !== TxStatus.MINED) - throw new Error(`Initialize token bridge tx status is ${initializeBridgeReceipt.status}`); if ((await bridge.methods.token().view()) !== token.address.toBigInt()) throw new Error(`Bridge token is not ${token.address}`); diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index d0f28f6eac5d..5c1bfdebed5b 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -33,8 +33,7 @@ describe('guides/dapp/testing', () => { // docs:end:in-proc-sandbox owner = await createAccount(pxe); recipient = await createAccount(pxe); - token = await TokenContract.deploy(owner).send().deployed(); - await token.methods._initialize(owner.getAddress()).send().wait(); + token = await TokenContract.deploy(owner, owner.getCompleteAddress()).send().deployed(); }, 60_000); // docs:start:stop-in-proc-sandbox @@ -77,8 +76,7 @@ describe('guides/dapp/testing', () => { pxe = createPXEClient(SANDBOX_URL); owner = await createAccount(pxe); recipient = await createAccount(pxe); - token = await TokenContract.deploy(owner).send().deployed(); - await token.methods._initialize(owner.getAddress()).send().wait(); + token = await TokenContract.deploy(owner, owner.getCompleteAddress()).send().deployed(); }, 30_000); it('increases recipient funds on mint', async () => { @@ -110,8 +108,7 @@ describe('guides/dapp/testing', () => { // docs:start:use-existing-wallets pxe = createPXEClient(SANDBOX_URL); [owner, recipient] = await getSandboxAccountsWallets(pxe); - token = await TokenContract.deploy(owner).send().deployed(); - await token.methods._initialize(owner.getAddress()).send().wait(); + token = await TokenContract.deploy(owner, owner.getCompleteAddress()).send().deployed(); // docs:end:use-existing-wallets }, 30_000); @@ -168,8 +165,7 @@ describe('guides/dapp/testing', () => { owner = await createAccount(pxe); recipient = await createAccount(pxe); testContract = await TestContract.deploy(owner).send().deployed(); - token = await TokenContract.deploy(owner).send().deployed(); - await token.methods._initialize(owner.getAddress()).send().wait(); + token = await TokenContract.deploy(owner, owner.getCompleteAddress()).send().deployed(); const ownerAddress = owner.getAddress(); const mintAmount = 100n; diff --git a/yarn-project/end-to-end/src/guides/up_quick_start.sh b/yarn-project/end-to-end/src/guides/up_quick_start.sh index 2ad7e7eab786..c97dd0d5e295 100755 --- a/yarn-project/end-to-end/src/guides/up_quick_start.sh +++ b/yarn-project/end-to-end/src/guides/up_quick_start.sh @@ -12,17 +12,12 @@ ALICE_PRIVATE_KEY="0x2153536ff6628eee01cf4024889ff977a18d9fa61d0e414422f7681cf08 # docs:start:deploy aztec-cli deploy \ TokenContractAbi \ - --salt 0 + --salt 0 \ + --args $ALICE -aztec-cli check-deploy --contract-address 0x2d23acefa3ce07b3c308caf78d86c064cdf8957bcea48b38753cf58441796c8c +aztec-cli check-deploy --contract-address 0x2219e810bff6e04abdefce9f91c2d1dd1e4d52fafa602def3c90b77f4331feca -CONTRACT="0x2d23acefa3ce07b3c308caf78d86c064cdf8957bcea48b38753cf58441796c8c" - -aztec-cli send _initialize \ - --args $ALICE \ - --contract-abi TokenContractAbi \ - --contract-address $CONTRACT \ - --private-key $ALICE_PRIVATE_KEY +CONTRACT="0x2219e810bff6e04abdefce9f91c2d1dd1e4d52fafa602def3c90b77f4331feca" # docs:end:deploy # docs:start:mint-private @@ -77,7 +72,7 @@ aztec-cli get-logs # Test end result BOB_BALANCE=$(aztec-cli call balance_of_private --args $BOB --contract-abi TokenContractAbi --contract-address $CONTRACT) -if ! echo $BOB_BALANCE | grep -q 500; then +if ! echo $BOB_BALANCE | grep -q 500; then echo "Incorrect Bob balance after transaction (expected 500 but got $BOB_BALANCE)" exit 1 -fi \ No newline at end of file +fi diff --git a/yarn-project/end-to-end/src/guides/up_quick_start.test.ts b/yarn-project/end-to-end/src/guides/up_quick_start.test.ts index 855dbcaf4ffb..15d135e2459f 100644 --- a/yarn-project/end-to-end/src/guides/up_quick_start.test.ts +++ b/yarn-project/end-to-end/src/guides/up_quick_start.test.ts @@ -10,7 +10,7 @@ describe('guides/up_quick_start', () => { `DEBUG="aztec:*" PXE_HOST=\${SANDBOX_URL:-http://localhost:8080} PATH=$PATH:../node_modules/.bin ./src/guides/up_quick_start.sh`, { shell: '/bin/bash', - stdio: 'pipe', + stdio: 'inherit', }, ); }, 90_000); diff --git a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts index 4e66831c2d9b..f61e3c4c881f 100644 --- a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts +++ b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts @@ -61,9 +61,8 @@ describe('guides/writing_an_account_contract', () => { logger(`Deployed account contract at ${address}`); // docs:start:account-contract-works - const token = await TokenContract.deploy(wallet).send().deployed(); + const token = await TokenContract.deploy(wallet, { address }).send().deployed(); logger(`Deployed token contract at ${token.address}`); - await token.methods._initialize({ address }).send().wait(); const secret = Fr.random(); const secretHash = await computeMessageSecretHash(secret); diff --git a/yarn-project/end-to-end/src/sample-dapp/deploy.mjs b/yarn-project/end-to-end/src/sample-dapp/deploy.mjs index 7d9c655ac196..866c14889cd9 100644 --- a/yarn-project/end-to-end/src/sample-dapp/deploy.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/deploy.mjs @@ -11,8 +11,7 @@ async function main() { const pxe = createPXEClient(SANDBOX_URL); const [owner] = await getSandboxAccountsWallets(pxe); - const token = await Contract.deploy(pxe, TokenContractAbi, []).send().deployed(); - await token.withWallet(owner).methods._initialize(owner.getAddress()).send().wait(); + const token = await Contract.deploy(pxe, TokenContractAbi, [owner.getCompleteAddress()]).send().deployed(); console.log(`Token deployed at ${token.address.toString()}`); diff --git a/yarn-project/end-to-end/src/sample-dapp/index.test.mjs b/yarn-project/end-to-end/src/sample-dapp/index.test.mjs index 04d0bc2a2273..19d49926e452 100644 --- a/yarn-project/end-to-end/src/sample-dapp/index.test.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/index.test.mjs @@ -10,8 +10,7 @@ describe('token', () => { owner = await createAccount(pxe); recipient = await createAccount(pxe); - token = await Contract.deploy(owner, TokenContractAbi, []).send().deployed(); - await token.methods._initialize(owner.getAddress()).send().wait(); + token = await Contract.deploy(owner, TokenContractAbi, [owner.getCompleteAddress()]).send().deployed(); const initialBalance = 20n; const secret = Fr.random(); diff --git a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr index ac5563fa8353..f64c89c37dd6 100644 --- a/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/token_bridge_contract/src/main.nr @@ -2,7 +2,7 @@ mod util; mod token_interface; // Minimal implementation of the token bridge that can move funds between L1 <> L2. -// The bridge has a corresponding Portal contract on L1 that it is attached to +// The bridge has a corresponding Portal contract on L1 that it is attached to // And corresponds to a Token on L2 that uses the `AuthWit` accounts pattern. // Bridge has to be set as a minter on the token before it can be used @@ -40,10 +40,9 @@ contract TokenBridge { // Constructs the contract. #[aztec(private)] - fn constructor() { - // Currently not possible to execute public calls from constructor as code not yet available to sequencer. - // let selector = compute_selector("_initialize((Field))"); - // let _callStackItem = context.call_public_function(context.this_address(), selector, [context.msg_sender()]); + fn constructor(token: AztecAddress) { + let selector = compute_selector("_initialize((Field))"); + context.call_public_function(context.this_address(), selector, [token.address]); } // docs:start:claim_public @@ -60,7 +59,7 @@ contract TokenBridge { // Consume message and emit nullifier context.consume_l1_to_l2_message(msg_key, content_hash, secret); - // Mint tokens + // Mint tokens Token::at(storage.token.read()).mint_public(context, to.address, amount); 1 @@ -69,7 +68,7 @@ contract TokenBridge { // docs:start:exit_to_l1_public // Burns the appropriate amount of tokens and creates a L2 to L1 withdraw message publicly - // Requires `msg.sender` to give approval to the bridge to burn tokens on their behalf using witness signatures + // Requires `msg.sender` to give approval to the bridge to burn tokens on their behalf using witness signatures #[aztec(public)] fn exit_to_l1_public( recipient: EthereumAddress, // ethereum address to withdraw to @@ -79,11 +78,11 @@ contract TokenBridge { ) -> Field { // Send an L2 to L1 message let content = get_withdraw_content_hash(recipient.address, amount, callerOnL1.address); - context.message_portal(content); + context.message_portal(content); - // Burn tokens + // Burn tokens Token::at(storage.token.read()).burn_public(context, context.msg_sender(), amount, nonce); - + 1 } // docs:end:exit_to_l1_public @@ -102,10 +101,10 @@ contract TokenBridge { let content_hash = get_mint_private_content_hash(amount, secret_hash_for_redeeming_minted_notes, canceller.address); context.consume_l1_to_l2_message(msg_key, content_hash, secret_for_L1_to_L2_message_consumption); - // Mint tokens on L2 - // `mint_private` on token is public. So we call an internal public function + // Mint tokens on L2 + // `mint_private` on token is public. So we call an internal public function // which then calls the public method on the token contract. - // Since the secret_hash is passed, no secret is leaked. + // Since the secret_hash is passed, no secret is leaked. context.call_public_function( context.this_address(), compute_selector("_call_mint_on_token(Field,Field)"), @@ -117,7 +116,7 @@ contract TokenBridge { // docs:start:exit_to_l1_private // Burns the appropriate amount of tokens and creates a L2 to L1 withdraw message privately - // Requires `msg.sender` (caller of the method) to give approval to the bridge to burn tokens on their behalf using witness signatures + // Requires `msg.sender` (caller of the method) to give approval to the bridge to burn tokens on their behalf using witness signatures #[aztec(private)] fn exit_to_l1_private( recipient: EthereumAddress, // ethereum address to withdraw to @@ -130,7 +129,7 @@ contract TokenBridge { let content = get_withdraw_content_hash(recipient.address, amount, callerOnL1.address); context.message_portal(content); - // Assert that user provided token address is same as seen in storage. + // Assert that user provided token address is same as seen in storage. context.call_public_function(context.this_address(), compute_selector("_assert_token_is_same(Field)"), [token.address]); // Burn tokens @@ -141,25 +140,20 @@ contract TokenBridge { /// docs:end:exit_to_l1_private // View function that is callable by other contracts. - // Unconstrained can't be called by others since it isn't safe. + // Unconstrained can't be called by others since it isn't safe. #[aztec(public)] fn get_token() -> Field { storage.token.read() } - - // /// Unconstrained /// + + // /// Unconstrained /// unconstrained fn token() -> Field { storage.token.read() } - /// SHOULD BE Internal /// - - // We cannot do this from the constructor currently - // Since this should be internal, for now, we ignore the safety checks of it, as they are - // enforced by it being internal and only called from the constructor. #[aztec(public)] - fn _initialize(token: AztecAddress) { + internal fn _initialize(token: AztecAddress) { storage.token.write(token.address); } diff --git a/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr index a100c701794b..25163712a367 100644 --- a/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr @@ -112,10 +112,9 @@ contract Token { // docs:start:constructor #[aztec(private)] - fn constructor() { - // Currently not possible to execute public calls from constructor as code not yet available to sequencer. - // let selector = compute_selector("_initialize((Field))"); - // let _callStackItem = context.call_public_function(context.this_address(), selector, [context.msg_sender()]); + fn constructor(admin: AztecAddress) { + let selector = compute_selector("_initialize((Field))"); + context.call_public_function(context.this_address(), selector, [admin.address]); } // docs:end:constructor @@ -334,7 +333,7 @@ contract Token { 1 } // docs:end:transfer - + // docs:start:burn #[aztec(private)] fn burn( @@ -359,14 +358,9 @@ contract Token { } // docs:end:burn - /// SHOULD BE Internal /// - // docs:start:initialize - // We cannot do this from the constructor currently - // Since this should be internal, for now, we ignore the safety checks of it, as they are - // enforced by it being internal and only called from the constructor. #[aztec(public)] - fn _initialize( + internal fn _initialize( new_admin: AztecAddress, ) { storage.admin.write(new_admin); @@ -398,7 +392,7 @@ contract Token { } // docs:end:reduce_total_supply - /// Unconstrained /// + /// Unconstrained /// // docs:start:admin unconstrained fn admin() -> Field { @@ -453,4 +447,4 @@ contract Token { } // docs:end:compute_note_hash_and_nullifier } -// docs:end:token_all \ No newline at end of file +// docs:end:token_all diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 40c185cf6ec9..2edcae3d43fb 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -548,7 +548,10 @@ export class PXEService implements PXE { const unencryptedLogs = new TxL2Logs(collectUnencryptedLogs(executionResult)); const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult); - const contractData = new ContractData(newContract?.completeAddress.address ?? AztecAddress.ZERO, EthAddress.ZERO); + const contractData = new ContractData( + newContract?.completeAddress.address ?? AztecAddress.ZERO, + newContract?.portalContract ?? EthAddress.ZERO, + ); const extendedContractData = new ExtendedContractData( contractData, newContractPublicFunctions, diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index d049b30dc3e0..90764a5f369e 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -55,6 +55,7 @@ export class SequencerClient { blockBuilder, l2BlockSource, l1ToL2MessageSource, + contractDataSource, publicProcessorFactory, config, ); 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 1362c3c5a312..ec9fcdf3a9ef 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -27,17 +27,7 @@ import { makeSelector, } from '@aztec/circuits.js/factories'; import { padArrayEnd } from '@aztec/foundation/collection'; -import { - ContractDataSource, - EncodedContractFunction, - ExtendedContractData, - FunctionCall, - FunctionL2Logs, - SiblingPath, - Tx, - TxL2Logs, - mockTx, -} from '@aztec/types'; +import { ExtendedContractData, FunctionCall, FunctionL2Logs, SiblingPath, Tx, TxL2Logs, mockTx } from '@aztec/types'; import { MerkleTreeOperations, TreeInfo } from '@aztec/world-state'; import { MockProxy, mock } from 'jest-mock-extended'; @@ -45,6 +35,7 @@ import times from 'lodash.times'; import { PublicProver } from '../prover/index.js'; import { PublicKernelCircuitSimulator } from '../simulator/index.js'; +import { ContractsDataSourcePublicDB } from '../simulator/public_executor.js'; import { WasmPublicKernelCircuitSimulator } from '../simulator/public_kernel.js'; import { PublicProcessor } from './public_processor.js'; @@ -52,10 +43,8 @@ describe('public_processor', () => { let db: MockProxy; let publicExecutor: MockProxy; let publicProver: MockProxy; - let contractDataSource: MockProxy; + let publicContractsDB: MockProxy; - let publicFunction: EncodedContractFunction; - let contractData: ExtendedContractData; let proof: Proof; let root: Buffer; @@ -65,18 +54,14 @@ describe('public_processor', () => { db = mock(); publicExecutor = mock(); publicProver = mock(); - contractDataSource = mock(); + publicContractsDB = mock(); - contractData = ExtendedContractData.random(); - publicFunction = EncodedContractFunction.random(); proof = makeEmptyProof(); root = Buffer.alloc(32, 5); publicProver.getPublicCircuitProof.mockResolvedValue(proof); publicProver.getPublicKernelCircuitProof.mockResolvedValue(proof); db.getTreeInfo.mockResolvedValue({ root } as TreeInfo); - contractDataSource.getExtendedContractData.mockResolvedValue(contractData); - contractDataSource.getPublicFunction.mockResolvedValue(publicFunction); }); describe('with mock circuits', () => { @@ -89,9 +74,9 @@ describe('public_processor', () => { publicExecutor, publicKernel, publicProver, - contractDataSource, GlobalVariables.empty(), HistoricBlockData.empty(), + publicContractsDB, ); }); @@ -145,9 +130,9 @@ describe('public_processor', () => { publicExecutor, publicKernel, publicProver, - contractDataSource, GlobalVariables.empty(), HistoricBlockData.empty(), + publicContractsDB, ); }); diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.ts index 3195989c1b2d..1f63c6f4b82a 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.ts @@ -47,7 +47,8 @@ import { MerkleTreeOperations } from '@aztec/world-state'; import { getVerificationKeys } from '../index.js'; import { EmptyPublicProver } from '../prover/empty.js'; import { PublicProver } from '../prover/index.js'; -import { PublicKernelCircuitSimulator, getPublicExecutor } from '../simulator/index.js'; +import { PublicKernelCircuitSimulator } from '../simulator/index.js'; +import { ContractsDataSourcePublicDB, getPublicExecutor } from '../simulator/public_executor.js'; import { WasmPublicKernelCircuitSimulator } from '../simulator/public_kernel.js'; import { FailedTx, ProcessedTx, makeEmptyProcessedTx, makeProcessedTx } from './processed_tx.js'; import { getHistoricBlockData } from './utils.js'; @@ -66,6 +67,7 @@ export class PublicProcessorFactory { * Creates a new instance of a PublicProcessor. * @param prevGlobalVariables - The global variables for the previous block, used to calculate the prev global variables hash. * @param globalVariables - The global variables for the block being processed. + * @param newContracts - Provides access to contract bytecode for public executions. * @returns A new instance of a PublicProcessor. */ public async create( @@ -73,14 +75,15 @@ export class PublicProcessorFactory { globalVariables: GlobalVariables, ): Promise { const blockData = await getHistoricBlockData(this.merkleTree, prevGlobalVariables); + const publicContractsDB = new ContractsDataSourcePublicDB(this.contractDataSource); return new PublicProcessor( this.merkleTree, - getPublicExecutor(this.merkleTree, this.contractDataSource, this.l1Tol2MessagesDataSource, blockData), + getPublicExecutor(this.merkleTree, publicContractsDB, this.l1Tol2MessagesDataSource, blockData), new WasmPublicKernelCircuitSimulator(), new EmptyPublicProver(), - this.contractDataSource, globalVariables, blockData, + publicContractsDB, ); } } @@ -95,9 +98,9 @@ export class PublicProcessor { protected publicExecutor: PublicExecutor, protected publicKernel: PublicKernelCircuitSimulator, protected publicProver: PublicProver, - protected contractDataSource: ContractDataSource, protected globalVariables: GlobalVariables, protected blockData: HistoricBlockData, + protected publicContractsDB: ContractsDataSourcePublicDB, private log = createDebugLogger('aztec:sequencer:public-processor'), ) {} @@ -116,6 +119,8 @@ export class PublicProcessor { for (const tx of txs) { this.log(`Processing tx ${await tx.getTxHash()}`); try { + // add new contracts to the contracts db so that their functions may be found and called + await this.publicContractsDB.addNewContracts(tx); result.push(await this.processTx(tx)); } catch (err) { this.log.warn(`Error processing tx ${await tx.getTxHash()}: ${err}`); @@ -123,8 +128,11 @@ export class PublicProcessor { tx, error: err instanceof Error ? err : new Error('Unknown error'), }); + // remove contracts on failure + await this.publicContractsDB.removeNewContracts(tx); } } + return [result, failed]; } @@ -405,6 +413,7 @@ export class PublicProcessor { PublicDataRead.empty(), MAX_PUBLIC_DATA_READS_PER_TX, ); + // Override kernel output publicInputs.end.publicDataUpdateRequests = padArrayEnd( [ diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index dc69b5f1b271..1ec5c92280a9 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -6,7 +6,16 @@ import { makeEmptyProof, } from '@aztec/circuits.js'; import { P2P, P2PClientState } from '@aztec/p2p'; -import { L1ToL2MessageSource, L2Block, L2BlockSource, MerkleTreeId, Tx, TxHash, mockTx } from '@aztec/types'; +import { + ContractDataSource, + L1ToL2MessageSource, + L2Block, + L2BlockSource, + MerkleTreeId, + Tx, + TxHash, + mockTx, +} from '@aztec/types'; import { MerkleTreeOperations, WorldStateRunningState, WorldStateSynchronizer } from '@aztec/world-state'; import { MockProxy, mock } from 'jest-mock-extended'; @@ -61,7 +70,7 @@ describe('sequencer', () => { }); publicProcessorFactory = mock({ - create: (_, __) => Promise.resolve(publicProcessor), + create: (_a, _b_) => Promise.resolve(publicProcessor), }); l2BlockSource = mock({ @@ -73,6 +82,8 @@ describe('sequencer', () => { getBlockNumber: () => Promise.resolve(lastBlockNumber), }); + const contractDataSource = mock({}); + sequencer = new TestSubject( publisher, globalVariableBuilder, @@ -81,6 +92,7 @@ describe('sequencer', () => { blockBuilder, l2BlockSource, l1ToL2MessageSource, + contractDataSource, publicProcessorFactory, { chainId: Number(chainId.value), diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index f4cbe839c76b..487b227f5d16 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; import { P2P } from '@aztec/p2p'; -import { L1ToL2MessageSource, L2Block, L2BlockSource, MerkleTreeId, Tx } from '@aztec/types'; +import { ContractDataSource, L1ToL2MessageSource, L2Block, L2BlockSource, MerkleTreeId, Tx } from '@aztec/types'; import { WorldStateStatus, WorldStateSynchronizer } from '@aztec/world-state'; import times from 'lodash.times'; @@ -41,6 +41,7 @@ export class Sequencer { private blockBuilder: BlockBuilder, private l2BlockSource: L2BlockSource, private l1ToL2MessageSource: L1ToL2MessageSource, + private contractDataSource: ContractDataSource, private publicProcessorFactory: PublicProcessorFactory, config: SequencerConfig, private log = createDebugLogger('aztec:sequencer'), diff --git a/yarn-project/sequencer-client/src/simulator/index.ts b/yarn-project/sequencer-client/src/simulator/index.ts index 0cc49389219f..a9ede5663766 100644 --- a/yarn-project/sequencer-client/src/simulator/index.ts +++ b/yarn-project/sequencer-client/src/simulator/index.ts @@ -8,8 +8,6 @@ import { RootRollupPublicInputs, } from '@aztec/circuits.js'; -export { getPublicExecutor } from './public_executor.js'; - /** * Circuit simulator for the rollup circuits. */ diff --git a/yarn-project/sequencer-client/src/simulator/public_executor.ts b/yarn-project/sequencer-client/src/simulator/public_executor.ts index 1296dcae50a6..6928cff3ffcd 100644 --- a/yarn-project/sequencer-client/src/simulator/public_executor.ts +++ b/yarn-project/sequencer-client/src/simulator/public_executor.ts @@ -6,7 +6,7 @@ import { PublicStateDB, } from '@aztec/acir-simulator'; import { AztecAddress, CircuitsWasm, EthAddress, Fr, FunctionSelector, HistoricBlockData } from '@aztec/circuits.js'; -import { ContractDataSource, L1ToL2MessageSource, MerkleTreeId } from '@aztec/types'; +import { ContractDataSource, ExtendedContractData, L1ToL2MessageSource, MerkleTreeId, Tx } from '@aztec/types'; import { MerkleTreeOperations, computePublicDataTreeLeafIndex } from '@aztec/world-state'; /** @@ -17,13 +17,13 @@ import { MerkleTreeOperations, computePublicDataTreeLeafIndex } from '@aztec/wor */ export function getPublicExecutor( merkleTree: MerkleTreeOperations, - contractDataSource: ContractDataSource, + publicContractsDB: PublicContractsDB, l1toL2MessageSource: L1ToL2MessageSource, blockData: HistoricBlockData, ) { return new PublicExecutor( new WorldStatePublicDB(merkleTree), - new ContractsDataSourcePublicDB(contractDataSource), + publicContractsDB, new WorldStateDB(merkleTree, l1toL2MessageSource), blockData, ); @@ -31,17 +31,63 @@ export function getPublicExecutor( /** * Implements the PublicContractsDB using a ContractDataSource. + * Progresively records contracts in transaction as they are processed in a block. */ -class ContractsDataSourcePublicDB implements PublicContractsDB { +export class ContractsDataSourcePublicDB implements PublicContractsDB { + cache = new Map(); + constructor(private db: ContractDataSource) {} + + /** + * Add new contracts from a transaction + * @param tx - The transaction to add contracts from. + */ + public addNewContracts(tx: Tx): Promise { + for (const contract of tx.newContracts) { + const contractAddress = contract.contractData.contractAddress; + + if (contractAddress.isZero()) { + continue; + } + + this.cache.set(contractAddress.toString(), contract); + } + + return Promise.resolve(); + } + + /** + * Removes new contracts added from transactions + * @param tx - The tx's contracts to be removed + */ + public removeNewContracts(tx: Tx): Promise { + for (const contract of tx.newContracts) { + const contractAddress = contract.contractData.contractAddress; + + if (contractAddress.isZero()) { + continue; + } + + this.cache.delete(contractAddress.toString()); + } + return Promise.resolve(); + } + async getBytecode(address: AztecAddress, selector: FunctionSelector): Promise { - return (await this.db.getPublicFunction(address, selector))?.bytecode; + const contract = await this.#getContract(address); + return contract?.getPublicFunction(selector)?.bytecode; } async getIsInternal(address: AztecAddress, selector: FunctionSelector): Promise { - return (await this.db.getPublicFunction(address, selector))?.isInternal; + const contract = await this.#getContract(address); + return contract?.getPublicFunction(selector)?.isInternal; } async getPortalContractAddress(address: AztecAddress): Promise { - return (await this.db.getContractData(address))?.portalContractAddress; + const contract = await this.#getContract(address); + return contract?.contractData.portalContractAddress; + } + + async #getContract(address: AztecAddress): Promise { + return this.cache.get(address.toString()) ?? (await this.db.getExtendedContractData(address)); } }