Skip to content

Commit

Permalink
Fix and refactor e2e deploy tests
Browse files Browse the repository at this point in the history
  • Loading branch information
spalladino committed Mar 13, 2024
1 parent 90a4f3c commit 235d22d
Showing 1 changed file with 111 additions and 91 deletions.
202 changes: 111 additions & 91 deletions yarn-project/end-to-end/src/e2e_deploy_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
Fr,
PXE,
SignerlessWallet,
TxStatus,
Wallet,
getContractClassFromArtifact,
getContractInstanceFromDeployParams,
Expand Down Expand Up @@ -179,7 +178,7 @@ describe('e2e_deploy_contract', () => {
const testWallet = kind === 'as entrypoint' ? new SignerlessWallet(pxe) : wallet;
const owner = await registerRandomAccount(pxe);
const initArgs: StatefulContractCtorArgs = [owner, 42];
const contract = await registerContract(testWallet, StatefulTestContract, initArgs);
const contract = await registerContract(testWallet, StatefulTestContract, { initArgs });
logger.info(`Calling the constructor for ${contract.address}`);
await contract.methods
.constructor(...initArgs)
Expand All @@ -197,9 +196,11 @@ describe('e2e_deploy_contract', () => {
// Tests privately initializing multiple undeployed contracts on the same tx through an account contract.
it('initializes multiple undeployed contracts in a single tx', async () => {
const owner = await registerRandomAccount(pxe);
const initArgs: StatefulContractCtorArgs[] = [42, 52].map(value => [owner, value]);
const contracts = await Promise.all(initArgs.map(args => registerContract(wallet, StatefulTestContract, args)));
const calls = contracts.map((c, i) => c.methods.constructor(...initArgs[i]).request());
const initArgss: StatefulContractCtorArgs[] = [42, 52].map(value => [owner, value]);
const contracts = await Promise.all(
initArgss.map(initArgs => registerContract(wallet, StatefulTestContract, { initArgs })),
);
const calls = contracts.map((c, i) => c.methods.constructor(...initArgss[i]).request());
await new BatchCall(wallet, calls).send().wait();
expect(await contracts[0].methods.summed_values(owner).view()).toEqual(42n);
expect(await contracts[1].methods.summed_values(owner).view()).toEqual(52n);
Expand All @@ -209,7 +210,7 @@ describe('e2e_deploy_contract', () => {
it.skip('initializes and calls a private function in a single tx', async () => {
const owner = await registerRandomAccount(pxe);
const initArgs: StatefulContractCtorArgs = [owner, 42];
const contract = await registerContract(wallet, StatefulTestContract, initArgs);
const contract = await registerContract(wallet, StatefulTestContract, { initArgs });
const batch = new BatchCall(wallet, [
contract.methods.constructor(...initArgs).request(),
contract.methods.create_note(owner, 10).request(),
Expand All @@ -222,7 +223,7 @@ describe('e2e_deploy_contract', () => {
it('refuses to initialize a contract twice', async () => {
const owner = await registerRandomAccount(pxe);
const initArgs: StatefulContractCtorArgs = [owner, 42];
const contract = await registerContract(wallet, StatefulTestContract, initArgs);
const contract = await registerContract(wallet, StatefulTestContract, { initArgs });
await contract.methods
.constructor(...initArgs)
.send()
Expand All @@ -238,7 +239,7 @@ describe('e2e_deploy_contract', () => {
it('refuses to call a private function that requires initialization', async () => {
const owner = await registerRandomAccount(pxe);
const initArgs: StatefulContractCtorArgs = [owner, 42];
const contract = await registerContract(wallet, StatefulTestContract, initArgs);
const contract = await registerContract(wallet, StatefulTestContract, { initArgs });
// TODO(@spalladino): It'd be nicer to be able to fail the assert with a more descriptive message.
await expect(contract.methods.create_note(owner, 10).send().wait()).rejects.toThrow(
/nullifier witness not found/i,
Expand All @@ -247,7 +248,7 @@ describe('e2e_deploy_contract', () => {

it('refuses to initialize a contract with incorrect args', async () => {
const owner = await registerRandomAccount(pxe);
const contract = await registerContract(wallet, StatefulTestContract, [owner, 42]);
const contract = await registerContract(wallet, StatefulTestContract, { initArgs: [owner, 42] });
await expect(contract.methods.constructor(owner, 43).simulate()).rejects.toThrow(
/Initialization hash does not match/,
);
Expand Down Expand Up @@ -301,7 +302,7 @@ describe('e2e_deploy_contract', () => {
let initArgs: StatefulContractCtorArgs;
let contract: StatefulTestContract;

const deployInstance = async () => {
const deployInstance = async (opts: { constructorName?: string } = {}) => {
const initArgs = [accounts[0].address, 42] as StatefulContractCtorArgs;
const salt = Fr.random();
const portalAddress = EthAddress.random();
Expand All @@ -311,6 +312,7 @@ describe('e2e_deploy_contract', () => {
salt,
publicKey,
portalAddress,
constructorName: opts.constructorName,
});
const { address, contractClassId } = instance;
logger(`Deploying contract instance at ${address.toString()} class id ${contractClassId.toString()}`);
Expand All @@ -325,95 +327,113 @@ describe('e2e_deploy_contract', () => {
// we are not going to run those - but this may require registering "partial" contracts in the pxe.
// Anyway, when we implement that, we should be able to replace this `registerContract` with
// a simpler `Contract.at(instance.address, wallet)`.
const registered = await registerContract(wallet, StatefulTestContract, initArgs, {
const registered = await registerContract(wallet, StatefulTestContract, {
constructorName: opts.constructorName,
salt: instance.salt,
portalAddress: instance.portalContractAddress,
publicKey,
initArgs,
});
expect(registered.address).toEqual(instance.address);
const contract = await StatefulTestContract.at(instance.address, wallet);
return { contract, initArgs, instance, publicKey };
};

beforeAll(async () => {
({ instance, initArgs, contract } = await deployInstance());
}, 60_000);

it('stores contract instance in the aztec node', async () => {
const deployed = await aztecNode.getContract(instance.address);
expect(deployed).toBeDefined();
expect(deployed!.address).toEqual(instance.address);
expect(deployed!.contractClassId).toEqual(contractClass.id);
expect(deployed!.initializationHash).toEqual(instance.initializationHash);
expect(deployed!.portalContractAddress).toEqual(instance.portalContractAddress);
expect(deployed!.publicKeysHash).toEqual(instance.publicKeysHash);
expect(deployed!.salt).toEqual(instance.salt);
describe('using a private constructor', () => {
beforeAll(async () => {
({ instance, initArgs, contract } = await deployInstance());
}, 60_000);

it('stores contract instance in the aztec node', async () => {
const deployed = await aztecNode.getContract(instance.address);
expect(deployed).toBeDefined();
expect(deployed!.address).toEqual(instance.address);
expect(deployed!.contractClassId).toEqual(contractClass.id);
expect(deployed!.initializationHash).toEqual(instance.initializationHash);
expect(deployed!.portalContractAddress).toEqual(instance.portalContractAddress);
expect(deployed!.publicKeysHash).toEqual(instance.publicKeysHash);
expect(deployed!.salt).toEqual(instance.salt);
});

it('calls a public function with no init check on the deployed instance', async () => {
const whom = AztecAddress.random();
await contract.methods
.increment_public_value_no_init_check(whom, 10)
.send({ skipPublicSimulation: true })
.wait();
const stored = await contract.methods.get_public_value(whom).view();
expect(stored).toEqual(10n);
}, 30_000);

it('refuses to call a public function with init check if the instance is not initialized', async () => {
const whom = AztecAddress.random();
await contract.methods.increment_public_value(whom, 10).send({ skipPublicSimulation: true }).wait();

// TODO(#4972) check for reverted flag
// Meanwhile we check we didn't increment the value
expect(await contract.methods.get_public_value(whom).view()).toEqual(0n);
}, 30_000);

it('refuses to initialize the instance with wrong args via a private function', async () => {
await expect(contract.methods.constructor(AztecAddress.random(), 43).simulate()).rejects.toThrow(
/initialization hash does not match/i,
);
}, 30_000);

it('initializes the contract and calls a public function', async () => {
await contract.methods
.constructor(...initArgs)
.send()
.wait();
const whom = AztecAddress.random();
await contract.methods.increment_public_value(whom, 10).send({ skipPublicSimulation: true }).wait();
const stored = await contract.methods.get_public_value(whom).view();
expect(stored).toEqual(10n);
}, 30_000);

it('refuses to reinitialize the contract', async () => {
await expect(
contract.methods
.constructor(...initArgs)
.send({ skipPublicSimulation: true })
.wait(),
).rejects.toThrow(/dropped/i);
}, 30_000);
});

it('calls a public function with no init check on the deployed instance', async () => {
const whom = AztecAddress.random();
await contract.methods
.increment_public_value_no_init_check(whom, 10)
.send({ skipPublicSimulation: true })
.wait();
const stored = await contract.methods.get_public_value(whom).view();
expect(stored).toEqual(10n);
}, 30_000);

it('refuses to call a public function with init check if the instance is not initialized', async () => {
// TODO(#4972) check for reverted flag
const receipt = await contract.methods
.increment_public_value(AztecAddress.random(), 10)
.send({ skipPublicSimulation: true })
.wait();

expect(receipt.status).toEqual(TxStatus.MINED);
}, 30_000);

it('refuses to initialize the instance with wrong args via a public function', async () => {
const whom = AztecAddress.random();
// TODO(@spalladino): This tx is mined but reverts, we need to check revert flag once it's available
// Meanwhile, we check that its side effects did not come through as a means to assert it reverted
await contract.methods.public_constructor(whom, 43).send({ skipPublicSimulation: true }).wait();
expect(await contract.methods.get_public_value(whom).view()).toEqual(0n);
}, 30_000);

it('calls a public function with init check after initialization', async () => {
await contract.methods
.constructor(...initArgs)
.send()
.wait();
const whom = AztecAddress.random();
await contract.methods.increment_public_value(whom, 10).send({ skipPublicSimulation: true }).wait();
const stored = await contract.methods.get_public_value(whom).view();
expect(stored).toEqual(10n);
}, 30_000);

it('refuses to reinitialize the contract', async () => {
await expect(
contract.methods
describe('using a public constructor', () => {
beforeAll(async () => {
({ instance, initArgs, contract } = await deployInstance({ constructorName: 'public_constructor' }));
}, 60_000);

it('refuses to initialize the instance with wrong args via a public function', async () => {
// TODO(@spalladino): This tx is mined but reverts, we need to check revert flag once it's available
// Meanwhile, we check that its side effects did not come through as a means to assert it reverted
const whom = AztecAddress.random();
await contract.methods.public_constructor(whom, 43).send({ skipPublicSimulation: true }).wait();
expect(await contract.methods.get_public_value(whom).view()).toEqual(0n);
}, 30_000);

it('initializes the contract and calls a public function', async () => {
await contract.methods
.public_constructor(...initArgs)
.send({ skipPublicSimulation: true })
.wait(),
).rejects.toThrow(/dropped/i);
}, 30_000);

it('initializes a new instance of the contract via a public function', async () => {
const { contract, initArgs } = await deployInstance();
const whom = initArgs[0];
logger.info(`Initializing contract at ${contract.address} via a public function`);
await contract.methods
.public_constructor(...initArgs)
.send({ skipPublicSimulation: true })
.wait();
expect(await contract.methods.get_public_value(whom).view()).toEqual(42n);
logger.info(`Calling a public function that requires initialization on ${contract.address}`);
await contract.methods.increment_public_value(whom, 10).send().wait();
expect(await contract.methods.get_public_value(whom).view()).toEqual(52n);
logger.info(`Calling a private function that requires initialization on ${contract.address}`);
await contract.methods.create_note(whom, 10).send().wait();
expect(await contract.methods.summed_values(whom).view()).toEqual(10n);
}, 90_000);
.send()
.wait();
const whom = AztecAddress.random();
await contract.methods.increment_public_value(whom, 10).send({ skipPublicSimulation: true }).wait();
const stored = await contract.methods.get_public_value(whom).view();
expect(stored).toEqual(10n);
}, 30_000);

it('refuses to reinitialize the contract', async () => {
await expect(
contract.methods
.public_constructor(...initArgs)
.send({ skipPublicSimulation: true })
.wait(),
).rejects.toThrow(/dropped/i);
}, 30_000);
});
});

testDeployingAnInstance('from a wallet', async instance => {
Expand Down Expand Up @@ -492,12 +512,12 @@ type ContractArtifactClass<T extends ContractBase> = {
async function registerContract<T extends ContractBase>(
wallet: Wallet,
contractArtifact: ContractArtifactClass<T>,
args: any[] = [],
opts: { salt?: Fr; publicKey?: Point; portalAddress?: EthAddress } = {},
opts: { salt?: Fr; publicKey?: Point; portalAddress?: EthAddress; initArgs?: any[]; constructorName?: string } = {},
): Promise<T> {
const { salt, publicKey, portalAddress } = opts;
const { salt, publicKey, portalAddress, initArgs, constructorName } = opts;
const instance = getContractInstanceFromDeployParams(contractArtifact.artifact, {
constructorArgs: args,
constructorArgs: initArgs ?? [],
constructorName,
salt,
publicKey,
portalAddress,
Expand Down

0 comments on commit 235d22d

Please sign in to comment.