Skip to content

Commit

Permalink
feat: use scopes in wallet calls (#7749)
Browse files Browse the repository at this point in the history
This PR introduces an initial implementation for adding scopes to calls
via a wallet. Note that this only affects wallets that are associated
with an "account", and does not yet affect signerless wallets.
Signerless wallets that are not associated with any account still has
access to all notes.
  • Loading branch information
sklppy88 authored Aug 6, 2024
1 parent c263c4e commit d04183c
Show file tree
Hide file tree
Showing 27 changed files with 134 additions and 28 deletions.
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/wallet/account_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { BaseWallet } from './base_wallet.js';
*/
export class AccountWallet extends BaseWallet {
constructor(pxe: PXE, protected account: AccountInterface) {
super(pxe);
super(pxe, [account.getAddress()]);
}

createTxExecutionRequest(exec: ExecutionRequestInit): Promise<TxExecutionRequest> {
Expand Down
12 changes: 8 additions & 4 deletions yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { type IntentAction, type IntentInnerHash } from '../utils/authwit.js';
* A base class for Wallet implementations
*/
export abstract class BaseWallet implements Wallet {
constructor(protected readonly pxe: PXE) {}
constructor(protected readonly pxe: PXE, private scopes?: AztecAddress[]) {}

abstract getCompleteAddress(): CompleteAddress;

Expand All @@ -53,6 +53,10 @@ export abstract class BaseWallet implements Wallet {

abstract rotateNullifierKeys(newNskM: Fq): Promise<void>;

setScopes(scopes: AztecAddress[]) {
this.scopes = scopes;
}

getAddress() {
return this.getCompleteAddress().address;
}
Expand Down Expand Up @@ -102,10 +106,10 @@ export abstract class BaseWallet implements Wallet {
return this.pxe.getContracts();
}
proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean): Promise<Tx> {
return this.pxe.proveTx(txRequest, simulatePublic);
return this.pxe.proveTx(txRequest, simulatePublic, this.scopes);
}
simulateTx(txRequest: TxExecutionRequest, simulatePublic: boolean, msgSender?: AztecAddress): Promise<SimulatedTx> {
return this.pxe.simulateTx(txRequest, simulatePublic, msgSender);
return this.pxe.simulateTx(txRequest, simulatePublic, msgSender, this.scopes);
}
sendTx(tx: Tx): Promise<TxHash> {
return this.pxe.sendTx(tx);
Expand All @@ -130,7 +134,7 @@ export abstract class BaseWallet implements Wallet {
return this.pxe.getPublicStorageAt(contract, storageSlot);
}
addNote(note: ExtendedNote): Promise<void> {
return this.pxe.addNote(note);
return this.pxe.addNote(note, this.getAddress());
}
addNullifiedNote(note: ExtendedNote): Promise<void> {
return this.pxe.addNullifiedNote(note);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('e2e_sandbox_example', () => {

// Add the newly created "pending shield" note to PXE
const note = new Note([new Fr(initialSupply), aliceSecretHash]);
await pxe.addNote(
await aliceWallet.addNote(
new ExtendedNote(
note,
alice,
Expand Down Expand Up @@ -152,7 +152,7 @@ describe('e2e_sandbox_example', () => {
const mintPrivateReceipt = await tokenContractBob.methods.mint_private(mintQuantity, bobSecretHash).send().wait();

const bobPendingShield = new Note([new Fr(mintQuantity), bobSecretHash]);
await pxe.addNote(
await bobWallet.addNote(
new ExtendedNote(
bobPendingShield,
bob,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_2_pxes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ describe('e2e_2_pxes', () => {
TokenContract.notes.TransparentNote.id,
receipt.txHash,
);
await recipientPxe.addNote(extendedNote);
await recipientPxe.addNote(extendedNote, recipient);

await contractAsRecipient.methods.redeem_shield(recipient, balance, secret).send().wait();
};
Expand Down
6 changes: 6 additions & 0 deletions yarn-project/end-to-end/src/e2e_authwit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,18 @@ describe('e2e_authwit_tests', () => {
isValidInPublic: false,
});

// We give wallets[0] access to wallets[1]'s notes.
wallets[0].setScopes([wallets[0].getAddress(), wallets[1].getAddress()]);

// Check that the authwit is NOT valid in private for wallets[1]
expect(await wallets[0].lookupValidity(wallets[1].getAddress(), intent)).toEqual({
isValidInPrivate: false,
isValidInPublic: false,
});

// We give wallets[1] access to wallets[0]'s notes.
wallets[1].setScopes([wallets[0].getAddress(), wallets[1].getAddress()]);

// Consume the inner hash using the wallets[0] as the "on behalf of".
await auth.withWallet(wallets[1]).methods.consume(wallets[0].getAddress(), innerHash).send().wait();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ describe('e2e_blacklist_token_contract burn', () => {
const witness = await wallets[0].createAuthWit({ caller: wallets[1].getAddress(), action });
await wallets[1].addAuthWitness(witness);

// We give wallets[1] access to wallets[0]'s notes to be able to burn the notes.
wallets[1].setScopes([wallets[1].getAddress(), wallets[0].getAddress()]);

await asset.withWallet(wallets[1]).methods.burn(wallets[0].getAddress(), amount, nonce).send().wait();
tokenSim.burnPrivate(wallets[0].getAddress(), amount);

Expand Down Expand Up @@ -198,6 +201,9 @@ describe('e2e_blacklist_token_contract burn', () => {
{ chainId: wallets[0].getChainId(), version: wallets[0].getVersion() },
);

// We give wallets[1] access to wallets[0]'s notes to test the authwit.
wallets[1].setScopes([wallets[1].getAddress(), wallets[0].getAddress()]);

await expect(action.prove()).rejects.toThrow(`Unknown auth witness for message hash ${messageHash.toString()}`);
});

Expand All @@ -217,6 +223,9 @@ describe('e2e_blacklist_token_contract burn', () => {
const witness = await wallets[0].createAuthWit({ caller: wallets[1].getAddress(), action });
await wallets[2].addAuthWitness(witness);

// We give wallets[2] access to wallets[0]'s notes to test the authwit.
wallets[2].setScopes([wallets[2].getAddress(), wallets[0].getAddress()]);

await expect(action.prove()).rejects.toThrow(
`Unknown auth witness for message hash ${expectedMessageHash.toString()}`,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ describe('e2e_blacklist_token_contract transfer private', () => {
expect(amount).toBeGreaterThan(0n);
await asset.methods.transfer(wallets[0].getAddress(), wallets[1].getAddress(), amount, 0).send().wait();
tokenSim.transferPrivate(wallets[0].getAddress(), wallets[1].getAddress(), amount);

// We give wallets[0] access to wallets[1]'s notes to be able to check balances after the test.
wallets[0].setScopes([wallets[0].getAddress(), wallets[1].getAddress()]);
});

it('transfer to self', async () => {
Expand Down Expand Up @@ -62,6 +65,9 @@ describe('e2e_blacklist_token_contract transfer private', () => {
// docs:end:add_authwit
// docs:end:authwit_transfer_example

// We give wallets[1] access to wallets[0]'s notes to be able to transfer the notes.
wallets[1].setScopes([wallets[1].getAddress(), wallets[0].getAddress()]);

// Perform the transfer
await action.send().wait();
tokenSim.transferPrivate(wallets[0].getAddress(), wallets[1].getAddress(), amount);
Expand All @@ -72,6 +78,9 @@ describe('e2e_blacklist_token_contract transfer private', () => {
.methods.transfer(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce)
.send();
await expect(txReplay.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR);

// We give wallets[0] access to wallets[1]'s notes to be able to check balances after the test.
wallets[0].setScopes([wallets[0].getAddress(), wallets[1].getAddress()]);
});

describe('failure cases', () => {
Expand Down Expand Up @@ -161,6 +170,9 @@ describe('e2e_blacklist_token_contract transfer private', () => {
const witness = await wallets[0].createAuthWit({ caller: wallets[1].getAddress(), action });
await wallets[2].addAuthWitness(witness);

// We give wallets[2] access to wallets[0]'s notes to test the authwit.
wallets[2].setScopes([wallets[2].getAddress(), wallets[0].getAddress()]);

await expect(action.prove()).rejects.toThrow(
`Unknown auth witness for message hash ${expectedMessageHash.toString()}`,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ describe('e2e_blacklist_token_contract unshielding', () => {
const witness = await wallets[0].createAuthWit({ caller: wallets[1].getAddress(), action });
await wallets[1].addAuthWitness(witness);

// We give wallets[1] access to wallets[0]'s notes to unshield the note.
wallets[1].setScopes([wallets[1].getAddress(), wallets[0].getAddress()]);

await action.send().wait();
tokenSim.unshield(wallets[0].getAddress(), wallets[1].getAddress(), amount);

Expand Down Expand Up @@ -122,6 +125,9 @@ describe('e2e_blacklist_token_contract unshielding', () => {
const witness = await wallets[0].createAuthWit({ caller: wallets[1].getAddress(), action });
await wallets[2].addAuthWitness(witness);

// We give wallets[2] access to wallets[0]'s notes to test the authwit.
wallets[2].setScopes([wallets[2].getAddress(), wallets[0].getAddress()]);

await expect(action.prove()).rejects.toThrow(
`Unknown auth witness for message hash ${expectedMessageHash.toString()}`,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ describe('e2e_crowdfunding_and_claim', () => {

// 3) We claim the reward token via the Claim contract
{
// We allow the donor wallet to use the crowdfunding contract's notes
donorWallets[0].setScopes([donorWallets[0].getAddress(), crowdfundingContract.address]);

await claimContract
.withWallet(donorWallets[0])
.methods.claim(valueNote, donorWallets[0].getAddress())
Expand All @@ -254,6 +257,8 @@ describe('e2e_crowdfunding_and_claim', () => {
.simulate();
expect(balanceDNTBeforeWithdrawal).toEqual(0n);

// We allow the operator wallet to use the crowdfunding contract's notes
operatorWallet.setScopes([operatorWallet.getAddress(), crowdfundingContract.address]);
// 4) At last, we withdraw the raised funds from the crowdfunding contract to the operator's address
await crowdfundingContract.methods.withdraw(donationAmount).send().wait();

Expand Down Expand Up @@ -399,6 +404,10 @@ describe('e2e_crowdfunding_and_claim', () => {
// a non-entrypoint function (withdraw never calls context.end_setup()), meaning the min revertible counter will remain 0.
// This does not protect fully against impersonation as the contract could just call context.end_setup() and the below would pass.
// => the private_init msg_sender assertion is required (#7190, #7404)

// We allow the donor wallet to use the crowdfunding contract's notes
donorWallets[1].setScopes([donorWallets[1].getAddress(), crowdfundingContract.address]);

await expect(donorWallets[1].simulateTx(request, true, operatorWallet.getAddress())).rejects.toThrow(
'Assertion failed: Users cannot set msg_sender in first call',
);
Expand Down
7 changes: 5 additions & 2 deletions yarn-project/end-to-end/src/e2e_escrow_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,13 @@ describe('e2e_escrow_contract', () => {
TokenContract.notes.TransparentNote.id,
receipt.txHash,
);
await pxe.addNote(extendedNote);
await wallet.addNote(extendedNote);

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

// We allow our wallet to see the escrow contract's notes.
wallet.setScopes([wallet.getAddress(), escrowContract.address]);

logger.info(`Token contract deployed at ${token.address}`);
});

Expand Down Expand Up @@ -123,7 +126,7 @@ describe('e2e_escrow_contract', () => {
TokenContract.notes.TransparentNote.id,
receipt.txHash,
);
await pxe.addNote(extendedNote);
await wallet.addNote(extendedNote);

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

Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_fees/account_init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('e2e_fees account_init', () => {
await expect(t.getGasBalanceFn(bananaFPC.address)).resolves.toEqual([fpcsInitialGas - actualFee]);

// the new account should have received a refund
await t.addPendingShieldNoteToPXE(bobsAddress, maxFee - actualFee, computeSecretHash(rebateSecret), tx.txHash);
await t.addPendingShieldNoteToPXE(aliceAddress, maxFee - actualFee, computeSecretHash(rebateSecret), tx.txHash);

// and it can redeem the refund
await bananaCoin.methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ describe('e2e_fees dapp_subscription', () => {
new PrivateFeePaymentMethod(bananaCoin.address, bananaFPC.address, aliceWallet),
);

// We let Alice see Bob's notes because the expect uses Alice's wallet to interact with the contracts to "get" state.
aliceWallet.setScopes([aliceAddress, bobAddress]);

await expectMapping(
t.getGasBalanceFn,
[sequencerAddress, bananaFPC.address],
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_fees/fees_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export class FeesTest {
BananaCoin.notes.TransparentNote.id,
txHash,
);
await this.pxe.addNote(extendedNote);
await this.pxe.addNote(extendedNote, ownerAddress);
}

public async applyBaseSnapshots() {
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ describe('e2e_fees gas_estimation', () => {
({ aliceWallet, aliceAddress, bobAddress, bananaCoin, bananaFPC, gasSettings, logger } = await t.setup());

teardownFixedFee = gasSettings.teardownGasLimits.computeFee(GasFees.default()).toBigInt();

// We let Alice see Bob's notes because the expect uses Alice's wallet to interact with the contracts to "get" state.
aliceWallet.setScopes([aliceAddress, bobAddress]);
});

afterAll(async () => {
Expand Down
13 changes: 11 additions & 2 deletions yarn-project/end-to-end/src/e2e_fees/native_payments.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { type AztecAddress, NativeFeePaymentMethod, NativeFeePaymentMethodWithClaim } from '@aztec/aztec.js';
import {
type AccountWallet,
type AztecAddress,
NativeFeePaymentMethod,
NativeFeePaymentMethodWithClaim,
} from '@aztec/aztec.js';
import { type GasSettings } from '@aztec/circuits.js';
import { type TokenContract as BananaCoin, type GasTokenContract } from '@aztec/noir-contracts.js';

import { FeesTest } from './fees_test.js';

describe('e2e_fees native_payments', () => {
let aliceAddress: AztecAddress;
let aliceWallet: AccountWallet;
let bobAddress: AztecAddress;
let bananaCoin: BananaCoin;
let gasSettings: GasSettings;
Expand All @@ -17,9 +23,12 @@ describe('e2e_fees native_payments', () => {
beforeAll(async () => {
await t.applyBaseSnapshots();
await t.applyFundAliceWithBananas();
({ gasTokenContract, aliceAddress, bobAddress, bananaCoin, gasSettings } = await t.setup());
({ gasTokenContract, aliceAddress, aliceWallet, bobAddress, bananaCoin, gasSettings } = await t.setup());

paymentMethod = new NativeFeePaymentMethod(aliceAddress);

// We let Alice see Bob's notes because the expect uses Alice's wallet to interact with the contracts to "get" state.
aliceWallet.setScopes([aliceAddress, bobAddress]);
});

afterAll(async () => {
Expand Down
7 changes: 5 additions & 2 deletions yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {
type AccountWallet,
type AztecAddress,
BatchCall,
Fr,
PrivateFeePaymentMethod,
type TxReceipt,
type Wallet,
computeSecretHash,
} from '@aztec/aztec.js';
import { type GasSettings } from '@aztec/circuits.js';
Expand All @@ -14,7 +14,7 @@ import { expectMapping } from '../fixtures/utils.js';
import { FeesTest } from './fees_test.js';

describe('e2e_fees private_payment', () => {
let aliceWallet: Wallet;
let aliceWallet: AccountWallet;
let aliceAddress: AztecAddress;
let bobAddress: AztecAddress;
let sequencerAddress: AztecAddress;
Expand Down Expand Up @@ -73,6 +73,9 @@ describe('e2e_fees private_payment', () => {
t.getBananaPublicBalanceFn(aliceAddress, bobAddress, bananaFPC.address),
t.getGasBalanceFn(aliceAddress, bananaFPC.address, sequencerAddress),
]);

// We let Alice see Bob's notes because the expect uses Alice's wallet to interact with the contracts to "get" state.
aliceWallet.setScopes([aliceAddress, bobAddress]);
});

const getFeeAndRefund = (tx: Pick<TxReceipt, 'transactionFee'>) => [tx.transactionFee!, maxFee - tx.transactionFee!];
Expand Down
9 changes: 7 additions & 2 deletions yarn-project/end-to-end/src/e2e_fees/private_refunds.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
type AccountWallet,
type AztecAddress,
ExtendedNote,
type FeePaymentMethod,
Expand All @@ -16,8 +17,9 @@ import { expectMapping } from '../fixtures/utils.js';
import { FeesTest } from './fees_test.js';

describe('e2e_fees/private_refunds', () => {
let aliceWallet: Wallet;
let aliceWallet: AccountWallet;
let aliceAddress: AztecAddress;
let bobAddress: AztecAddress;
let tokenWithRefunds: TokenWithRefundsContract;
let privateFPC: PrivateFPCContract;

Expand All @@ -34,8 +36,11 @@ describe('e2e_fees/private_refunds', () => {
await t.applyDeployGasTokenSnapshot();
await t.applyTokenWithRefundsAndFPC();
await t.applyFundAliceWithTokens();
({ aliceWallet, aliceAddress, privateFPC, tokenWithRefunds } = await t.setup());
({ aliceWallet, aliceAddress, bobAddress, privateFPC, tokenWithRefunds } = await t.setup());
t.logger.debug(`Alice address: ${aliceAddress}`);

// We give Alice access to Bob's notes because Alice is used to check if balances are correct.
aliceWallet.setScopes([aliceAddress, bobAddress]);
});

afterAll(async () => {
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_keys.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe('Key Registry', () => {

expect(await getNumNullifiedNotes(nskApp, testContract.address)).toEqual(0);

await testContract.methods.call_destroy_note(noteStorageSlot).send().wait();
await testContract.withWallet(account).methods.call_destroy_note(noteStorageSlot).send().wait();

expect(await getNumNullifiedNotes(nskApp, testContract.address)).toEqual(1);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe('e2e_multiple_accounts_1_enc_key', () => {
TokenContract.notes.TransparentNote.id,
receipt.txHash,
);
await pxe.addNote(extendedNote);
await wallets[0].addNote(extendedNote);

await token.methods.redeem_shield(accounts[0], initialBalance, secret).send().wait();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export class PublicCrossChainMessagingContractTest {
walletClient,
this.ownerAddress,
this.aztecNodeConfig.l1Contracts,
this.user1Wallet,
);

this.publicClient = publicClient;
Expand Down
Loading

0 comments on commit d04183c

Please sign in to comment.