From 14ab6d6664c769132d4fedffb9bdd33e364505e8 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 bd08b4c5bdb..87e9df7d6dc 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 872fe819aba..b5857dfc305 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 4d89e1a7b4e..efb29ebb328 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 bf145149e99..c2b7a642cd0 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 1974ee6573e..2bac457365d 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 f19ee8aebca..e3f8391ff52 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 a7e589d08ff..9336b1fc264 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 ad0315c5fa0..77d9db582d2 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 9ba8808215d..2b9469b922b 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 b1069faedc0..61a50b53866 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 c3ddec3f42a..665dc3714ad 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 c0d6a38ef0e..ad35a01bc68 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 43c02e7bffc..2955f2b2554 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 d0f28f6eac5..5c1bfdebed5 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 2ad7e7eab78..c97dd0d5e29 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 855dbcaf4ff..15d135e2459 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 4e66831c2d9..f61e3c4c881 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 7d9c655ac19..866c14889cd 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 04d0bc2a227..19d49926e45 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 ac5563fa835..f64c89c37dd 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 a100c701794..25163712a36 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 40c185cf6ec..2edcae3d43f 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 d049b30dc3e..90764a5f369 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 1362c3c5a31..ec9fcdf3a9e 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 3195989c1b2..1f63c6f4b82 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 dc69b5f1b27..1ec5c92280a 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 f4cbe839c76..487b227f5d1 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 0cc49389219..a9ede566376 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 1296dcae50a..6928cff3ffc 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)); } }