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: deprecate assert_contains_and_remove #2594

Merged
merged 9 commits into from
Oct 2, 2023
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Update tests to use add_note.
LeilaWang committed Sep 30, 2023
commit 90b9185ab4adc164adc43c5434e42ae78f9ded2b
1 change: 1 addition & 0 deletions yarn-project/aztec.js/src/index.ts
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ export {
FunctionCall,
L2BlockL2Logs,
NodeInfo,
NotePreimage,
PackedArguments,
PublicKey,
GrumpkinPrivateKey,
13 changes: 10 additions & 3 deletions yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AztecAddress, Fr, GrumpkinPrivateKey, PartialAddress, Point } from '@aztec/circuits.js';
import { AztecAddress, Fr, GrumpkinPrivateKey, PartialAddress } from '@aztec/circuits.js';
import {
AuthWitness,
ContractData,
@@ -74,8 +74,15 @@ export abstract class BaseWallet implements Wallet {
getPublicStorageAt(contract: AztecAddress, storageSlot: Fr): Promise<any> {
return this.pxe.getPublicStorageAt(contract, storageSlot);
}
addNote(contract: AztecAddress, storageSlot: Fr, preimage: NotePreimage, nonce: Fr, account: Point): Promise<void> {
return this.pxe.addNote(contract, storageSlot, preimage, nonce, account);
addNote(
account: AztecAddress,
contract: AztecAddress,
storageSlot: Fr,
preimage: NotePreimage,
txHash: TxHash,
nonce?: Fr,
): Promise<void> {
return this.pxe.addNote(account, contract, storageSlot, preimage, txHash, nonce);
}
getNoteNonces(contract: AztecAddress, storageSlot: Fr, preimage: NotePreimage, txHash: TxHash): Promise<Fr[]> {
return this.pxe.getNoteNonces(contract, storageSlot, preimage, txHash);
2 changes: 1 addition & 1 deletion yarn-project/canary/package.json
Original file line number Diff line number Diff line change
@@ -21,8 +21,8 @@
"rootDir": "./src"
},
"dependencies": {
"@aztec/circuits.js": "workspace:^",
"@aztec/aztec.js": "workspace:^",
"@aztec/circuits.js": "workspace:^",
"@aztec/cli": "workspace:^",
"@aztec/end-to-end": "workspace:^",
"@aztec/l1-artifacts": "workspace:^",
32 changes: 28 additions & 4 deletions yarn-project/canary/src/uniswap_trade_on_l1_from_l2.test.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ import {
AztecAddress,
EthAddress,
Fr,
NotePreimage,
TxHash,
TxStatus,
computeMessageSecretHash,
createDebugLogger,
@@ -194,14 +196,22 @@ const consumeMessageOnAztecAndMintSecretly = async (
.send();
const consumptionReceipt = await consumptionTx.wait();
expect(consumptionReceipt.status).toBe(TxStatus.MINED);
return consumptionReceipt.txHash;
};

const redeemShieldPrivatelyOnL2 = async (
l2Contract: TokenContract,
to: AztecAddress,
shieldAmount: bigint,
secret: Fr,
secretHash: Fr,
txHash: TxHash,
) => {
// Add the note to the pxe.
const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(shieldAmount), secretHash]);
await pxe.addNote(to, l2Contract.address, storageSlot, preimage, txHash);

logger('Spending commitment in private call');
const privateTx = l2Contract.methods.redeem_shield(to, shieldAmount, secret).send();
const privateReceipt = await privateTx.wait();
@@ -300,15 +310,22 @@ describe('uniswap_trade_on_l1_from_l2', () => {

// 3. Claim WETH on L2
logger('Minting weth on L2');
await consumeMessageOnAztecAndMintSecretly(
const redeemingWethTxHash = await consumeMessageOnAztecAndMintSecretly(
wethL2Bridge,
wethAmountToBridge,
secretHashForRedeemingWeth,
ownerEthAddress,
messageKey,
secretForMintingWeth,
);
await redeemShieldPrivatelyOnL2(wethL2Contract, ownerAddress, wethAmountToBridge, secretForRedeemingWeth);
await redeemShieldPrivatelyOnL2(
wethL2Contract,
ownerAddress,
wethAmountToBridge,
secretForRedeemingWeth,
secretHashForRedeemingWeth,
redeemingWethTxHash,
);
await expectPrivateBalanceOnL2(ownerAddress, wethAmountToBridge + BigInt(ownerInitialBalance), wethL2Contract);

// Store balances
@@ -396,15 +413,22 @@ describe('uniswap_trade_on_l1_from_l2', () => {

// 7. claim dai on L2
logger('Consuming messages to mint dai on L2');
await consumeMessageOnAztecAndMintSecretly(
const redeemingDaiTxHash = await consumeMessageOnAztecAndMintSecretly(
daiL2Bridge,
daiAmountToBridge,
secretHashForRedeemingDai,
ownerEthAddress,
depositDaiMessageKey,
secretForDepositingSwappedDai,
);
await redeemShieldPrivatelyOnL2(daiL2Contract, ownerAddress, daiAmountToBridge, secretForRedeemingDai);
await redeemShieldPrivatelyOnL2(
daiL2Contract,
ownerAddress,
daiAmountToBridge,
secretForRedeemingDai,
secretHashForRedeemingDai,
redeemingDaiTxHash,
);
await expectPrivateBalanceOnL2(ownerAddress, daiL2BalanceBeforeSwap + daiAmountToBridge, daiL2Contract);

const wethL2BalanceAfterSwap = await getL2PrivateBalanceOf(ownerAddress, wethL2Contract);
13 changes: 10 additions & 3 deletions yarn-project/end-to-end/src/canary/browser.ts
Original file line number Diff line number Diff line change
@@ -162,6 +162,7 @@ export const browserTestSuite = (setup: () => Server, pageLogger: AztecJs.DebugL
getUnsafeSchnorrAccount,
Contract,
Fr,
NotePreimage,
computeMessageSecretHash,
getSandboxAccountsWallets,
} = window.AztecJs;
@@ -174,17 +175,23 @@ export const browserTestSuite = (setup: () => Server, pageLogger: AztecJs.DebugL
accounts = await pxe.getRegisteredAccounts();
}
const [owner] = await getSandboxAccountsWallets(pxe);
const ownerAddress = owner.getAddress();
const tx = new DeployMethod(accounts[0].publicKey, pxe, TokenContractAbi).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(owner.getAddress()).send().wait();
await token.methods._initialize(ownerAddress).send().wait();
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);
await token.methods.mint_private(initialBalance, secretHash).send().wait();
await token.methods.redeem_shield(owner.getAddress(), initialBalance, secret).send().wait();
const mintPrivateReceipt = await token.methods.mint_private(initialBalance, secretHash).send().wait();

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(initialBalance), secretHash]);
await pxe.addNote(ownerAddress, token.address, storageSlot, preimage, mintPrivateReceipt.txHash);

await token.methods.redeem_shield(ownerAddress, initialBalance, secret).send().wait();

return receipt.txHash.toString();
},
22 changes: 14 additions & 8 deletions yarn-project/end-to-end/src/e2e_2_pxes.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AztecNodeService } from '@aztec/aztec-node';
import { AztecAddress, Wallet, computeMessageSecretHash } from '@aztec/aztec.js';
import { AztecAddress, NotePreimage, Wallet, computeMessageSecretHash } from '@aztec/aztec.js';
import { DebugLogger } from '@aztec/foundation/log';
import { retryUntil } from '@aztec/foundation/retry';
import { toBigInt } from '@aztec/foundation/serialize';
@@ -83,25 +83,31 @@ describe('e2e_2_pxes', () => {
expect(balance).toBe(expectedBalance);
};

const deployTokenContract = async (initialAdminBalance: bigint, admin: AztecAddress) => {
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);

if (initialAdminBalance > 0n) {
await mintTokens(contract, admin, initialAdminBalance);
await mintTokens(contract, admin, initialAdminBalance, pxe);
}

logger('L2 contract deployed');

return contract.completeAddress;
};

const mintTokens = async (contract: TokenContract, recipient: AztecAddress, balance: bigint) => {
const mintTokens = async (contract: TokenContract, recipient: AztecAddress, balance: bigint, pxe: PXE) => {
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);

expect((await contract.methods.mint_private(balance, secretHash).send().wait()).status).toEqual(TxStatus.MINED);
const receipt = await contract.methods.mint_private(balance, secretHash).send().wait();
expect(receipt.status).toEqual(TxStatus.MINED);

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(balance), secretHash]);
await pxe.addNote(recipient, contract.address, storageSlot, preimage, receipt.txHash);

expect((await contract.methods.redeem_shield(recipient, balance, secret).send().wait()).status).toEqual(
TxStatus.MINED,
);
@@ -112,7 +118,7 @@ describe('e2e_2_pxes', () => {
const transferAmount1 = 654n;
const transferAmount2 = 323n;

const completeTokenAddress = await deployTokenContract(initialBalance, userA.address);
const completeTokenAddress = await deployTokenContract(initialBalance, userA.address, pxeA);
const tokenAddress = completeTokenAddress.address;

// Add account B to wallet A
@@ -207,7 +213,7 @@ describe('e2e_2_pxes', () => {
const userABalance = 100n;
const userBBalance = 150n;

const completeTokenAddress = await deployTokenContract(userABalance, userA.address);
const completeTokenAddress = await deployTokenContract(userABalance, userA.address, pxeA);
const contractWithWalletA = await TokenContract.at(completeTokenAddress.address, walletA);

// Add account B to wallet A
@@ -225,7 +231,7 @@ describe('e2e_2_pxes', () => {
]);

// Mint tokens to user B
await mintTokens(contractWithWalletA, userB.address, userBBalance);
await mintTokens(contractWithWalletA, userB.address, userBBalance, pxeA);

// Check that user A balance is 100 on server A
await expectTokenBalance(walletA, completeTokenAddress.address, userA.address, userABalance);
34 changes: 27 additions & 7 deletions yarn-project/end-to-end/src/e2e_escrow_contract.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { AccountWallet, AztecAddress, BatchCall, computeMessageSecretHash, generatePublicKey } from '@aztec/aztec.js';
import {
AccountWallet,
AztecAddress,
BatchCall,
NotePreimage,
computeMessageSecretHash,
generatePublicKey,
} from '@aztec/aztec.js';
import { CompleteAddress, Fr, GrumpkinPrivateKey, GrumpkinScalar, getContractDeploymentInfo } from '@aztec/circuits.js';
import { DebugLogger } from '@aztec/foundation/log';
import { EscrowContractAbi } from '@aztec/noir-contracts/artifacts';
@@ -8,6 +15,7 @@ import { PXE, PublicKey, TxStatus } from '@aztec/types';
import { setup } from './fixtures/utils.js';

describe('e2e_escrow_contract', () => {
const pendingShieldsStorageSlot = new Fr(5);
let pxe: PXE;
let wallet: AccountWallet;
let recipientWallet: AccountWallet;
@@ -53,13 +61,19 @@ describe('e2e_escrow_contract', () => {

expect((await token.methods._initialize(owner).send().wait()).status).toBe(TxStatus.MINED);

const mintAmount = 100n;
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);

expect((await token.methods.mint_private(100n, secretHash).send().wait()).status).toEqual(TxStatus.MINED);
expect((await token.methods.redeem_shield(escrowContract.address, 100n, secret).send().wait()).status).toEqual(
TxStatus.MINED,
);
const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait();
expect(receipt.status).toEqual(TxStatus.MINED);

const preimage = new NotePreimage([new Fr(mintAmount), secretHash]);
await pxe.addNote(escrowContract.address, token.address, pendingShieldsStorageSlot, preimage, receipt.txHash);

expect(
(await token.methods.redeem_shield(escrowContract.address, mintAmount, secret).send().wait()).status,
).toEqual(TxStatus.MINED);

logger(`Token contract deployed at ${token.address}`);
}, 100_000);
@@ -93,11 +107,17 @@ describe('e2e_escrow_contract', () => {

it('moves funds using multiple keys on the same tx (#1010)', async () => {
logger(`Minting funds in token contract to ${owner}`);
const mintAmount = 50n;
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);

expect((await token.methods.mint_private(50n, secretHash).send().wait()).status).toEqual(TxStatus.MINED);
expect((await token.methods.redeem_shield(owner, 50n, secret).send().wait()).status).toEqual(TxStatus.MINED);
const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait();
expect(receipt.status).toEqual(TxStatus.MINED);

const preimage = new NotePreimage([new Fr(mintAmount), secretHash]);
await pxe.addNote(owner, token.address, pendingShieldsStorageSlot, preimage, receipt.txHash);

expect((await token.methods.redeem_shield(owner, mintAmount, secret).send().wait()).status).toEqual(TxStatus.MINED);

await expectBalance(owner, 50n);

9 changes: 7 additions & 2 deletions yarn-project/end-to-end/src/e2e_lending_contract.test.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { CircuitsWasm, CompleteAddress, FunctionSelector, GeneratorIndex } from
import { pedersenPlookupCompressWithHashIndex } from '@aztec/circuits.js/barretenberg';
import { DebugLogger } from '@aztec/foundation/log';
import { LendingContract, PriceFeedContract, TokenContract } from '@aztec/noir-contracts/types';
import { TxStatus } from '@aztec/types';
import { NotePreimage, TxStatus } from '@aztec/types';

import { jest } from '@jest/globals';

@@ -121,8 +121,13 @@ describe('e2e_lending_contract', () => {

const a = asset.methods.mint_public(lendingAccount.address, mintAmount).send();
const b = asset.methods.mint_private(mintAmount, secretHash).send();

await Promise.all([a, b].map(waitForSuccess));

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(mintAmount), secretHash]);
const txHash = await b.getTxHash();
await wallet.addNote(accounts[0].address, asset.address, storageSlot, preimage, txHash);

await waitForSuccess(asset.methods.redeem_shield(lendingAccount.address, mintAmount, secret).send());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { AztecNodeService } from '@aztec/aztec-node';
import { AztecAddress, Wallet, computeMessageSecretHash, generatePublicKey, getSchnorrAccount } from '@aztec/aztec.js';
import {
AztecAddress,
NotePreimage,
Wallet,
computeMessageSecretHash,
generatePublicKey,
getSchnorrAccount,
} from '@aztec/aztec.js';
import { Fr, GrumpkinScalar } from '@aztec/circuits.js';
import { DebugLogger } from '@aztec/foundation/log';
import { TokenContract } from '@aztec/noir-contracts/types';
@@ -52,7 +59,13 @@ describe('e2e_multiple_accounts_1_enc_key', () => {
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);

expect((await token.methods.mint_private(initialBalance, secretHash).send().wait()).status).toEqual(TxStatus.MINED);
const receipt = await token.methods.mint_private(initialBalance, secretHash).send().wait();
expect(receipt.status).toEqual(TxStatus.MINED);

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(initialBalance), secretHash]);
await pxe.addNote(accounts[0], token.address, storageSlot, preimage, receipt.txHash);

expect((await token.methods.redeem_shield(accounts[0], initialBalance, secret).send().wait()).status).toEqual(
TxStatus.MINED,
);
7 changes: 3 additions & 4 deletions yarn-project/end-to-end/src/e2e_private_airdrop.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { CompleteAddress, TxHash, Wallet } from '@aztec/aztec.js';
import { CompleteAddress, NotePreimage, TxHash, Wallet } from '@aztec/aztec.js';
import { Fr, MAX_NEW_COMMITMENTS_PER_CALL } from '@aztec/circuits.js';
import { DebugLogger } from '@aztec/foundation/log';
import { PrivateTokenAirdropContract } from '@aztec/noir-contracts/types';
import { NotePreimage } from '@aztec/types';

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

@@ -62,6 +61,7 @@ describe('private airdrop', () => {

const claimToken = async (accountIndex: number, claim: Claim, txHash: TxHash, nonceIndex = 0) => {
const contract = contracts[accountIndex];
const account = accounts[accountIndex].address;
const wallet = wallets[accountIndex];
const nonces = await wallet.getNoteNonces(contract.address, claimsStorageSlot, claim.preimage, txHash);

@@ -71,8 +71,7 @@ describe('private airdrop', () => {
expect(nonces[nonceIndex]).not.toEqual(Fr.ZERO);

const nonce = nonces[nonceIndex];
const { publicKey } = wallet.getCompleteAddress();
await wallet.addNote(contract.address, claimsStorageSlot, claim.preimage, nonce, publicKey);
await wallet.addNote(account, contract.address, claimsStorageSlot, claim.preimage, txHash, nonce);

return contract.methods.claim(claim.amount, claim.secret).send().wait();
};
14 changes: 12 additions & 2 deletions yarn-project/end-to-end/src/e2e_sandbox_example.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// docs:start:imports
import {
Fr,
NotePreimage,
PXE,
computeMessageSecretHash,
createDebugLogger,
@@ -69,7 +70,12 @@ describe('e2e_sandbox_example', () => {
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);

await tokenContractAlice.methods.mint_private(initialSupply, secretHash).send().wait();
const receipt = await tokenContractAlice.methods.mint_private(initialSupply, secretHash).send().wait();

const pendingShieldsStorageSlot = new Fr(5); // The storage slot of `pending_shields` is 5.
const preimage = new NotePreimage([new Fr(initialSupply), secretHash]);
await pxe.addNote(alice, contract.address, pendingShieldsStorageSlot, preimage, receipt.txHash);

await tokenContractAlice.methods.redeem_shield(alice, initialSupply, secret).send().wait();
// docs:end:Deployment

@@ -120,7 +126,11 @@ describe('e2e_sandbox_example', () => {
// Now mint some further funds for Bob
const mintQuantity = 10_000n;
logger(`Minting ${mintQuantity} tokens to Bob...`);
await tokenContractBob.methods.mint_private(mintQuantity, secretHash).send().wait();
const mintPrivateReceipt = await tokenContractBob.methods.mint_private(mintQuantity, secretHash).send().wait();

const bobPendingShield = new NotePreimage([new Fr(mintQuantity), secretHash]);
await pxe.addNote(bob, contract.address, pendingShieldsStorageSlot, bobPendingShield, mintPrivateReceipt.txHash);

await tokenContractBob.methods.redeem_shield(bob, mintQuantity, secret).send().wait();

// Check the new balances
36 changes: 18 additions & 18 deletions yarn-project/end-to-end/src/e2e_token_contract.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { AccountWallet, computeMessageSecretHash } from '@aztec/aztec.js';
import { AccountWallet, NotePreimage, TxHash, TxStatus, computeMessageSecretHash } from '@aztec/aztec.js';
import { CircuitsWasm, CompleteAddress, Fr, FunctionSelector, GeneratorIndex } from '@aztec/circuits.js';
import { pedersenPlookupCompressWithHashIndex } from '@aztec/circuits.js/barretenberg';
import { DebugLogger } from '@aztec/foundation/log';
import { TokenContract } from '@aztec/noir-contracts/types';
import { TxStatus } from '@aztec/types';

import { jest } from '@jest/globals';

@@ -32,6 +31,12 @@ describe('e2e_token_contract', () => {

let tokenSim: TokenSimulator;

const addPendingShieldNoteToPXE = async (accountIndex: number, amount: bigint, secretHash: Fr, txHash: TxHash) => {
const storageSlot = new Fr(5); // The storage slot of `pending_shields` is 5.
const preimage = new NotePreimage([new Fr(amount), secretHash]);
await wallets[accountIndex].addNote(accounts[0].address, asset.address, storageSlot, preimage, txHash);
};

beforeAll(async () => {
({ teardown, logger, wallets, accounts } = await setup(3));

@@ -148,6 +153,7 @@ describe('e2e_token_contract', () => {
const secret = Fr.random();
const amount = 10000n;
let secretHash: Fr;
let txHash: TxHash;

beforeAll(async () => {
secretHash = await computeMessageSecretHash(secret);
@@ -159,9 +165,11 @@ describe('e2e_token_contract', () => {
const receipt = await tx.wait();
expect(receipt.status).toBe(TxStatus.MINED);
tokenSim.mintPrivate(amount);
txHash = receipt.txHash;
});

it('redeem as recipient', async () => {
await addPendingShieldNoteToPXE(0, amount, secretHash, txHash);
const txClaim = asset.methods.redeem_shield(accounts[0].address, amount, secret).send();
const receiptClaim = await txClaim.wait();
expect(receiptClaim.status).toBe(TxStatus.MINED);
@@ -171,10 +179,12 @@ describe('e2e_token_contract', () => {

describe('failure cases', () => {
it('try to redeem as recipient (double-spend) [REVERTS]', async () => {
const txClaim = asset.methods.redeem_shield(accounts[0].address, amount, secret).send();
await txClaim.isMined();
const receipt = await txClaim.getReceipt();
expect(receipt.status).toBe(TxStatus.DROPPED);
await expect(addPendingShieldNoteToPXE(0, amount, secretHash, txHash)).rejects.toThrowError(
'The note has been destroyed.',
);
await expect(
asset.methods.redeem_shield(accounts[0].address, amount, secret).simulate(),
).rejects.toThrowError('Can only remove a note that has been read from the set.');
});

it('mint_private as non-minter', async () => {
@@ -596,17 +606,12 @@ describe('e2e_token_contract', () => {
await tokenSim.check();

// Redeem it
await addPendingShieldNoteToPXE(0, amount, secretHash, receipt.txHash);
const txClaim = asset.methods.redeem_shield(accounts[0].address, amount, secret).send();
const receiptClaim = await txClaim.wait();
expect(receiptClaim.status).toBe(TxStatus.MINED);

tokenSim.redeemShield(accounts[0].address, amount);

// Check that claiming again will hit a double-spend and fail due to pending note already consumed.
const txClaimDoubleSpend = asset.methods.redeem_shield(accounts[0].address, amount, secret).send();
await txClaimDoubleSpend.isMined();
const receiptDoubleSpend = await txClaimDoubleSpend.getReceipt();
expect(receiptDoubleSpend.status).toBe(TxStatus.DROPPED);
});

it('on behalf of other', async () => {
@@ -636,17 +641,12 @@ describe('e2e_token_contract', () => {
expect(receiptReplay.status).toBe(TxStatus.DROPPED);

// Redeem it
await addPendingShieldNoteToPXE(0, amount, secretHash, receipt.txHash);
const txClaim = asset.methods.redeem_shield(accounts[0].address, amount, secret).send();
const receiptClaim = await txClaim.wait();
expect(receiptClaim.status).toBe(TxStatus.MINED);

tokenSim.redeemShield(accounts[0].address, amount);

// Check that claiming again will hit a double-spend and fail due to pending note already consumed.
const txClaimDoubleSpend = asset.methods.redeem_shield(accounts[0].address, amount, secret).send();
await txClaimDoubleSpend.isMined();
const receiptDoubleSpend = await txClaimDoubleSpend.getReceipt();
expect(receiptDoubleSpend.status).toBe(TxStatus.DROPPED);
});

describe('failure cases', () => {
Original file line number Diff line number Diff line change
@@ -325,13 +325,7 @@ export class CrossChainTestHarness {
this.logger('Adding note to PXE');
const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(shieldAmount), secretHash]);
const [nonce] = await this.pxeService.getNoteNonces(
this.l2Token.address,
storageSlot,
preimage,
shieldReceipt.txHash,
);
await this.pxeService.addNote(this.l2Token.address, storageSlot, preimage, nonce, this.ownerPub);
await this.pxeService.addNote(this.ownerAddress, this.l2Token.address, storageSlot, preimage, shieldReceipt.txHash);
}

async redeemShieldPrivatelyOnL2(shieldAmount: bigint, secret: Fr) {
60 changes: 46 additions & 14 deletions yarn-project/end-to-end/src/guides/dapp_testing.test.ts
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import {
CheatCodes,
Fr,
L2BlockL2Logs,
NotePreimage,
PXE,
computeMessageSecretHash,
createAccount,
@@ -40,12 +41,20 @@ describe('guides/dapp/testing', () => {
// docs:end:stop-in-proc-sandbox

it('increases recipient funds on mint', async () => {
expect(await token.methods.balance_of_private(recipient.getAddress()).view()).toEqual(0n);
const recipientAddress = recipient.getAddress();
expect(await token.methods.balance_of_private(recipientAddress).view()).toEqual(0n);

const mintAmount = 20n;
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);
await token.methods.mint_private(20n, secretHash).send().wait();
await token.methods.redeem_shield(recipient.getAddress(), 20n, secret).send().wait();
expect(await token.methods.balance_of_private(recipient.getAddress()).view()).toEqual(20n);
const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait();

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(mintAmount), secretHash]);
await pxe.addNote(recipientAddress, token.address, storageSlot, preimage, receipt.txHash);

await token.methods.redeem_shield(recipientAddress, mintAmount, secret).send().wait();
expect(await token.methods.balance_of_private(recipientAddress).view()).toEqual(20n);
}, 30_000);
});
});
@@ -72,12 +81,20 @@ describe('guides/dapp/testing', () => {
}, 30_000);

it('increases recipient funds on mint', async () => {
expect(await token.methods.balance_of_private(recipient.getAddress()).view()).toEqual(0n);
const recipientAddress = recipient.getAddress();
expect(await token.methods.balance_of_private(recipientAddress).view()).toEqual(0n);

const mintAmount = 20n;
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);
await token.methods.mint_private(20n, secretHash).send().wait();
await token.methods.redeem_shield(recipient.getAddress(), 20n, secret).send().wait();
expect(await token.methods.balance_of_private(recipient.getAddress()).view()).toEqual(20n);
const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait();

const storageSlot = new Fr(5); // The storage slot of `pending_shields` is 5.
const preimage = new NotePreimage([new Fr(mintAmount), secretHash]);
await pxe.addNote(recipientAddress, token.address, storageSlot, preimage, receipt.txHash);

await token.methods.redeem_shield(recipientAddress, mintAmount, secret).send().wait();
expect(await token.methods.balance_of_private(recipientAddress).view()).toEqual(20n);
}, 30_000);
});
// docs:end:sandbox-example
@@ -99,11 +116,18 @@ describe('guides/dapp/testing', () => {

it('increases recipient funds on mint', async () => {
expect(await token.methods.balance_of_private(recipient.getAddress()).view()).toEqual(0n);
const recipientAddress = recipient.getAddress();
const mintAmount = 20n;
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);
await token.methods.mint_private(20n, secretHash).send().wait();
await token.methods.redeem_shield(recipient.getAddress(), 20n, secret).send().wait();
expect(await token.methods.balance_of_private(recipient.getAddress()).view()).toEqual(20n);
const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait();

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(mintAmount), secretHash]);
await pxe.addNote(recipientAddress, token.address, storageSlot, preimage, receipt.txHash);

await token.methods.redeem_shield(recipientAddress, mintAmount, secret).send().wait();
expect(await token.methods.balance_of_private(recipientAddress).view()).toEqual(20n);
}, 30_000);
});

@@ -145,15 +169,23 @@ describe('guides/dapp/testing', () => {
testContract = await TestContract.deploy(owner).send().deployed();
token = await TokenContract.deploy(owner).send().deployed();
await token.methods._initialize(owner.getAddress()).send().wait();

const ownerAddress = owner.getAddress();
const mintAmount = 100n;
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);
await token.methods.mint_private(100n, secretHash).send().wait();
await token.methods.redeem_shield(owner.getAddress(), 100n, secret).send().wait();
const receipt = await token.methods.mint_private(100n, secretHash).send().wait();

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(mintAmount), secretHash]);
await pxe.addNote(ownerAddress, token.address, storageSlot, preimage, receipt.txHash);

await token.methods.redeem_shield(ownerAddress, 100n, secret).send().wait();

// docs:start:calc-slot
cheats = await CheatCodes.create(ETHEREUM_HOST, pxe);
// The balances mapping is defined on storage slot 3 and is indexed by user address
ownerSlot = cheats.aztec.computeSlotInMap(3n, owner.getAddress());
ownerSlot = cheats.aztec.computeSlotInMap(3n, ownerAddress);
// docs:end:calc-slot
}, 60_000);

Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import {
BaseAccountContract,
CompleteAddress,
Fr,
NotePreimage,
computeMessageSecretHash,
} from '@aztec/aztec.js';
import { GrumpkinPrivateKey, GrumpkinScalar } from '@aztec/circuits.js';
@@ -67,8 +68,14 @@ describe('guides/writing_an_account_contract', () => {
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);

await token.methods.mint_private(50, secretHash).send().wait();
await token.methods.redeem_shield({ address }, 50, secret).send().wait();
const mintAmount = 50n;
const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait();

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(mintAmount), secretHash]);
await pxe.addNote(address, token.address, storageSlot, preimage, receipt.txHash);

await token.methods.redeem_shield({ address }, mintAmount, secret).send().wait();

const balance = await token.methods.balance_of_private({ address }).view();
logger(`Balance of wallet is now ${balance}`);
8 changes: 7 additions & 1 deletion yarn-project/end-to-end/src/sample-dapp/index.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Fr,
L2BlockL2Logs,
NotePreimage,
computeMessageSecretHash,
createPXEClient,
getSandboxAccountsWallets,
@@ -40,7 +41,12 @@ async function mintPrivateFunds(pxe) {
const mintAmount = 20n;
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);
await token.methods.mint_private(mintAmount, secretHash).send().wait();
const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait();

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(mintAmount), secretHash]);
await pxe.addNote(owner.getAddress(), token.address, storageSlot, preimage, receipt.txHash);

await token.methods.redeem_shield(owner.getAddress(), mintAmount, secret).send().wait();

await showPrivateBalances(pxe);
9 changes: 7 additions & 2 deletions yarn-project/end-to-end/src/sample-dapp/index.test.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createSandbox } from '@aztec/aztec-sandbox';
import { Contract, Fr, computeMessageSecretHash, createAccount } from '@aztec/aztec.js';
import { Contract, Fr, NotePreimage, computeMessageSecretHash, createAccount } from '@aztec/aztec.js';
import { TokenContractAbi } from '@aztec/noir-contracts/artifacts';

describe('token', () => {
@@ -16,7 +16,12 @@ describe('token', () => {
const initialBalance = 20n;
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);
await token.methods.mint_private(initialBalance, secretHash).send().wait();
const receipt = await token.methods.mint_private(initialBalance, secretHash).send().wait();

const storageSlot = new Fr(5);
const preimage = new NotePreimage([new Fr(initialBalance), secretHash]);
await pxe.addNote(owner.getAddress(), token.address, storageSlot, preimage, receipt.txHash);

await token.methods.redeem_shield({ address: owner.getAddress() }, initialBalance, secret).send().wait();
}, 120_000);

20 changes: 16 additions & 4 deletions yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
@@ -41,7 +41,6 @@ import {
NodeInfo,
NotePreimage,
PXE,
PublicKey,
SimulationError,
Tx,
TxExecutionRequest,
@@ -200,12 +199,25 @@ export class PXEService implements PXE {
}

public async addNote(
account: AztecAddress,
contractAddress: AztecAddress,
storageSlot: Fr,
preimage: NotePreimage,
nonce: Fr,
account: PublicKey,
txHash: TxHash,
nonce?: Fr,
) {
const { publicKey } = (await this.db.getCompleteAddress(account)) ?? {};
if (!publicKey) {
throw new Error('Unknown account.');
}

if (!nonce) {
[nonce] = await this.getNoteNonces(contractAddress, storageSlot, preimage, txHash);
}
if (!nonce) {
throw new Error(`Cannot find the note in tx: ${txHash}.`);
}

const { innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier } =
await this.simulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, preimage.items);

@@ -232,7 +244,7 @@ export class PXEService implements PXE {
innerNoteHash,
siloedNullifier,
index,
publicKey: account,
publicKey,
});
}

11 changes: 6 additions & 5 deletions yarn-project/types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@ import {
L2BlockL2Logs,
L2Tx,
NotePreimage,
PublicKey,
Tx,
TxExecutionRequest,
TxHash,
@@ -172,18 +171,20 @@ export interface PXE {

/**
* Adds a note to the database. Throw if the note hash of the note doesn't exist in the tree.
* @param account - The account the note is associated with.
* @param contract - The contract address of the note.
* @param storageSlot - The storage slot of the note.
* @param preimage - The note preimage.
* @param nonce - The nonce of the note.
* @param account - The public key of the account the note is associated with.
* @param txHash - The tx hash of the tx containing the note.
* @param nonce - The nonce of the note. If undefined, will look for the first index that matches the preimage.
*/
addNote(
account: AztecAddress,
contract: AztecAddress,
storageSlot: Fr,
preimage: NotePreimage,
nonce: Fr,
account: PublicKey,
txHash: TxHash,
nonce?: Fr,
): Promise<void>;

/**