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: call public fn in contract constructor #2549

Merged
merged 19 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion circuits/cpp/src/aztec3/circuits/kernel/public/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ void common_initialise_end_values(PublicKernelInputs<NT> 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;
}

/**
Expand Down Expand Up @@ -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
} // namespace aztec3::circuits::kernel::public_kernel
2 changes: 1 addition & 1 deletion docs/docs/dev_docs/tutorials/writing_token_contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
27 changes: 7 additions & 20 deletions yarn-project/canary/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);

Expand Down
5 changes: 3 additions & 2 deletions yarn-project/end-to-end/src/canary/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
7 changes: 1 addition & 6 deletions yarn-project/end-to-end/src/canary/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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+(?<address>0x[a-fA-F0-9]+)/)?.groups?.address;
expect(loggedAddress).toBeDefined();
contractAddress = AztecAddress.fromString(loggedAddress!);
Expand All @@ -131,11 +131,6 @@ export const cliTestSuite = (
const checkResult = findInLogs(/Contract\sfound\sat\s+(?<address>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);

Expand Down
3 changes: 1 addition & 2 deletions yarn-project/end-to-end/src/e2e_2_pxes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
59 changes: 51 additions & 8 deletions yarn-project/end-to-end/src/e2e_block_building.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<void>;

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());
Expand All @@ -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) {
Expand All @@ -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
Expand All @@ -59,8 +102,8 @@ describe('e2e_block_building', () => {
let teardown: () => Promise<void>;

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());
Expand All @@ -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 () => {
Expand Down
4 changes: 1 addition & 3 deletions yarn-project/end-to-end/src/e2e_escrow_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
6 changes: 2 additions & 4 deletions yarn-project/end-to-end/src/e2e_lending_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
5 changes: 2 additions & 3 deletions yarn-project/end-to-end/src/e2e_sandbox_example.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()}`);
Expand Down
3 changes: 1 addition & 2 deletions yarn-project/end-to-end/src/e2e_token_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,14 @@ 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,
logger,
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 => {
Expand Down
26 changes: 6 additions & 20 deletions yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);

Expand Down
Loading