diff --git a/yarn-project/boxes/blank/src/index.ts b/yarn-project/boxes/blank/src/index.ts index 45e6bebde76..518e2cfc75b 100644 --- a/yarn-project/boxes/blank/src/index.ts +++ b/yarn-project/boxes/blank/src/index.ts @@ -14,6 +14,7 @@ import { } from '@aztec/aztec.js'; import { ContractArtifact, FunctionArtifact, encodeArguments } from '@aztec/foundation/abi'; import { FieldsOf } from '@aztec/foundation/types'; + // docs:end:imports export const contractArtifact: ContractArtifact = BlankContractArtifact; diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts index 79d057d051d..83d5dc3a47d 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts @@ -1,7 +1,8 @@ import { AztecAddress, Contract, ContractDeployer, EthAddress, Fr, Wallet, isContractDeployed } from '@aztec/aztec.js'; import { CompleteAddress, getContractDeploymentInfo } from '@aztec/circuits.js'; import { DebugLogger } from '@aztec/foundation/log'; -import { TestContractArtifact } from '@aztec/noir-contracts/artifacts'; +import { TestContractArtifact, TokenContractArtifact } from '@aztec/noir-contracts/artifacts'; +import { SequencerClient } from '@aztec/sequencer-client'; import { PXE, TxStatus } from '@aztec/types'; import { setup } from './fixtures/utils.js'; @@ -11,10 +12,11 @@ describe('e2e_deploy_contract', () => { let accounts: CompleteAddress[]; let logger: DebugLogger; let wallet: Wallet; + let sequencer: SequencerClient | undefined; let teardown: () => Promise; beforeEach(async () => { - ({ teardown, pxe, accounts, logger, wallet } = await setup()); + ({ teardown, pxe, accounts, logger, wallet, sequencer } = await setup()); }, 100_000); afterEach(() => teardown()); @@ -123,4 +125,47 @@ describe('e2e_deploy_contract', () => { portalContract.toString(), ); }); + + it('it should not deploy a contract which failed the public part of the execution', async () => { + sequencer?.updateSequencerConfig({ + minTxsPerBlock: 2, + }); + + try { + // This test requires at least another good transaction to go through in the same block as the bad one. + // I deployed the same contract again but it could really be any valid transaction here. + const goodDeploy = new ContractDeployer(TokenContractArtifact, wallet).deploy(AztecAddress.random()); + const badDeploy = new ContractDeployer(TokenContractArtifact, wallet).deploy(AztecAddress.ZERO); + + await Promise.all([ + goodDeploy.simulate({ skipPublicSimulation: true }), + badDeploy.simulate({ skipPublicSimulation: true }), + ]); + + const [goodTx, badTx] = [ + goodDeploy.send({ skipPublicSimulation: true }), + badDeploy.send({ skipPublicSimulation: true }), + ]; + + const [goodTxPromiseResult, badTxReceiptResult] = await Promise.allSettled([goodTx.wait(), badTx.wait()]); + + expect(goodTxPromiseResult.status).toBe('fulfilled'); + expect(badTxReceiptResult.status).toBe('rejected'); + + const [goodTxReceipt, badTxReceipt] = await Promise.all([goodTx.getReceipt(), badTx.getReceipt()]); + + expect(goodTxReceipt.blockNumber).toEqual(expect.any(Number)); + expect(badTxReceipt.blockNumber).toBeUndefined(); + + await expect(pxe.getExtendedContractData(goodDeploy.completeAddress!.address)).resolves.toBeDefined(); + await expect(pxe.getExtendedContractData(goodDeploy.completeAddress!.address)).resolves.toBeDefined(); + + await expect(pxe.getContractData(badDeploy.completeAddress!.address)).resolves.toBeUndefined(); + await expect(pxe.getExtendedContractData(badDeploy.completeAddress!.address)).resolves.toBeUndefined(); + } finally { + sequencer?.updateSequencerConfig({ + minTxsPerBlock: 1, + }); + } + }); }); diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 8c922c1114f..c4f63723ce6 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -28,6 +28,7 @@ import { RollupBytecode, } from '@aztec/l1-artifacts'; import { PXEService, createPXEService, getPXEServiceConfig } from '@aztec/pxe'; +import { SequencerClient } from '@aztec/sequencer-client'; import { AztecNode, L2BlockL2Logs, LogType, PXE, createAztecNodeRpcClient } from '@aztec/types'; import * as path from 'path'; @@ -193,6 +194,7 @@ async function setupWithSandbox(account: Account, config: AztecNodeConfig, logge const teardown = () => Promise.resolve(); return { aztecNode, + sequencer: undefined, pxe: pxeClient, deployL1ContractsValues, accounts: await pxeClient!.getRegisteredAccounts(), @@ -212,6 +214,8 @@ type SetupOptions = { /** State load */ stateLoad?: string } & Partial & { +export type ProcessedTx = Pick & { /** * Output of the public kernel circuit for this tx. */ @@ -78,6 +78,7 @@ export async function makeProcessedTx( proof: proof ?? tx.proof, encryptedLogs: tx.encryptedLogs, unencryptedLogs: tx.unencryptedLogs, + newContracts: tx.newContracts, isEmpty: false, }; } @@ -104,6 +105,7 @@ export function makeEmptyProcessedTx( unencryptedLogs: new TxL2Logs([]), data: emptyKernelOutput, proof: emptyProof, + newContracts: [ExtendedContractData.empty()], isEmpty: true, }); } 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 3e6686b3e61..e0761465d09 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -97,6 +97,7 @@ describe('public_processor', () => { proof: tx.proof, encryptedLogs: tx.encryptedLogs, unencryptedLogs: tx.unencryptedLogs, + newContracts: tx.newContracts, }, ]); expect(failed).toEqual([]); diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 56d45ce6ead..4b82ac99fa5 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -201,7 +201,7 @@ export class Sequencer { await assertBlockHeight(); - await this.publishExtendedContractData(validTxs, block); + await this.publishExtendedContractData(processedValidTxs, block); await assertBlockHeight(); @@ -218,16 +218,13 @@ export class Sequencer { * @param validTxs - The set of real transactions being published as part of the block. * @param block - The L2Block to be published. */ - protected async publishExtendedContractData(validTxs: Tx[], block: L2Block) { + protected async publishExtendedContractData(validTxs: ProcessedTx[], block: L2Block) { // Publishes contract data for txs to the network and awaits the tx to be mined this.state = SequencerState.PUBLISHING_CONTRACT_DATA; const newContractData = validTxs .map(tx => { // Currently can only have 1 new contract per tx - const newContract = tx.data?.end.newContracts[0]; - if (newContract) { - return tx.newContracts[0]; - } + return tx.newContracts[0]; }) .filter((cd): cd is Exclude => cd !== undefined);