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

feat: nuking Pokeable contract #2939

Merged
merged 12 commits into from
Oct 20, 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
7 changes: 6 additions & 1 deletion docs/docs/concepts/foundation/accounts/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@ A side-effect of not having nonces at the protocol level is that it is not possi

Since the `entrypoint` interface is not enshrined, there is nothing that differentiates an account contract from an application one in the protocol. This means that a transaction can be initiated in any contract. This allows implementing functions that do not need to be called by any particular user and are just intended to advance the state of a contract.

As an example, we can think of a lottery contract, where at some point a prize needs to be paid out to its winners. This `pay` action does not require authentication and does not need to be executed by any user in particular, so anyone could submit a transaction that defines the lottery contract itself as `origin` and `pay` as entrypoint function. For an example implementation of a different use case, refer to the [`pokeable_token_contract`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/pokeable_token_contract/src/main.nr) in the repository.
As an example, we can think of a lottery contract, where at some point a prize needs to be paid out to its winners. This `pay` action does not require authentication and does not need to be executed by any user in particular, so anyone could submit a transaction that defines the lottery contract itself as `origin` and `pay` as entrypoint function. For an example of this behavior see our [non_contract_account test](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/end-to-end/src/e2e_non_contract_account.test.ts) and the [SignerLess wallet](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/wallet/signerless_wallet.ts) implementation.
Notice that the Signerless wallet doesn't invoke an entrypoint function of an account contract but instead invokes the target contract function directly.

:::info
In case no contract entrypoint is used `msg_sender` is set to 0.
:::

### Account initialization

