Skip to content
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
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ services:
environment:
AZTEC_NODE_URL: http://node:8080
NODE_NO_WARNINGS: 1
SECRET_KEY:
SIGNING_KEY:
ETHEREUM_HOSTS:
profiles:
- cli
Expand Down
8 changes: 4 additions & 4 deletions docs/docs-developers/docs/aztec-js/how_to_create_account.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ yarn add @aztec/aztec.js@#include_version_without_prefix @aztec/wallets@#include

## Create a new account

Using the [`wallet` from the connection guide](./how_to_connect_to_local_network.md), call `createSchnorrAccount` to create a new account with a random secret and salt:
Using the [`wallet` from the connection guide](./how_to_connect_to_local_network.md), call `createSchnorrAccount` to create a new account with a random secret, salt, and signing key:

#include_code create_account /docs/examples/ts/aztecjs_connection/index.ts typescript

The secret is used to derive the account's encryption keys, and the salt ensures address uniqueness. The signing key is automatically derived from the secret.
The secret derives the account's encryption keys, the signing key authenticates its transactions, and the salt ensures address uniqueness. The signing key is provided independently and is not derived from the secret: it is an ownership key, so keep it separate from the encryption secret that your PXE holds.

:::warning Store your secret and salt
Save the `secret` and `salt` values securely. You need both to recover access to your account. If you lose them, you will permanently lose access to the account and any assets it holds.
:::warning Store your secret, salt, and signing key
Save the `secret`, `salt`, and `signingKey` values securely. You need all three to recover access to your account. If you lose them, you will permanently lose access to the account and any assets it holds.
:::

## Deploy the account
Expand Down
2 changes: 0 additions & 2 deletions docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,6 @@ Registering classes and instances are now separate, unvalidated operations. `reg
The new class is used automatically once the upgrade takes effect on chain; no further PXE action is needed. Registering it beforehand is harmless: until the update activates, the node still resolves the contract's current class to the previous one, so it keeps running its old code.

- `pxe.getContractInstance(address)` and `wallet.getContractMetadata(address).instance` now return the contract's **address preimage**, which no longer includes `currentContractClassId`.


### [Aztec.js] `AccountWithSecretKey` removed, read account keys from the `AccountManager` or PXE

`AccountWithSecretKey` was a thin wrapper that bundled an account's transaction signer with its master secret key, used mainly to print or export the secret. It has been removed, and `AccountManager.getAccount()` now returns the plain `Account` signer. The wrapper's extra methods are no longer available on that value:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,27 @@ An Aztec account has:

## Key Derivation

The wallet uses the standard derivation from `@aztec/stdlib/keys`:
The wallet generates a random signing key as the account's root and derives the privacy secret from it:

```typescript
import { deriveSigningKey } from '@aztec/stdlib/keys';
import { GrumpkinScalar } from '@aztec/aztec.js/fields';
import { deriveSecretKeyFromSigningKey } from '@aztec/accounts/utils';

// Generate a random secret
const secret = Fr.random();

// Derive the signing key
const signingKey = deriveSigningKey(secret);
// The signing key is the account's root; the privacy secret is derived from it
const signingKey = GrumpkinScalar.random();
const secret = await deriveSecretKeyFromSigningKey(signingKey);
```

The derivation uses SHA-512 with domain separators to derive different keys:
The nullifier, viewing, and tagging keys are then derived from that secret with SHA-512 and domain separators, while the signing key is supplied to the account contract directly:

```typescript
// From stdlib/src/keys/derivation.ts
export function deriveSigningKey(secretKey: Fr): GrumpkinScalar {
return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.IVSK_M]);
export function deriveMasterIncomingViewingSecretKey(secretKey: Fr): GrumpkinScalar {
return sha512ToGrumpkinScalar([secretKey, DomainSeparator.IVSK_M]);
}

export function deriveMasterNullifierHidingSecretKey(secretKey: Fr): GrumpkinScalar {
return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.NHK_M]);
return sha512ToGrumpkinScalar([secretKey, DomainSeparator.NHK_M]);
}
```

Expand Down
7 changes: 5 additions & 2 deletions docs/examples/ts/aztecjs_connection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ console.log(`Alice's fee juice balance: ${aliceBalance}`);
// docs:end:check_fee_juice

