diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 196e8dffbc29..63c71c0e7bbd 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -9,6 +9,21 @@ Aztec is in active development. Each version may introduce breaking changes that ## TBD +### [Aztec.js] Removed `SingleKeyAccountContract` + +The `SchnorrSingleKeyAccount` contract and its TypeScript wrapper `SingleKeyAccountContract` have been removed. This contract was insecure: it used `ivpk_m` (incoming viewing public key) as its Schnorr signing key, meaning anyone who received a user's viewing key could sign transactions on their behalf. + +**Migration:** + +```diff +- import { SingleKeyAccountContract } from '@aztec/accounts/single_key'; +- const contract = new SingleKeyAccountContract(signingKey); ++ import { SchnorrAccountContract } from '@aztec/accounts/schnorr'; ++ const contract = new SchnorrAccountContract(signingKey); +``` + +**Impact**: If you were using `@aztec/accounts/single_key`, switch to `@aztec/accounts/schnorr` which uses separate keys for encryption and authentication. + ### `aztec new` and `aztec init` now create a 2-crate workspace `aztec new` and `aztec init` now create a workspace with two crates instead of a single contract crate: diff --git a/noir-projects/noir-contracts/Nargo.toml b/noir-projects/noir-contracts/Nargo.toml index 9d35116f5a36..11d4942fbd63 100644 --- a/noir-projects/noir-contracts/Nargo.toml +++ b/noir-projects/noir-contracts/Nargo.toml @@ -4,7 +4,6 @@ members = [ "contracts/account/ecdsa_r_account_contract", "contracts/account/schnorr_account_contract", "contracts/account/schnorr_hardcoded_account_contract", - "contracts/account/schnorr_single_key_account_contract", "contracts/account/simulated_account_contract", "contracts/app/amm_contract", "contracts/app/app_subscription_contract", diff --git a/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/Nargo.toml deleted file mode 100644 index 808c3b8c60d2..000000000000 --- a/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/Nargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "schnorr_single_key_account_contract" -authors = [""] -compiler_version = ">=0.25.0" -type = "contract" - -[dependencies] -aztec = { path = "../../../../aztec-nr/aztec" } -schnorr = { tag = "v0.1.3", git = "https://github.com/noir-lang/schnorr" } diff --git a/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/src/auth_oracle.nr b/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/src/auth_oracle.nr deleted file mode 100644 index 3a2040d8e19e..000000000000 --- a/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/src/auth_oracle.nr +++ /dev/null @@ -1,17 +0,0 @@ -use aztec::{ - oracle::auth_witness, - protocol::{address::PartialAddress, public_keys::PublicKeys, traits::Deserialize}, -}; -use std::meta::derive; - -#[derive(Deserialize)] -pub struct AuthWitness { - pub keys: PublicKeys, - pub signature: [u8; 64], - pub partial_address: PartialAddress, -} - -pub unconstrained fn get_auth_witness(message_hash: Field) -> AuthWitness { - let witness: [Field; 77] = auth_witness::get_auth_witness(message_hash); - AuthWitness::deserialize(witness) -} diff --git a/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/src/main.nr deleted file mode 100644 index a0757937c291..000000000000 --- a/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/src/main.nr +++ /dev/null @@ -1,45 +0,0 @@ -mod util; -mod auth_oracle; - -use aztec::macros::aztec; - -#[aztec] -pub contract SchnorrSingleKeyAccount { - use aztec::{ - authwit::{account::AccountActions, entrypoint::app::AppPayload}, - context::PrivateContext, - oracle::notes::set_sender_for_tags, - }; - - use crate::{auth_oracle::get_auth_witness, util::recover_address}; - - use aztec::macros::functions::{allow_phase_change, external, view}; - - // @dev: If you globally change the entrypoint signature don't forget to update account_entrypoint.ts (specifically `getEntrypointAbi()`) - #[external("private")] - #[allow_phase_change] - fn entrypoint(app_payload: AppPayload, fee_payment_method: u8, cancellable: bool) { - // Safety: The sender for tags is only used to compute unconstrained shared secrets for emitting logs. - // Since this value is only used for unconstrained tagging and not for any constrained logic, - // it is safe to set from a constrained context. - unsafe { set_sender_for_tags(self.address) }; - - let actions = AccountActions::init(self.context, is_valid_impl); - actions.entrypoint(app_payload, fee_payment_method, cancellable); - } - - #[external("private")] - #[view] - fn verify_private_authwit(inner_hash: Field) -> Field { - let actions = AccountActions::init(self.context, is_valid_impl); - actions.verify_private_authwit(inner_hash) - } - - #[contract_library_method] - fn is_valid_impl(context: &mut PrivateContext, outer_hash: Field) -> bool { - // Safety: The witness is only used as a "magical value" that makes the signature verification - // in `recover_address` and the address check below pass. Hence it's safe. - let witness = unsafe { get_auth_witness(outer_hash) }; - recover_address(outer_hash, witness).eq(context.this_address()) - } -} diff --git a/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/src/util.nr b/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/src/util.nr deleted file mode 100644 index 6bdfe457c6a6..000000000000 --- a/noir-projects/noir-contracts/contracts/account/schnorr_single_key_account_contract/src/util.nr +++ /dev/null @@ -1,17 +0,0 @@ -use crate::auth_oracle::AuthWitness; -use aztec::protocol::address::AztecAddress; -use std::embedded_curve_ops::EmbeddedCurvePoint; - -pub fn recover_address(message_hash: Field, witness: AuthWitness) -> AztecAddress { - let message_bytes: [u8; 32] = message_hash.to_be_bytes(); - let public_key = EmbeddedCurvePoint { - x: witness.keys.ivpk_m.inner.x, - y: witness.keys.ivpk_m.inner.y, - is_infinite: false, - }; - - // In a single key account contract we re-used ivpk_m as signing key - schnorr::assert_valid_signature(public_key, witness.signature, message_bytes); - - AztecAddress::compute(witness.keys, witness.partial_address) -} diff --git a/yarn-project/accounts/README.md b/yarn-project/accounts/README.md index 6ac67ec26c09..b0ffbbf61516 100644 --- a/yarn-project/accounts/README.md +++ b/yarn-project/accounts/README.md @@ -12,7 +12,6 @@ npm install @aztec/accounts - **Schnorr**: Uses an Grumpkin private key with Schnorr signatures for authentication, and a separate Grumpkin private key for encryption. Recommended for most use cases. - **ECDSA**: Uses an ECDSA private key for authentication, and a Grumpkin private key for encryption. Recommended for building integrations with Ethereum wallets. -- **SingleKey**: Uses a single Grumpkin private key for both authentication and encryption. Recommended for testing purposes only. ## Usage diff --git a/yarn-project/accounts/package.json b/yarn-project/accounts/package.json index 20ff7be2456e..01f6ed385e54 100644 --- a/yarn-project/accounts/package.json +++ b/yarn-project/accounts/package.json @@ -10,8 +10,6 @@ "./ecdsa/lazy": "./dest/ecdsa/lazy.js", "./schnorr": "./dest/schnorr/index.js", "./schnorr/lazy": "./dest/schnorr/lazy.js", - "./single_key": "./dest/single_key/index.js", - "./single_key/lazy": "./dest/single_key/lazy.js", "./stub": "./dest/stub/index.js", "./stub/lazy": "./dest/stub/lazy.js", "./testing": "./dest/testing/index.js", @@ -25,7 +23,6 @@ "./src/defaults/index.ts", "./src/ecdsa/index.ts", "./src/schnorr/index.ts", - "./src/single_key/index.ts", "./src/testing/index.ts" ], "name": "Accounts", diff --git a/yarn-project/accounts/scripts/copy-contracts.sh b/yarn-project/accounts/scripts/copy-contracts.sh index c19308ea4997..16dfcd9b3325 100755 --- a/yarn-project/accounts/scripts/copy-contracts.sh +++ b/yarn-project/accounts/scripts/copy-contracts.sh @@ -2,7 +2,7 @@ set -euo pipefail mkdir -p ./artifacts -contracts=(schnorr_account_contract-SchnorrAccount ecdsa_k_account_contract-EcdsaKAccount ecdsa_r_account_contract-EcdsaRAccount schnorr_single_key_account_contract-SchnorrSingleKeyAccount simulated_account_contract-SimulatedAccount ) +contracts=(schnorr_account_contract-SchnorrAccount ecdsa_k_account_contract-EcdsaKAccount ecdsa_r_account_contract-EcdsaRAccount simulated_account_contract-SimulatedAccount ) decl=$(cat < { - const schnorr = new Schnorr(); - const signature = await schnorr.constructSignature(messageHash.toBuffer(), this.privateKey); - const witness = [...this.account.publicKeys.toFields(), ...signature.toBuffer(), this.account.partialAddress]; - return Promise.resolve(new AuthWitness(messageHash, witness)); - } -} diff --git a/yarn-project/accounts/src/single_key/index.ts b/yarn-project/accounts/src/single_key/index.ts deleted file mode 100644 index e82802b71de6..000000000000 --- a/yarn-project/accounts/src/single_key/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * The `@aztec/accounts/single_key` export provides a testing account contract implementation that uses a single Grumpkin key for both authentication and encryption. - * It is not recommended to use this account type in production. - * - * @packageDocumentation - */ -import type { GrumpkinScalar } from '@aztec/aztec.js/fields'; -import type { ContractArtifact } from '@aztec/stdlib/abi'; -import { loadContractArtifact } from '@aztec/stdlib/abi'; -import type { NoirCompiledContract } from '@aztec/stdlib/noir'; - -import SchnorrSingleKeyAccountContractJson from '../../artifacts/SchnorrSingleKeyAccount.json' with { type: 'json' }; -import { SingleKeyBaseAccountContract } from './account_contract.js'; - -export const SchnorrSingleKeyAccountContractArtifact = loadContractArtifact( - SchnorrSingleKeyAccountContractJson as NoirCompiledContract, -); - -/** - * Account contract that authenticates transactions using Schnorr signatures verified against - * the note encryption key, relying on a single private key for both encryption and authentication. - * Eagerly loads the contract artifact - */ -export class SingleKeyAccountContract extends SingleKeyBaseAccountContract { - constructor(signingPrivateKey: GrumpkinScalar) { - super(signingPrivateKey); - } - - override getContractArtifact(): Promise { - return Promise.resolve(SchnorrSingleKeyAccountContractArtifact); - } -} diff --git a/yarn-project/accounts/src/single_key/lazy.ts b/yarn-project/accounts/src/single_key/lazy.ts deleted file mode 100644 index 79d1d352b6cf..000000000000 --- a/yarn-project/accounts/src/single_key/lazy.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * The `@aztec/accounts/single_key` export provides a testing account contract implementation that uses a single Grumpkin key for both authentication and encryption. - * It is not recommended to use this account type in production. - * - * @packageDocumentation - */ -import type { GrumpkinScalar } from '@aztec/aztec.js/fields'; -import type { ContractArtifact } from '@aztec/stdlib/abi'; -import { loadContractArtifact } from '@aztec/stdlib/abi'; - -import { SingleKeyBaseAccountContract } from './account_contract.js'; - -/** - * Lazily loads the contract artifact - * @returns The contract artifact for the single key account contract - */ -export async function getSingleKeyAccountContractArtifact() { - // Cannot assert this import as it's incompatible with bundlers like vite - // https://github.com/vitejs/vite/issues/19095#issuecomment-2566074352 - // Even if now supported by al major browsers, the MIME type is replaced with - // "text/javascript" - // In the meantime, this lazy import is INCOMPATIBLE WITH NODEJS - const { default: schnorrAccountContractJson } = await import('../../artifacts/SchnorrAccount.json'); - return loadContractArtifact(schnorrAccountContractJson); -} - -/** - * Account contract that authenticates transactions using Schnorr signatures verified against - * the note encryption key, relying on a single private key for both encryption and authentication. - * Lazily loads the contract artifact - */ -export class SingleKeyAccountContract extends SingleKeyBaseAccountContract { - constructor(signingPrivateKey: GrumpkinScalar) { - super(signingPrivateKey); - } - - override getContractArtifact(): Promise { - return getSingleKeyAccountContractArtifact(); - } -} diff --git a/yarn-project/cli-wallet/src/cmds/index.ts b/yarn-project/cli-wallet/src/cmds/index.ts index b7410401a1be..b137155cf4c7 100644 --- a/yarn-project/cli-wallet/src/cmds/index.ts +++ b/yarn-project/cli-wallet/src/cmds/index.ts @@ -63,7 +63,7 @@ export function injectCommands( const createAccountCommand = program .command('create-account') .description( - 'Creates an aztec account that can be used for sending transactions. Registers the account on the PXE and deploys an account contract. Uses a Schnorr single-key account which uses the same key for encryption and authentication (not secure for production usage).', + 'Creates an aztec account that can be used for sending transactions. Registers the account on the PXE and deploys an account contract. Uses a Schnorr account which uses an immutable key for authentication.', ) .summary('Creates an aztec account that can be used for sending transactions.') .addOption(createAccountOption('Alias or address of the account performing the deployment', !db, db)) diff --git a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts index 85285d0a4fb1..71c8498a1057 100644 --- a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts @@ -1,6 +1,5 @@ import { EcdsaKAccountContract } from '@aztec/accounts/ecdsa'; import { SchnorrAccountContract } from '@aztec/accounts/schnorr'; -import { SingleKeyAccountContract } from '@aztec/accounts/single_key'; import { type Account, type AccountContract, BaseAccount, getAccountContractAddress } from '@aztec/aztec.js/account'; import { AztecAddress, CompleteAddress } from '@aztec/aztec.js/addresses'; import { Fr, GrumpkinScalar } from '@aztec/aztec.js/fields'; @@ -99,11 +98,7 @@ const itShouldBehaveLikeAnAccountContract = ( }; describe('e2e_account_contracts', () => { - describe('schnorr single-key account', () => { - itShouldBehaveLikeAnAccountContract((encryptionKey: GrumpkinScalar) => new SingleKeyAccountContract(encryptionKey)); - }); - - describe('schnorr multi-key account', () => { + describe('schnorr account', () => { itShouldBehaveLikeAnAccountContract(() => new SchnorrAccountContract(GrumpkinScalar.random())); });