Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: bad contract txs publishing contract data #2673

Merged
merged 10 commits into from
Oct 26, 2023
1 change: 1 addition & 0 deletions yarn-project/boxes/blank/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
49 changes: 47 additions & 2 deletions yarn-project/end-to-end/src/e2e_deploy_contract.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -11,10 +12,11 @@ describe('e2e_deploy_contract', () => {
let accounts: CompleteAddress[];
let logger: DebugLogger;
let wallet: Wallet;
let sequencer: SequencerClient | undefined;
let teardown: () => Promise<void>;

beforeEach(async () => {
({ teardown, pxe, accounts, logger, wallet } = await setup());
({ teardown, pxe, accounts, logger, wallet, sequencer } = await setup());
}, 100_000);

afterEach(() => teardown());
Expand Down Expand Up @@ -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,
});
}
Comment on lines +165 to +169
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't really need to restore the sequencer config, since you're creating a new one for each test due to the beforeEach, right?

});
});
6 changes: 6 additions & 0 deletions yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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(),
Expand All @@ -212,6 +214,8 @@ type SetupOptions = { /** State load */ stateLoad?: string } & Partial<AztecNode
export type EndToEndContext = {
/** The Aztec Node service or client a connected to it. */
aztecNode: AztecNode | undefined;
/** A client to the sequencer service */
sequencer: SequencerClient | undefined;
/** The Private eXecution Environment (PXE). */
pxe: PXE;
/** Return values from deployL1Contracts function. */
Expand Down Expand Up @@ -273,6 +277,7 @@ export async function setup(numberOfAccounts = 1, opts: SetupOptions = {}): Prom

logger('Creating and synching an aztec node...');
const aztecNode = await AztecNodeService.createAndSync(config);
const sequencer = aztecNode.getSequencer();

const { pxe, accounts, wallets } = await setupPXEService(numberOfAccounts, aztecNode!, logger);

Expand All @@ -293,6 +298,7 @@ export async function setup(numberOfAccounts = 1, opts: SetupOptions = {}): Prom
wallets,
logger,
cheatCodes,
sequencer,
teardown,
};
}
Expand Down
6 changes: 3 additions & 3 deletions yarn-project/noir-contracts/Nargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[workspace]
members = [
"src/contracts/benchmarking_contract",
"src/contracts/card_game_contract",
"src/contracts/child_contract",
"src/contracts/benchmarking_contract",
"src/contracts/card_game_contract",
"src/contracts/child_contract",
"src/contracts/docs_example_contract",
"src/contracts/easy_private_token_contract",
"src/contracts/ecdsa_account_contract",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ contract Token {
// docs:start:import_authwit
use dep::authwit::{
auth::{
assert_current_call_valid_authwit,
assert_current_call_valid_authwit_public,
assert_current_call_valid_authwit,
assert_current_call_valid_authwit_public,
},
};
// docs:end:import_authwit
Expand Down Expand Up @@ -363,6 +363,7 @@ contract Token {
internal fn _initialize(
new_admin: AztecAddress,
) {
assert(new_admin.address != 0, "invalid admin");
storage.admin.write(new_admin);
storage.minters.at(new_admin.address).write(true);
}
Expand Down
6 changes: 4 additions & 2 deletions yarn-project/sequencer-client/src/sequencer/processed_tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import {
PublicKernelPublicInputs,
makeEmptyProof,
} from '@aztec/circuits.js';
import { Tx, TxHash, TxL2Logs } from '@aztec/types';
import { ExtendedContractData, Tx, TxHash, TxL2Logs } from '@aztec/types';

/**
* Represents a tx that has been processed by the sequencer public processor,
* so its kernel circuit public inputs are filled in.
*/
export type ProcessedTx = Pick<Tx, 'proof' | 'encryptedLogs' | 'unencryptedLogs'> & {
export type ProcessedTx = Pick<Tx, 'proof' | 'encryptedLogs' | 'unencryptedLogs' | 'newContracts'> & {
/**
* Output of the public kernel circuit for this tx.
*/
Expand Down Expand Up @@ -78,6 +78,7 @@ export async function makeProcessedTx(
proof: proof ?? tx.proof,
encryptedLogs: tx.encryptedLogs,
unencryptedLogs: tx.unencryptedLogs,
newContracts: tx.newContracts,
isEmpty: false,
};
}
Expand All @@ -104,6 +105,7 @@ export function makeEmptyProcessedTx(
unencryptedLogs: new TxL2Logs([]),
data: emptyKernelOutput,
proof: emptyProof,
newContracts: [ExtendedContractData.empty()],
isEmpty: true,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ describe('public_processor', () => {
proof: tx.proof,
encryptedLogs: tx.encryptedLogs,
unencryptedLogs: tx.unencryptedLogs,
newContracts: tx.newContracts,
},
]);
expect(failed).toEqual([]);
Expand Down
9 changes: 3 additions & 6 deletions yarn-project/sequencer-client/src/sequencer/sequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export class Sequencer {

await assertBlockHeight();

await this.publishExtendedContractData(validTxs, block);
await this.publishExtendedContractData(processedValidTxs, block);

await assertBlockHeight();

Expand All @@ -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<typeof cd, undefined> => cd !== undefined);

Expand Down