Expand Down
10 changes: 5 additions & 5 deletions yarn-project/acir-simulator/src/client/private_execution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ describe('Private Execution test suite', () => {
const dummyNote = { amount: 1, secretHash: 2 };
const deepStruct = { aField: 1, aBool: true, aNote: dummyNote, manyNotes: [dummyNote, dummyNote, dummyNote] };
args = [1, true, 1, [1, 2], dummyNote, deepStruct];
testCodeGenArtifact = TestContractArtifact.functions.find(f => f.name === 'testCodeGen')!;
testCodeGenArtifact = TestContractArtifact.functions.find(f => f.name === 'test_code_gen')!;
benesjan marked this conversation as resolved.
Show resolved Hide resolved
const serializedArgs = encodeArguments(testCodeGenArtifact, args);
argsHash = await computeVarArgsHash(await CircuitsWasm.get(), serializedArgs);
});
Expand Down Expand Up @@ -786,7 +786,7 @@ describe('Private Execution test suite', () => {
describe('get public key', () => {
it('gets the public key for an address', async () => {
// Tweak the contract artifact so we can extract return values
const artifact = getFunctionArtifact(TestContractArtifact, 'getPublicKey');
const artifact = getFunctionArtifact(TestContractArtifact, 'get_public_key');
artifact.returnTypes = [{ kind: 'array', length: 2, type: { kind: 'field' } }];

// Generate a partial address, pubkey, and resulting address
Expand All @@ -806,7 +806,7 @@ describe('Private Execution test suite', () => {
const aztecAddressToQuery = AztecAddress.random();

// Tweak the contract artifact so we can extract return values
const artifact = getFunctionArtifact(TestContractArtifact, 'getPortalContractAddress');
const artifact = getFunctionArtifact(TestContractArtifact, 'get_portal_contract_address');
artifact.returnTypes = [{ kind: 'field' }];

const args = [aztecAddressToQuery.toField()];
Expand All @@ -821,7 +821,7 @@ describe('Private Execution test suite', () => {
const contractAddress = AztecAddress.random();

// Tweak the contract artifact so we can extract return values
const artifact = getFunctionArtifact(TestContractArtifact, 'getThisAddress');
const artifact = getFunctionArtifact(TestContractArtifact, 'get_this_address');
artifact.returnTypes = [{ kind: 'field' }];

// Overwrite the oracle return value
Expand All @@ -833,7 +833,7 @@ describe('Private Execution test suite', () => {
const portalContractAddress = EthAddress.random();

// Tweak the contract artifact so we can extract return values
const artifact = getFunctionArtifact(TestContractArtifact, 'getThisPortalAddress');
const artifact = getFunctionArtifact(TestContractArtifact, 'get_this_portal_address');
artifact.returnTypes = [{ kind: 'field' }];

// Overwrite the oracle return value
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/acir-simulator/src/public/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ describe('ACIR public execution simulator', () => {

it('Should be able to create a L2 to L1 message from the public context', async () => {
const createL2ToL1MessagePublicArtifact = TestContractArtifact.functions.find(
f => f.name === 'createL2ToL1MessagePublic',
f => f.name === 'create_l2_to_l1_message_public',
)!;
const args = encodeArguments(createL2ToL1MessagePublicArtifact, params);

Expand Down Expand Up @@ -429,7 +429,7 @@ describe('ACIR public execution simulator', () => {

it('Should be able to create a nullifier from the public context', async () => {
const createNullifierPublicArtifact = TestContractArtifact.functions.find(
f => f.name === 'createNullifierPublic',
f => f.name === 'create_nullifier_public',
)!;

const args = encodeArguments(createNullifierPublicArtifact, params);
Expand Down
1 change: 0 additions & 1 deletion yarn-project/end-to-end/src/cli_docs_sandbox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ ImportTestContractArtifact
LendingContractArtifact
ParentContractArtifact
PendingCommitmentsContractArtifact
PokeableTokenContractArtifact
PriceFeedContractArtifact
SchnorrAccountContractArtifact
SchnorrHardcodedAccountContractArtifact
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/e2e_block_building.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ describe('e2e_block_building', () => {

it('drops tx with public nullifier already emitted on the same block', async () => {
const secret = Fr.random();
const calls = times(2, () => contract.methods.createNullifierPublic(140n, secret));
const calls = times(2, () => contract.methods.create_nullifier_public(140n, secret));
for (const call of calls) await call.simulate();
const [tx1, tx2] = calls.map(call => call.send());
await tx1.wait();
Expand All @@ -141,7 +141,7 @@ describe('e2e_block_building', () => {
);

const calls = [
contract.methods.createNullifierPublic(140n, secret),
contract.methods.create_nullifier_public(140n, secret),
contract.methods.emit_nullifier(emittedPublicNullifier),
];

Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/e2e_cheat_codes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,13 @@ describe('e2e_cheat_codes', () => {
expect(Number(await rollup.read.lastBlockTs())).toEqual(newTimestamp);
expect(Number(await rollup.read.lastWarpedBlockTs())).toEqual(newTimestamp);

const txIsTimeEqual = contract.methods.isTimeEqual(newTimestamp).send();
const txIsTimeEqual = contract.methods.is_time_equal(newTimestamp).send();
const isTimeEqualReceipt = await txIsTimeEqual.wait({ interval: 0.1 });
expect(isTimeEqualReceipt.status).toBe(TxStatus.MINED);

// Since last rollup block was warped, txs for this rollup will have time incremented by 1
// See https://github.com/AztecProtocol/aztec-packages/issues/1614 for details
const txTimeNotEqual = contract.methods.isTimeEqual(newTimestamp + 1).send();
const txTimeNotEqual = contract.methods.is_time_equal(newTimestamp + 1).send();
const isTimeNotEqualReceipt = await txTimeNotEqual.wait({ interval: 0.1 });
expect(isTimeNotEqualReceipt.status).toBe(TxStatus.MINED);
// block is published at t >= newTimestamp + 1.
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_deploy_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describe('e2e_deploy_contract', () => {

const contract = await Contract.at(receipt.contractAddress!, TestContractArtifact, wallet);
logger(`Sending TX to contract ${index + 1}...`);
await contract.methods.getPublicKey(accounts[0].address).send().wait();
await contract.methods.get_public_key(accounts[0].address).send().wait();
}
}, 30_000);

Expand Down
59 changes: 21 additions & 38 deletions yarn-project/end-to-end/src/e2e_non_contract_account.test.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,46 @@
import { AztecAddress, SignerlessWallet, Wallet } from '@aztec/aztec.js';
import { SignerlessWallet, Wallet } from '@aztec/aztec.js';
import { CircuitsWasm, Fr } from '@aztec/circuits.js';
import { siloNullifier } from '@aztec/circuits.js/abis';
import { DebugLogger } from '@aztec/foundation/log';
import { PokeableTokenContract } from '@aztec/noir-contracts/types';
import { AztecNode, CompleteAddress, PXE, TxStatus } from '@aztec/types';
import { TestContract } from '@aztec/noir-contracts/types';
import { AztecNode, PXE, TxStatus } from '@aztec/types';

import { expectsNumOfEncryptedLogsInTheLastBlockToBe, setup } from './fixtures/utils.js';
import { setup } from './fixtures/utils.js';

describe('e2e_non_contract_account', () => {
let aztecNode: AztecNode | undefined;
let pxe: PXE;
let wallet: Wallet;
let sender: AztecAddress;
let recipient: AztecAddress;
let pokerWallet: Wallet;
let nonContractAccountWallet: Wallet;
let teardown: () => Promise<void>;

let logger: DebugLogger;

let contract: PokeableTokenContract;

const initialBalance = 987n;
let contract: TestContract;

beforeEach(async () => {
let accounts: CompleteAddress[];
({ teardown, aztecNode, pxe, accounts, wallet, logger } = await setup(2));
sender = accounts[0].address;
recipient = accounts[1].address;
pokerWallet = new SignerlessWallet(pxe);
let wallet: Wallet;
({ teardown, aztecNode, pxe, wallet, logger } = await setup(1));
nonContractAccountWallet = new SignerlessWallet(pxe);

logger(`Deploying L2 contract...`);
const tx = PokeableTokenContract.deploy(pxe, initialBalance, sender, recipient).send();
await tx.isMined({ interval: 0.1 });
const receipt = await tx.getReceipt();
expect(receipt.status).toEqual(TxStatus.MINED);
contract = await TestContract.deploy(wallet).send().deployed();
logger('L2 contract deployed');
contract = await PokeableTokenContract.at(receipt.contractAddress!, wallet);
}, 100_000);

afterEach(() => teardown());

const expectBalance = async (owner: AztecAddress, expectedBalance: bigint) => {
const balance = await contract.methods.getBalance(owner).view({ from: owner });
logger(`Account ${owner} balance: ${balance}`);
expect(balance).toBe(expectedBalance);
};

it('Arbitrary non-contract account can call a private function on a contract', async () => {
await expectBalance(sender, initialBalance);
await expectBalance(recipient, 0n);
await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 1);

const contractWithNoContractWallet = await PokeableTokenContract.at(contract.address, pokerWallet);
const contractWithNoContractWallet = await TestContract.at(contract.address, nonContractAccountWallet);

// Send transaction as poker (arbitrary non-contract account)
await contractWithNoContractWallet.methods.poke(sender, recipient).send().wait({ interval: 0.1 });
// Send transaction as arbitrary non-contract account
const nullifier = new Fr(940);
const receipt = await contractWithNoContractWallet.methods.emit_nullifier(nullifier).send().wait({ interval: 0.1 });
expect(receipt.status).toBe(TxStatus.MINED);

// Initial balance should be fully transferred to the recipient
await expectBalance(sender, 0n);
await expectBalance(recipient, initialBalance);
const tx = await aztecNode!.getTx(receipt.txHash);
const expectedSiloedNullifier = siloNullifier(await CircuitsWasm.get(), contract.address, nullifier);
const siloedNullifier = tx!.newNullifiers[1];

await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 1);
expect(siloedNullifier.equals(expectedSiloedNullifier)).toBeTruthy();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not expect(siloedNullifier).toEqual(expectedSiloedNullifier)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It didn't work and I assume it was because TS was comparing object identity and not its values.

}, 120_000);
});
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/guides/dapp_testing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ describe('guides/dapp/testing', () => {
// docs:start:warp
const newTimestamp = Math.floor(Date.now() / 1000) + 60 * 60 * 24;
await cheats.aztec.warp(newTimestamp);
await testContract.methods.isTimeEqual(newTimestamp).send().wait();
await testContract.methods.is_time_equal(newTimestamp).send().wait();
// docs:end:warp
});
});
Expand Down
1 change: 0 additions & 1 deletion yarn-project/noir-contracts/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ members = [
"src/contracts/lending_contract",
"src/contracts/parent_contract",
"src/contracts/pending_commitments_contract",
"src/contracts/pokeable_token_contract",
"src/contracts/price_feed_contract",
"src/contracts/schnorr_account_contract",
"src/contracts/schnorr_hardcoded_account_contract",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract ImportTest {
target: Field
) -> Field {
let test_contract_instance = TestPrivateContextInterface::at(target);
let return_values = test_contract_instance.testCodeGen(
let return_values = test_contract_instance.test_code_gen(
&mut context,
1,
true,
Expand Down Expand Up @@ -56,7 +56,7 @@ contract ImportTest {
target: Field
) -> Field {
let test_contract_instance = TestPrivateContextInterface::at(target);
let return_values = test_contract_instance.getThisAddress(&mut context);
let return_values = test_contract_instance.get_this_address(&mut context);

return_values[0]
}
Expand All @@ -69,7 +69,7 @@ contract ImportTest {
target: Field,
) {
let test_contract_instance = TestPrivateContextInterface::at(target);
test_contract_instance.createNullifierPublic(&mut context, 1, 2);
test_contract_instance.create_nullifier_public(&mut context, 1, 2);
}

// Calls the createNullifierPublic on the Test contract at the target address
Expand All @@ -80,7 +80,7 @@ contract ImportTest {
target: Field,
) -> Field {
let test_contract_instance = TestPublicContextInterface::at(target);
let ret = test_contract_instance.createNullifierPublic(context, 1, 2);
let ret = test_contract_instance.create_nullifier_public(context, 1, 2);

ret[0]
}
Expand Down

This file was deleted.

Loading