// docs:start:create_account
import { Fr } from "@aztec/aztec.js/fields";
import { Fr, GrumpkinScalar } from "@aztec/aztec.js/fields";

const secret = Fr.random();
const salt = Fr.random();
const newAccount = await wallet.createSchnorrAccount(secret, salt);
const signingKey = GrumpkinScalar.random();
const newAccount = await wallet.createSchnorrAccount(secret, salt, signingKey);
console.log("New account address:", newAccount.address.toString());
// docs:end:create_account

Expand Down Expand Up @@ -87,9 +88,11 @@ await deployMethod.send({
// can coexist in one example; in your own code, pick whichever name fits.
const feeJuiceSecret = Fr.random();
const feeJuiceSalt = Fr.random();
const feeJuiceSigningKey = GrumpkinScalar.random();
const feeJuiceAccount = await wallet.createSchnorrAccount(
feeJuiceSecret,
feeJuiceSalt,
feeJuiceSigningKey,
);
// docs:end:create_fee_juice_account

Expand Down
12 changes: 10 additions & 2 deletions docs/examples/ts/aztecjs_getting_started/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ const nodeUrl = process.env.AZTEC_NODE_URL ?? "http://localhost:8080";
const wallet = await EmbeddedWallet.create(nodeUrl, { ephemeral: true });

const [alice, bob] = await getInitialTestAccountsData();
await wallet.createSchnorrInitializerlessAccount(alice.secret, alice.salt);
await wallet.createSchnorrInitializerlessAccount(bob.secret, bob.salt);
await wallet.createSchnorrInitializerlessAccount(
alice.secret,
alice.salt,
alice.signingKey,
);
await wallet.createSchnorrInitializerlessAccount(
bob.secret,
bob.salt,
bob.signingKey,
);
// docs:end:setup

// docs:start:deploy
Expand Down
3 changes: 3 additions & 0 deletions docs/examples/ts/bob_token_contract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,18 @@ async function main() {
const giggleAccountManager = await wallet.createSchnorrInitializerlessAccount(
giggleWalletData.secret,
giggleWalletData.salt,
giggleWalletData.signingKey,
);
const aliceAccountManager = await wallet.createSchnorrInitializerlessAccount(
aliceWalletData.secret,
aliceWalletData.salt,
aliceWalletData.signingKey,
);
const bobClinicAccountManager =
await wallet.createSchnorrInitializerlessAccount(
bobClinicWalletData.secret,
bobClinicWalletData.salt,
bobClinicWalletData.signingKey,
);

const giggleAddress = giggleAccountManager.address;
Expand Down
8 changes: 6 additions & 2 deletions docs/examples/ts/recursive_verification/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { SponsoredFPCContract } from "@aztec/noir-contracts.js/SponsoredFPC";
import { ValueNotEqualContract } from "./artifacts/ValueNotEqual.js";
import { EmbeddedWallet } from "@aztec/wallets/embedded";
import { NO_FROM } from "@aztec/aztec.js/account";
import { Fr } from "@aztec/aztec.js/fields";
import { Fr, GrumpkinScalar } from "@aztec/aztec.js/fields";
import assert from "node:assert";
import fs from "node:fs";

Expand Down Expand Up @@ -47,7 +47,11 @@ async function main() {
// Step 1: Setup wallet and create account
// Accounts in Aztec are smart contracts (account abstraction)
const wallet = await setupWallet();
const manager = await wallet.createSchnorrAccount(Fr.random(), Fr.random());
const manager = await wallet.createSchnorrAccount(
Fr.random(),
Fr.random(),
GrumpkinScalar.random(),
);

// Deploy the account contract
const deployMethod = await manager.getDeployMethod();
Expand Down
1 change: 1 addition & 0 deletions docs/examples/ts/token_bridge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const [accData] = await getInitialTestAccountsData();
const account = await aztecWallet.createSchnorrInitializerlessAccount(
accData.secret,
accData.salt,
accData.signingKey,
);
console.log(`Account: ${account.address.toString()}\n`);

Expand Down
18 changes: 12 additions & 6 deletions docs/examples/webapp-tutorial/scripts/deploy-and-interact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ const nodeUrl = process.env.AZTEC_NODE_URL ?? "http://localhost:8080";
const wallet = await EmbeddedWallet.create(nodeUrl, { ephemeral: true });

const [alice, bob] = await getInitialTestAccountsData();
await wallet.createSchnorrAccount(alice.secret, alice.salt);
await wallet.createSchnorrAccount(bob.secret, bob.salt);
console.log("Accounts ready:", alice.address.toString(), bob.address.toString());
await wallet.createSchnorrAccount(alice.secret, alice.salt, alice.signingKey);
await wallet.createSchnorrAccount(bob.secret, bob.salt, bob.signingKey);
console.log(
"Accounts ready:",
alice.address.toString(),
bob.address.toString(),
);
// docs:end:script-setup

// docs:start:script-deploy
const { contract } = await PodRacingContract.deploy(wallet, alice.address).send({
from: alice.address,
});
const { contract } = await PodRacingContract.deploy(wallet, alice.address).send(
{
from: alice.address,
},
);
console.log("Contract deployed at:", contract.address.toString());
// docs:end:script-deploy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* account creation, deployment, and registration.
*/

import { getAztecCore } from './aztec-imports';
import { getAztecCore } from "./aztec-imports";

/**
* Derives keys and instantiates a Schnorr account contract from a secret and salt.
Expand All @@ -21,17 +21,18 @@ import { getAztecCore } from './aztec-imports';
export async function instantiateAccount(secret: string, salt: string) {
const {
Fr,
GrumpkinScalar,
deriveKeys,
deriveSigningKey,
deriveSecretKeyFromSigningKey,
SchnorrAccountContract,
getContractInstanceFromInstantiationParams,
} = await getAztecCore();

const secretFr = Fr.fromString(secret);
const signingKey = GrumpkinScalar.fromString(secret);
const saltFr = Fr.fromString(salt);

const secretFr = await deriveSecretKeyFromSigningKey(signingKey);
const { publicKeys } = await deriveKeys(secretFr);
const signingKey = deriveSigningKey(secretFr);
const accountContract = new SchnorrAccountContract(signingKey);

const initInfo = await accountContract.getInitializationFunctionAndArgs();
Expand Down
71 changes: 38 additions & 33 deletions docs/examples/webapp-tutorial/test-extension/src/aztec-imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,30 @@

/** Core imports needed for account operations (key derivation, contract setup) */
export interface AztecCoreImports {
Fr: typeof import('@aztec/aztec.js/fields').Fr;
AztecAddress: typeof import('@aztec/aztec.js/addresses').AztecAddress;
deriveKeys: typeof import('@aztec/stdlib/keys').deriveKeys;
deriveSigningKey: typeof import('@aztec/stdlib/keys').deriveSigningKey;
SchnorrAccountContract: typeof import('@aztec/accounts/schnorr/lazy').SchnorrAccountContract;
getContractInstanceFromInstantiationParams: typeof import('@aztec/aztec.js/contracts').getContractInstanceFromInstantiationParams;
AccountManager: typeof import('@aztec/aztec.js/wallet').AccountManager;
Fr: typeof import("@aztec/aztec.js/fields").Fr;
GrumpkinScalar: typeof import("@aztec/aztec.js/fields").GrumpkinScalar;
AztecAddress: typeof import("@aztec/aztec.js/addresses").AztecAddress;
deriveKeys: typeof import("@aztec/stdlib/keys").deriveKeys;
deriveSecretKeyFromSigningKey: typeof import("@aztec/accounts/utils").deriveSecretKeyFromSigningKey;
SchnorrAccountContract: typeof import("@aztec/accounts/schnorr/lazy").SchnorrAccountContract;
getContractInstanceFromInstantiationParams: typeof import("@aztec/aztec.js/contracts").getContractInstanceFromInstantiationParams;
AccountManager: typeof import("@aztec/aztec.js/wallet").AccountManager;
}

/** Additional imports for the wallet runtime (BaseWallet, serialization) */
export interface AztecWalletImports extends AztecCoreImports {
BaseWallet: typeof import('@aztec/wallet-sdk/base-wallet').BaseWallet;
SignerlessAccount: typeof import('@aztec/aztec.js/account').SignerlessAccount;
WalletSchema: typeof import('@aztec/aztec.js/wallet').WalletSchema;
jsonStringify: typeof import('@aztec/foundation/json-rpc').jsonStringify;
schemaHasMethod: typeof import('@aztec/foundation/schemas').schemaHasMethod;
BaseWallet: typeof import("@aztec/wallet-sdk/base-wallet").BaseWallet;
SignerlessAccount: typeof import("@aztec/aztec.js/account").SignerlessAccount;
WalletSchema: typeof import("@aztec/aztec.js/wallet").WalletSchema;
jsonStringify: typeof import("@aztec/foundation/json-rpc").jsonStringify;
schemaHasMethod: typeof import("@aztec/foundation/schemas").schemaHasMethod;
}

/** Deploy-specific imports (fee payment, SponsoredFPC) */
export interface AztecDeployImports extends AztecCoreImports {
SponsoredFeePaymentMethod: typeof import('@aztec/aztec.js/fee').SponsoredFeePaymentMethod;
SponsoredFPCContract: typeof import('@aztec/noir-contracts.js/SponsoredFPC').SponsoredFPCContract;
SPONSORED_FPC_SALT: typeof import('@aztec/constants').SPONSORED_FPC_SALT;
SponsoredFeePaymentMethod: typeof import("@aztec/aztec.js/fee").SponsoredFeePaymentMethod;
SponsoredFPCContract: typeof import("@aztec/noir-contracts.js/SponsoredFPC").SponsoredFPCContract;
SPONSORED_FPC_SALT: typeof import("@aztec/constants").SPONSORED_FPC_SALT;
}

let coreCache: AztecCoreImports | null = null;
Expand All @@ -50,22 +51,26 @@ let deployCache: AztecDeployImports | null = null;
export async function getAztecCore(): Promise<AztecCoreImports> {
if (coreCache) return coreCache;

const [fields, addresses, keys, schnorr, contracts, wallet] = await Promise.all([
import('@aztec/aztec.js/fields'),
import('@aztec/aztec.js/addresses'),
import('@aztec/stdlib/keys'),
import('@aztec/accounts/schnorr/lazy'),
import('@aztec/aztec.js/contracts'),
import('@aztec/aztec.js/wallet'),
]);
const [fields, addresses, keys, accountUtils, schnorr, contracts, wallet] =
await Promise.all([
import("@aztec/aztec.js/fields"),
import("@aztec/aztec.js/addresses"),
import("@aztec/stdlib/keys"),
import("@aztec/accounts/utils"),
import("@aztec/accounts/schnorr/lazy"),
import("@aztec/aztec.js/contracts"),
import("@aztec/aztec.js/wallet"),
]);

coreCache = {
Fr: fields.Fr,
GrumpkinScalar: fields.GrumpkinScalar,
AztecAddress: addresses.AztecAddress,
deriveKeys: keys.deriveKeys,
deriveSigningKey: keys.deriveSigningKey,
deriveSecretKeyFromSigningKey: accountUtils.deriveSecretKeyFromSigningKey,
SchnorrAccountContract: schnorr.SchnorrAccountContract,
getContractInstanceFromInstantiationParams: contracts.getContractInstanceFromInstantiationParams,
getContractInstanceFromInstantiationParams:
contracts.getContractInstanceFromInstantiationParams,
AccountManager: wallet.AccountManager,
};

Expand All @@ -81,11 +86,11 @@ export async function getAztecWallet(): Promise<AztecWalletImports> {

const [core, bw, account, walletMod, jsonRpc, schemas] = await Promise.all([
getAztecCore(),
import('@aztec/wallet-sdk/base-wallet'),
import('@aztec/aztec.js/account'),
import('@aztec/aztec.js/wallet'),
import('@aztec/foundation/json-rpc'),
import('@aztec/foundation/schemas'),
import("@aztec/wallet-sdk/base-wallet"),
import("@aztec/aztec.js/account"),
import("@aztec/aztec.js/wallet"),
import("@aztec/foundation/json-rpc"),
import("@aztec/foundation/schemas"),
]);

walletCache = {
Expand All @@ -109,9 +114,9 @@ export async function getAztecDeploy(): Promise<AztecDeployImports> {

const [core, fee, sponsoredFpc, constants] = await Promise.all([
getAztecCore(),
import('@aztec/aztec.js/fee'),
import('@aztec/noir-contracts.js/SponsoredFPC'),
import('@aztec/constants'),
import("@aztec/aztec.js/fee"),
import("@aztec/noir-contracts.js/SponsoredFPC"),
import("@aztec/constants"),
]);

deployCache = {
Expand Down
Loading
Loading