From 7c076653169771223a378f6c01bd9d3e3aafb682 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Mon, 22 Jan 2024 15:03:29 +0000 Subject: [PATCH] feat!: nullifier key (#4166) - Rename oracle call `get_secret_key` to `get_nullifier_secret_key`. - Add new method `request_nullifier_secret_key(account: Address)` to `PrivateContext`, which returns a nullifier secret key for an account, and also pushes a request for the kernel circuit to verify the keys. - Changes to `NoteInterface`: - Change `compute_nullifier()` to `compute_nullifier(private_context: PrivateContext)`. If a secret is used in creating a nullifier, it must be fetched via `private_context.request_nullifier_secret_key(account_address)`. - Add new method `compute_nullifier_without_context()`. This will be used in unconstrained functions where the private context is not available, and using an unverified nullifier keys won't affect other users. For example, it's used in `compute_note_hash_and_nullifier` to compute values for the user's own notes. - Add new apis to `KeyStore`: - `getNullifierSecretKey(pubKey: PublicKey)` - `getNullifierPublicKey(pubKey: PublicKey)` - `getSiloedNullifierSecretKey(pubKey: PublicKey, contractAddress: AztecAddress)` --- .../src/contracts/src/types/balance_set.nr | 6 - .../src/contracts/src/types/token_note.nr | 26 ++- .../contracts/src/types/transparent_note.nr | 15 +- .../docs/concepts/foundation/accounts/keys.md | 2 +- .../writing_private_voting_contract.md | 2 +- .../acir-simulator/src/acvm/oracle/oracle.ts | 12 +- .../src/acvm/oracle/typed_oracle.ts | 20 ++- .../acir-simulator/src/client/db_oracle.ts | 18 +- .../src/client/private_execution.test.ts | 57 ++++++- .../src/client/simulator.test.ts | 24 +-- .../acir-simulator/src/client/simulator.ts | 4 +- .../src/client/view_data_oracle.ts | 10 +- .../aztec-nr/address-note/src/address_note.nr | 26 ++- yarn-project/aztec-nr/aztec/src/context.nr | 56 +++--- .../aztec/src/history/note_validity.nr | 4 +- .../src/history/nullifier_non_inclusion.nr | 6 +- yarn-project/aztec-nr/aztec/src/key.nr | 1 + .../aztec-nr/aztec/src/key/nullifier_key.nr | 24 +++ yarn-project/aztec-nr/aztec/src/lib.nr | 1 + .../aztec-nr/aztec/src/note/lifecycle.nr | 2 +- .../aztec-nr/aztec/src/note/note_interface.nr | 4 +- yarn-project/aztec-nr/aztec/src/note/utils.nr | 13 +- yarn-project/aztec-nr/aztec/src/oracle.nr | 2 +- .../aztec/src/oracle/get_secret_key.nr | 25 --- .../aztec/src/oracle/nullifier_key.nr | 29 ++++ .../src/state_vars/immutable_singleton.nr | 6 +- .../aztec/src/state_vars/singleton.nr | 20 ++- .../aztec-nr/field-note/src/field_note.nr | 16 +- .../aztec-nr/value-note/src/value_note.nr | 26 ++- .../circuit-types/src/keys/key_store.ts | 27 ++- yarn-project/circuits.js/src/index.ts | 1 + yarn-project/circuits.js/src/keys/index.ts | 44 +++++ .../src/types/grumpkin_private_key.ts | 2 +- yarn-project/key-store/src/test_key_store.ts | 25 ++- .../contracts/card_game_contract/src/cards.nr | 5 +- .../contracts/card_game_contract/src/main.nr | 2 +- .../src/account_contract_interface.nr | 11 -- .../docs_example_contract/src/actions.nr | 132 --------------- .../docs_example_contract/src/main.nr | 159 +----------------- .../docs_example_contract/src/types.nr | 3 - .../src/types/card_note.nr | 25 ++- .../src/types/profile_note.nr | 119 ------------- .../docs_example_contract/src/types/queen.nr | 26 --- .../src/types/rules_note.nr | 119 ------------- .../easy_private_voting_contract/src/main.nr | 3 +- .../src/ecdsa_public_key_note.nr | 26 ++- .../inclusion_proofs_contract/src/main.nr | 4 +- .../src/public_key_note.nr | 26 ++- .../src/types/balance_set.nr | 6 - .../src/types/token_note.nr | 26 ++- .../src/types/transparent_note.nr | 15 +- .../token_contract/src/types/balance_set.nr | 6 - .../token_contract/src/types/token_note.nr | 26 ++- .../src/types/transparent_note.nr | 15 +- .../pxe/src/simulator_oracle/index.ts | 20 +-- 55 files changed, 561 insertions(+), 769 deletions(-) create mode 100644 yarn-project/aztec-nr/aztec/src/key.nr create mode 100644 yarn-project/aztec-nr/aztec/src/key/nullifier_key.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/oracle/get_secret_key.nr create mode 100644 yarn-project/aztec-nr/aztec/src/oracle/nullifier_key.nr create mode 100644 yarn-project/circuits.js/src/keys/index.ts delete mode 100644 yarn-project/noir-contracts/contracts/docs_example_contract/src/account_contract_interface.nr delete mode 100644 yarn-project/noir-contracts/contracts/docs_example_contract/src/types/profile_note.nr delete mode 100644 yarn-project/noir-contracts/contracts/docs_example_contract/src/types/queen.nr delete mode 100644 yarn-project/noir-contracts/contracts/docs_example_contract/src/types/rules_note.nr diff --git a/boxes/token/src/contracts/src/types/balance_set.nr b/boxes/token/src/contracts/src/types/balance_set.nr index f81c33db5d4..231320de558 100644 --- a/boxes/token/src/contracts/src/types/balance_set.nr +++ b/boxes/token/src/contracts/src/types/balance_set.nr @@ -18,12 +18,6 @@ use dep::aztec::note::{ note_interface::NoteInterface, utils::compute_note_hash_for_read_or_nullify, }; -use dep::aztec::oracle::{ - rand::rand, - get_secret_key::get_secret_key, - get_public_key::get_public_key, -}; - use crate::types::token_note::{TokenNote, TOKEN_NOTE_LEN, TokenNoteMethods}; // A set implementing standard manipulation of balances. diff --git a/boxes/token/src/contracts/src/types/token_note.nr b/boxes/token/src/contracts/src/types/token_note.nr index ed22cfc0e98..f8adf31959d 100644 --- a/boxes/token/src/contracts/src/types/token_note.nr +++ b/boxes/token/src/contracts/src/types/token_note.nr @@ -17,7 +17,7 @@ use dep::aztec::{ }; use dep::aztec::oracle::{ rand::rand, - get_secret_key::get_secret_key, + nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key, }; use dep::safe_math::SafeU120; @@ -72,9 +72,9 @@ impl TokenNote { } // docs:start:nullifier - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); - let secret = get_secret_key(self.owner); + let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ note_hash_for_nullify, @@ -84,6 +84,17 @@ impl TokenNote { } // docs:end:nullifier + pub fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); + let secret = get_nullifier_secret_key(self.owner); + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash([ + note_hash_for_nullify, + secret.low, + secret.high, + ],0) + } + pub fn set_header(&mut self, header: NoteHeader) { self.header = header; } @@ -116,8 +127,12 @@ fn compute_note_hash(note: TokenNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: TokenNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: TokenNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: TokenNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: TokenNote) -> NoteHeader { @@ -138,6 +153,7 @@ global TokenNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/boxes/token/src/contracts/src/types/transparent_note.nr b/boxes/token/src/contracts/src/types/transparent_note.nr index 034b4b3390f..deb2bcdf6f1 100644 --- a/boxes/token/src/contracts/src/types/transparent_note.nr +++ b/boxes/token/src/contracts/src/types/transparent_note.nr @@ -70,7 +70,11 @@ impl TransparentNote { ],0) } - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { + self.compute_nullifier_without_context() + } + + pub fn compute_nullifier_without_context(self) -> Field { // TODO(#1386): should use `compute_note_hash_for_read_or_nullify` once public functions inject nonce! let siloed_note_hash = compute_siloed_note_hash(TransparentNoteMethods, self); // TODO(#1205) Should use a non-zero generator index. @@ -102,8 +106,12 @@ fn compute_note_hash(note: TransparentNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: TransparentNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: TransparentNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: TransparentNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: TransparentNote) -> NoteHeader { @@ -123,6 +131,7 @@ global TransparentNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/docs/docs/concepts/foundation/accounts/keys.md b/docs/docs/concepts/foundation/accounts/keys.md index 21d0f240c81..e5d897d0ffc 100644 --- a/docs/docs/concepts/foundation/accounts/keys.md +++ b/docs/docs/concepts/foundation/accounts/keys.md @@ -82,7 +82,7 @@ Note that any accounts you own that have been added to the PXE are automatically In addition to deriving encryption keys, the privacy master key is used for deriving nullifier secrets. Whenever a private note is consumed, a nullifier deterministically derived from it is emitted. This mechanisms prevents double-spends, since nullifiers are checked by the protocol to be unique. Now, in order to preserve privacy, a third party should not be able to link a note commitment to its nullifier - this link is enforced by the note implementation. Therefore, calculating the nullifier for a note requires a secret from its owner. -An application in Aztec.nr can request a secret from the current user for computing the nullifier of a note via the `get_secret_key` oracle call: +An application in Aztec.nr can request a secret from the current user for computing the nullifier of a note via the `request_nullifier_secret_key` api: #include_code nullifier /yarn-project/aztec-nr/value-note/src/value_note.nr rust diff --git a/docs/docs/dev_docs/tutorials/writing_private_voting_contract.md b/docs/docs/dev_docs/tutorials/writing_private_voting_contract.md index b5e6f084721..c8ab1e70f64 100644 --- a/docs/docs/dev_docs/tutorials/writing_private_voting_contract.md +++ b/docs/docs/dev_docs/tutorials/writing_private_voting_contract.md @@ -71,7 +71,7 @@ Inside this, paste these imports: We are using various utils within the Aztec library: - `context` - exposes things such as the contract address, msg_sender, etc -- `oracle::get_secret_key` - get your secret key to help us create a randomized nullifier +- `context.request_nullifier_secret_key` - get your secret key to help us create a randomized nullifier - `FunctionSelector::from_signature` - compute a function selector from signature so we can call functions from other functions - `state_vars::{ map::Map, public_state::PublicState, }` - we will use a Map to store the votes (key = voteId, value = number of votes), and PublicState to hold our public values that we mentioned earlier - `types::type_serialization::{..}` - various serialization methods for defining how to use these types diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 49449ffcbcb..10a78d70875 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -33,10 +33,14 @@ export class Oracle { return toACVMField(packed); } - async getSecretKey([publicKeyX]: ACVMField[], [publicKeyY]: ACVMField[]): Promise { - const publicKey = new Point(fromACVMField(publicKeyX), fromACVMField(publicKeyY)); - const secretKey = await this.typedOracle.getSecretKey(publicKey); - return [toACVMField(secretKey.low), toACVMField(secretKey.high)]; + async getNullifierKeyPair([accountAddress]: ACVMField[]): Promise { + const { publicKey, secretKey } = await this.typedOracle.getNullifierKeyPair(fromACVMField(accountAddress)); + return [ + toACVMField(publicKey.x), + toACVMField(publicKey.y), + toACVMField(secretKey.high), + toACVMField(secretKey.low), + ]; } async getPublicKeyAndPartialAddress([address]: ACVMField[]) { diff --git a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts index 995a285b7b1..621a62db10f 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -7,11 +7,25 @@ import { PublicKey, UnencryptedL2Log, } from '@aztec/circuit-types'; -import { BlockHeader, PrivateCallStackItem, PublicCallRequest } from '@aztec/circuits.js'; +import { BlockHeader, GrumpkinPrivateKey, PrivateCallStackItem, PublicCallRequest } from '@aztec/circuits.js'; import { FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; +import { Fr } from '@aztec/foundation/fields'; + +/** + * A pair of public key and secret key. + */ +export interface KeyPair { + /** + * Public key. + */ + publicKey: PublicKey; + /** + * Secret Key. + */ + secretKey: GrumpkinPrivateKey; +} /** * Information about a note needed during execution. @@ -76,7 +90,7 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getSecretKey(_owner: PublicKey): Promise { + getNullifierKeyPair(_accountAddress: AztecAddress): Promise { throw new Error('Not available.'); } diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index eb4f59fae84..7090aa52300 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -1,11 +1,11 @@ import { L2Block, MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/circuit-types'; -import { BlockHeader, CompleteAddress, GrumpkinPrivateKey, PublicKey } from '@aztec/circuits.js'; +import { BlockHeader, CompleteAddress } from '@aztec/circuits.js'; import { FunctionArtifactWithDebugMetadata, FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; -import { NoteData } from '../acvm/index.js'; +import { KeyPair, NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/db.js'; /** @@ -43,16 +43,16 @@ export interface DBOracle extends CommitmentsDB { popCapsule(): Promise; /** - * Retrieve the secret key associated with a specific public key. + * Retrieve the nullifier key pair associated with a specific account. * The function only allows access to the secret keys of the transaction creator, - * and throws an error if the address does not match the public key address of the key pair. + * and throws an error if the address does not match the account address of the key pair. * - * @param contractAddress - The contract address. Ignored here. But we might want to return different keys for different contracts. - * @param pubKey - The public key of an account. - * @returns A Promise that resolves to the secret key. - * @throws An Error if the input address does not match the public key address of the key pair. + * @param accountAddress - The account address. + * @param contractAddress - The contract address. + * @returns A Promise that resolves to the nullifier key pair. + * @throws An Error if the input address does not match the account address of the key pair. */ - getSecretKey(contractAddress: AztecAddress, pubKey: PublicKey): Promise; + getNullifierKeyPair(accountAddress: AztecAddress, contractAddress: AztecAddress): Promise; /** * Retrieves a set of notes stored in the database for a given contract address and storage slot. diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index becd6a0f73c..01b8c34ac75 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -9,8 +9,10 @@ import { MAX_NEW_COMMITMENTS_PER_CALL, NOTE_HASH_TREE_HEIGHT, PublicCallRequest, - PublicKey, TxContext, + computeNullifierSecretKey, + computeSiloedNullifierSecretKey, + derivePublicKey, nonEmptySideEffects, sideEffectArrayToValueArray, } from '@aztec/circuits.js'; @@ -52,6 +54,7 @@ import { default as levelup } from 'levelup'; import { type MemDown, default as memdown } from 'memdown'; import { getFunctionSelector } from 'viem'; +import { KeyPair } from '../acvm/index.js'; import { buildL1ToL2Message } from '../test/utils.js'; import { computeSlotForMapping } from '../utils.js'; import { DBOracle } from './db_oracle.js'; @@ -75,6 +78,8 @@ describe('Private Execution test suite', () => { let recipient: AztecAddress; let ownerCompleteAddress: CompleteAddress; let recipientCompleteAddress: CompleteAddress; + let ownerNullifierKeyPair: KeyPair; + let recipientNullifierKeyPair: KeyPair; const treeHeights: { [name: string]: number } = { noteHash: NOTE_HASH_TREE_HEIGHT, @@ -157,18 +162,36 @@ describe('Private Execution test suite', () => { owner = ownerCompleteAddress.address; recipient = recipientCompleteAddress.address; + + const ownerNullifierSecretKey = computeNullifierSecretKey(ownerPk); + ownerNullifierKeyPair = { + secretKey: ownerNullifierSecretKey, + publicKey: derivePublicKey(ownerNullifierSecretKey), + }; + + const recipientNullifierSecretKey = computeNullifierSecretKey(recipientPk); + recipientNullifierKeyPair = { + secretKey: recipientNullifierSecretKey, + publicKey: derivePublicKey(recipientNullifierSecretKey), + }; }); beforeEach(() => { oracle = mock(); - oracle.getSecretKey.mockImplementation((contractAddress: AztecAddress, pubKey: PublicKey) => { - if (pubKey.equals(ownerCompleteAddress.publicKey)) { - return Promise.resolve(ownerPk); + oracle.getNullifierKeyPair.mockImplementation((accountAddress: AztecAddress, contractAddress: AztecAddress) => { + if (accountAddress.equals(ownerCompleteAddress.address)) { + return Promise.resolve({ + publicKey: ownerNullifierKeyPair.publicKey, + secretKey: computeSiloedNullifierSecretKey(ownerNullifierKeyPair.secretKey, contractAddress), + }); } - if (pubKey.equals(recipientCompleteAddress.publicKey)) { - return Promise.resolve(recipientPk); + if (accountAddress.equals(recipientCompleteAddress.address)) { + return Promise.resolve({ + publicKey: recipientNullifierKeyPair.publicKey, + secretKey: computeSiloedNullifierSecretKey(recipientNullifierKeyPair.secretKey, contractAddress), + }); } - throw new Error(`Unknown address ${pubKey}`); + throw new Error(`Unknown address ${accountAddress}`); }); oracle.getBlockHeader.mockResolvedValue(blockHeader); @@ -659,7 +682,15 @@ describe('Private Execution test suite', () => { expect(gotNoteValue).toEqual(amountToTransfer); const nullifier = result.callStackItem.publicInputs.newNullifiers[0]; - const expectedNullifier = hashFields([innerNoteHash, ownerPk.low, ownerPk.high]); + const siloedNullifierSecretKey = computeSiloedNullifierSecretKey( + ownerNullifierKeyPair.secretKey, + contractAddress, + ); + const expectedNullifier = hashFields([ + innerNoteHash, + siloedNullifierSecretKey.low, + siloedNullifierSecretKey.high, + ]); expect(nullifier.value).toEqual(expectedNullifier); }); @@ -732,7 +763,15 @@ describe('Private Execution test suite', () => { expect(gotNoteValue).toEqual(amountToTransfer); const nullifier = execGetThenNullify.callStackItem.publicInputs.newNullifiers[0]; - const expectedNullifier = hashFields([innerNoteHash, ownerPk.low, ownerPk.high]); + const siloedNullifierSecretKey = computeSiloedNullifierSecretKey( + ownerNullifierKeyPair.secretKey, + contractAddress, + ); + const expectedNullifier = hashFields([ + innerNoteHash, + siloedNullifierSecretKey.low, + siloedNullifierSecretKey.high, + ]); expect(nullifier.value).toEqual(expectedNullifier); // check that the last get_notes call return no note diff --git a/yarn-project/acir-simulator/src/client/simulator.test.ts b/yarn-project/acir-simulator/src/client/simulator.test.ts index b1e0d95a4bc..7c5a3e830e2 100644 --- a/yarn-project/acir-simulator/src/client/simulator.test.ts +++ b/yarn-project/acir-simulator/src/client/simulator.test.ts @@ -4,7 +4,7 @@ import { computeUniqueCommitment, siloCommitment } from '@aztec/circuits.js/abis import { ABIParameterVisibility, FunctionArtifactWithDebugMetadata, getFunctionArtifact } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { pedersenHash } from '@aztec/foundation/crypto'; -import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; +import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; import { TokenContractArtifact } from '@aztec/noir-contracts/Token'; import { MockProxy, mock } from 'jest-mock-extended'; @@ -15,20 +15,20 @@ import { AcirSimulator } from './simulator.js'; describe('Simulator', () => { let oracle: MockProxy; let simulator: AcirSimulator; - let ownerCompleteAddress: CompleteAddress; - let owner: AztecAddress; const ownerPk = GrumpkinScalar.fromString('2dcc5485a58316776299be08c78fa3788a1a7961ae30dc747fb1be17692a8d32'); + const ownerCompleteAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(ownerPk, Fr.random()); + const owner = ownerCompleteAddress.address; + const ownerNullifierSecretKey = GrumpkinScalar.random(); + const ownerNullifierPublicKey = Point.random(); const hashFields = (data: Fr[]) => Fr.fromBuffer(pedersenHash(data.map(f => f.toBuffer()))); - beforeAll(() => { - ownerCompleteAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(ownerPk, Fr.random()); - owner = ownerCompleteAddress.address; - }); - beforeEach(() => { oracle = mock(); - oracle.getSecretKey.mockResolvedValue(ownerPk); + oracle.getNullifierKeyPair.mockResolvedValue({ + secretKey: ownerNullifierSecretKey, + publicKey: ownerNullifierPublicKey, + }); oracle.getCompleteAddress.mockResolvedValue(ownerCompleteAddress); simulator = new AcirSimulator(oracle); @@ -50,7 +50,11 @@ describe('Simulator', () => { const innerNoteHash = hashFields([storageSlot, valueNoteHash]); const siloedNoteHash = siloCommitment(contractAddress, innerNoteHash); const uniqueSiloedNoteHash = computeUniqueCommitment(nonce, siloedNoteHash); - const innerNullifier = hashFields([uniqueSiloedNoteHash, ownerPk.low, ownerPk.high]); + const innerNullifier = hashFields([ + uniqueSiloedNoteHash, + ownerNullifierSecretKey.low, + ownerNullifierSecretKey.high, + ]); const result = await simulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, note); diff --git a/yarn-project/acir-simulator/src/client/simulator.ts b/yarn-project/acir-simulator/src/client/simulator.ts index 92430a19969..74e6c1ae4ee 100644 --- a/yarn-project/acir-simulator/src/client/simulator.ts +++ b/yarn-project/acir-simulator/src/client/simulator.ts @@ -184,7 +184,7 @@ export class AcirSimulator { const extendedNoteItems = note.items.concat(Array(maxNoteFields - note.items.length).fill(Fr.ZERO)); const execRequest: FunctionCall = { - to: AztecAddress.ZERO, + to: contractAddress, functionData: FunctionData.empty(), args: encodeArguments(artifact, [contractAddress, nonce, storageSlot, extendedNoteItems]), }; @@ -192,7 +192,7 @@ export class AcirSimulator { const [innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier] = (await this.runUnconstrained( execRequest, artifact, - AztecAddress.ZERO, + contractAddress, )) as bigint[]; return { diff --git a/yarn-project/acir-simulator/src/client/view_data_oracle.ts b/yarn-project/acir-simulator/src/client/view_data_oracle.ts index b029833f2ea..3b6128e3221 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -7,7 +7,7 @@ import { NullifierMembershipWitness, PublicDataWitness, } from '@aztec/circuit-types'; -import { BlockHeader, PublicKey } from '@aztec/circuits.js'; +import { BlockHeader } from '@aztec/circuits.js'; import { computeGlobalsHash, siloNullifier } from '@aztec/circuits.js/abis'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; @@ -36,11 +36,11 @@ export class ViewDataOracle extends TypedOracle { } /** - * Return the secret key of a owner to use in a specific contract. - * @param owner - The owner of the secret key. + * Return the nullifier key pair of an account to use in a specific contract. + * @param account - The account address of the nullifier key. */ - public getSecretKey(owner: PublicKey) { - return this.db.getSecretKey(this.contractAddress, owner); + public getNullifierKeyPair(account: AztecAddress) { + return this.db.getNullifierKeyPair(account, this.contractAddress); } /** diff --git a/yarn-project/aztec-nr/address-note/src/address_note.nr b/yarn-project/aztec-nr/address-note/src/address_note.nr index dc5e0fa47d6..199cbd7b078 100644 --- a/yarn-project/aztec-nr/address-note/src/address_note.nr +++ b/yarn-project/aztec-nr/address-note/src/address_note.nr @@ -10,7 +10,7 @@ use dep::aztec::{ }, oracle::{ rand::rand, - get_secret_key::get_secret_key, + nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key, }, hash::pedersen_hash, @@ -59,9 +59,20 @@ impl AddressNote { pedersen_hash(self.serialize(), 0) } - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(AddressNoteMethods, self); - let secret = get_secret_key(self.owner); + let secret = context.request_nullifier_secret_key(self.owner); + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash([ + note_hash_for_nullify, + secret.low, + secret.high, + ],0) + } + + pub fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(AddressNoteMethods, self); + let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ note_hash_for_nullify, @@ -101,8 +112,12 @@ fn compute_note_hash(note: AddressNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: AddressNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: AddressNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: AddressNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: AddressNote) -> NoteHeader { @@ -123,6 +138,7 @@ global AddressNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/aztec-nr/aztec/src/context.nr b/yarn-project/aztec-nr/aztec/src/context.nr index b89e570f53f..6329a9d541f 100644 --- a/yarn-project/aztec-nr/aztec/src/context.nr +++ b/yarn-project/aztec-nr/aztec/src/context.nr @@ -1,3 +1,22 @@ +use crate::{ + abi::{ + PrivateContextInputs, + PublicContextInputs, + }, + key::nullifier_key::validate_nullifier_key_against_address, + messaging::process_l1_to_l2_message, + oracle::{ + arguments, + call_private_function::call_private_function_internal, + public_call::call_public_function_internal, + enqueue_public_function_call::enqueue_public_function_call_internal, + context::get_portal_address, + get_block_header::get_block_header, + nullifier_key::get_nullifier_key_pair, + }, + types::vec::BoundedVec, + utils::Reader, +}; use dep::protocol_types::{ abis::{ block_header::BlockHeader, @@ -34,35 +53,14 @@ use dep::protocol_types::{ hash::hash_args, grumpkin_point::GrumpkinPoint, }; +use dep::std::{ + grumpkin_scalar::GrumpkinScalar, + option::Option, +}; // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) // use dep::std::collections::vec::Vec; -use crate::abi::{ - PrivateContextInputs, - PublicContextInputs, -}; - -// l1 to l2 messaging -use crate::messaging::process_l1_to_l2_message; - -use crate::types::{ - vec::BoundedVec, -}; - -use crate::utils::Reader; - -use crate::oracle::{ - arguments, - call_private_function::call_private_function_internal, - public_call::call_public_function_internal, - enqueue_public_function_call::enqueue_public_function_call_internal, - context::get_portal_address, - get_block_header::get_block_header, -}; - -use dep::std::option::Option; - // When finished, one can call .finish() to convert back to the abi struct PrivateContext { // docs:start:private-context @@ -201,6 +199,14 @@ impl PrivateContext { self.side_effect_counter = self.side_effect_counter + 1; } + pub fn request_nullifier_secret_key(&mut self, account: AztecAddress) -> GrumpkinScalar { + let key_pair = get_nullifier_key_pair(account); + validate_nullifier_key_against_address(account, key_pair.public_key, key_pair.secret_key); + // TODO: Add request to context. + // self.context.push_nullifier_key_validation_request(public_key, secret_key); + key_pair.secret_key + } + // docs:start:context_message_portal pub fn message_portal(&mut self, content: Field) // docs:end:context_message_portal diff --git a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr index d98a9e87831..30abac51c74 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr @@ -12,8 +12,8 @@ pub fn prove_note_validity( note_interface: NoteInterface, note_with_header: Note, block_number: u32, // The block at which we'll prove that the note exists - context: PrivateContext + context: &mut PrivateContext ) { - prove_note_inclusion(note_interface, note_with_header, block_number, context); + prove_note_inclusion(note_interface, note_with_header, block_number, *context); prove_note_not_nullified(note_interface, note_with_header, block_number, context); } diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr index 47036d2f9f3..f4cb297d17a 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -53,9 +53,9 @@ pub fn prove_note_not_nullified( note_interface: NoteInterface, note_with_header: Note, block_number: u32, // The block at which we'll prove that the note was not nullified - context: PrivateContext + context: &mut PrivateContext ) { - let nullifier = compute_siloed_nullifier(note_interface, note_with_header); + let nullifier = compute_siloed_nullifier(note_interface, note_with_header, context); - prove_nullifier_non_inclusion(nullifier, block_number, context); + prove_nullifier_non_inclusion(nullifier, block_number, *context); } diff --git a/yarn-project/aztec-nr/aztec/src/key.nr b/yarn-project/aztec-nr/aztec/src/key.nr new file mode 100644 index 00000000000..3ea8deca754 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/key.nr @@ -0,0 +1 @@ +mod nullifier_key; diff --git a/yarn-project/aztec-nr/aztec/src/key/nullifier_key.nr b/yarn-project/aztec-nr/aztec/src/key/nullifier_key.nr new file mode 100644 index 00000000000..3f07dba4b2c --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/key/nullifier_key.nr @@ -0,0 +1,24 @@ +use crate::oracle::get_public_key::get_public_key; +use dep::protocol_types::{ + address::AztecAddress, + grumpkin_point::GrumpkinPoint, +}; +use dep::std::{ + grumpkin_scalar::GrumpkinScalar, + grumpkin_scalar_mul::grumpkin_fixed_base, +}; + +pub fn validate_nullifier_key_against_address( + address: AztecAddress, + nullifier_public_key: GrumpkinPoint, + nullifier_secret_key: GrumpkinScalar +) { + // TODO: Nullifier public key should be part of the address. + // Validation of the secret key should happen in the kernel circuit. + let owner_public_key = get_public_key(address); + assert(owner_public_key.x == nullifier_public_key.x); + assert(owner_public_key.y == nullifier_public_key.y); + let computed_public_key = grumpkin_fixed_base(nullifier_secret_key); + assert(owner_public_key.x == computed_public_key[0]); + assert(owner_public_key.y == computed_public_key[1]); +} diff --git a/yarn-project/aztec-nr/aztec/src/lib.nr b/yarn-project/aztec-nr/aztec/src/lib.nr index 39c5b6c290e..9bf7bf42c8f 100644 --- a/yarn-project/aztec-nr/aztec/src/lib.nr +++ b/yarn-project/aztec-nr/aztec/src/lib.nr @@ -2,6 +2,7 @@ mod abi; mod context; mod hash; mod history; +mod key; mod log; mod messaging; mod note; diff --git a/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr b/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr index 0eb21346f8d..ec741e6dbae 100644 --- a/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr +++ b/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr @@ -60,7 +60,7 @@ pub fn destroy_note( let mut nullifier = 0; let mut nullified_commitment: Field = 0; let compute_nullifier = note_interface.compute_nullifier; - nullifier = compute_nullifier(note); + nullifier = compute_nullifier(note, context); // We also need the note commitment corresponding to the "nullifier" let get_header = note_interface.get_header; diff --git a/yarn-project/aztec-nr/aztec/src/note/note_interface.nr b/yarn-project/aztec-nr/aztec/src/note/note_interface.nr index b56d2030645..614c20d2777 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_interface.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_interface.nr @@ -9,7 +9,9 @@ struct NoteInterface { compute_note_hash: fn (Note) -> Field, - compute_nullifier: fn (Note) -> Field, + compute_nullifier: fn (Note, &mut PrivateContext) -> Field, + + compute_nullifier_without_context: fn (Note) -> Field, get_header: fn (Note) -> NoteHeader, diff --git a/yarn-project/aztec-nr/aztec/src/note/utils.nr b/yarn-project/aztec-nr/aztec/src/note/utils.nr index d9286fad123..5d88d904a77 100644 --- a/yarn-project/aztec-nr/aztec/src/note/utils.nr +++ b/yarn-project/aztec-nr/aztec/src/note/utils.nr @@ -3,6 +3,7 @@ use dep::protocol_types::{ hash::pedersen_hash, }; use crate::{ + context::PrivateContext, note::{ note_hash::{compute_inner_hash, compute_siloed_hash, compute_unique_hash}, note_header::NoteHeader, @@ -39,12 +40,16 @@ pub fn compute_unique_siloed_note_hash(note_interface: NoteInterface(note_interface: NoteInterface, note_with_header: Note) -> Field { +pub fn compute_siloed_nullifier( + note_interface: NoteInterface, + note_with_header: Note, + context: &mut PrivateContext +) -> Field { let get_header = note_interface.get_header; let header = get_header(note_with_header); let compute_nullifier = note_interface.compute_nullifier; - let inner_nullifier = compute_nullifier(note_with_header); + let inner_nullifier = compute_nullifier(note_with_header, context); let input = [header.contract_address.to_field(), inner_nullifier]; pedersen_hash(input, GENERATOR_INDEX__OUTER_NULLIFIER) @@ -88,8 +93,8 @@ pub fn compute_note_hash_and_nullifier( let unique_siloed_note_hash = compute_unique_hash(note_header.nonce, siloed_note_hash); - let compute_nullifier = note_interface.compute_nullifier; - let inner_nullifier = compute_nullifier(note); + let compute_nullifier_without_context = note_interface.compute_nullifier_without_context; + let inner_nullifier = compute_nullifier_without_context(note); [inner_note_hash, siloed_note_hash, unique_siloed_note_hash, inner_nullifier] } diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index edd47519d2b..81eb3571a49 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -11,7 +11,7 @@ mod get_nullifier_membership_witness; mod get_public_data_witness; mod get_membership_witness; mod get_public_key; -mod get_secret_key; +mod nullifier_key; mod get_sibling_path; mod rand; mod enqueue_public_function_call; diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_secret_key.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_secret_key.nr deleted file mode 100644 index a4da1c2a585..00000000000 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_secret_key.nr +++ /dev/null @@ -1,25 +0,0 @@ -use crate::oracle::get_public_key::get_public_key; -use dep::protocol_types::{ - address::AztecAddress, - grumpkin_point::GrumpkinPoint, -}; - -#[oracle(getSecretKey)] -fn get_secret_key_oracle(_owner: GrumpkinPoint) -> [Field; dep::std::grumpkin_scalar::GRUMPKIN_SCALAR_SERIALIZED_LEN] {} - -unconstrained fn get_secret_key_internal(owner_public_key: GrumpkinPoint) -> dep::std::grumpkin_scalar::GrumpkinScalar { - dep::std::grumpkin_scalar::deserialize_grumpkin_scalar(get_secret_key_oracle(owner_public_key)) -} - -pub fn get_secret_key(owner: AztecAddress) -> dep::std::grumpkin_scalar::GrumpkinScalar { - let owner_public_key = get_public_key(owner); - let secret = get_secret_key_internal(owner_public_key); - - // Constrain the owner - Nullifier secret key is currently just the encryption private key so we can constrain - // the owner by deriving the public key from the secret key and checking the result. - let computed_public_key = dep::std::grumpkin_scalar_mul::grumpkin_fixed_base(secret); - assert(owner_public_key.x == computed_public_key[0]); - assert(owner_public_key.y == computed_public_key[1]); - - secret -} diff --git a/yarn-project/aztec-nr/aztec/src/oracle/nullifier_key.nr b/yarn-project/aztec-nr/aztec/src/oracle/nullifier_key.nr new file mode 100644 index 00000000000..b97b2d651a3 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/oracle/nullifier_key.nr @@ -0,0 +1,29 @@ +use dep::protocol_types::{ + address::AztecAddress, + grumpkin_point::GrumpkinPoint, +}; +use dep::std::grumpkin_scalar::GrumpkinScalar; + +struct KeyPair { + public_key: GrumpkinPoint, + secret_key: GrumpkinScalar, +} + +#[oracle(getNullifierKeyPair)] +fn get_nullifier_key_pair_oracle(_account: AztecAddress) -> [Field; 4] {} + +unconstrained fn get_nullifier_key_pair_internal(account: AztecAddress) -> KeyPair { + let result = get_nullifier_key_pair_oracle(account); + KeyPair { + public_key: GrumpkinPoint { x: result[0], y: result[1] }, + secret_key: GrumpkinScalar { high: result[2], low: result[3] } + } +} + +pub fn get_nullifier_key_pair(account: AztecAddress) -> KeyPair { + get_nullifier_key_pair_internal(account) +} + +pub fn get_nullifier_secret_key(account: AztecAddress) -> GrumpkinScalar { + get_nullifier_key_pair_internal(account).secret_key +} diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr b/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr index e87555d5a82..699cecae250 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr @@ -18,7 +18,7 @@ struct ImmutableSingleton { context: Option<&mut PrivateContext>, storage_slot: Field, note_interface: NoteInterface, - compute_initialization_nullifier: fn (Field, Option) -> Field, + compute_initialization_nullifier: fn (Field, Option, Option<&mut PrivateContext>) -> Field, } // docs:end:struct @@ -42,7 +42,7 @@ impl ImmutableSingleton { // docs:start:is_initialized unconstrained pub fn is_initialized(self, owner: Option) -> bool { let compute_initialization_nullifier = self.compute_initialization_nullifier; - let nullifier = compute_initialization_nullifier(self.storage_slot, owner); + let nullifier = compute_initialization_nullifier(self.storage_slot, owner, Option::none()); check_nullifier_exists(nullifier) } // docs:end:is_initialized @@ -58,7 +58,7 @@ impl ImmutableSingleton { // Nullify the storage slot. let compute_initialization_nullifier = self.compute_initialization_nullifier; - let nullifier = compute_initialization_nullifier(self.storage_slot, owner); + let nullifier = compute_initialization_nullifier(self.storage_slot, owner, self.context); context.push_new_nullifier(nullifier, 0); create_note( diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr b/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr index 28cd3ae23b8..d564512a1ab 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr @@ -16,13 +16,21 @@ use crate::note::{ note_viewer_options::NoteViewerOptions, }; use crate::oracle::{ - get_secret_key::get_secret_key, + nullifier_key::get_nullifier_secret_key, notes::check_nullifier_exists, }; -pub fn compute_singleton_initialization_nullifier(storage_slot: Field, owner: Option) -> Field { +pub fn compute_singleton_initialization_nullifier( + storage_slot: Field, + owner: Option, + context: Option<&mut PrivateContext> +) -> Field { if owner.is_some() { - let secret = get_secret_key(owner.unwrap_unchecked()); + let secret = if context.is_some() { + context.unwrap_unchecked().request_nullifier_secret_key(owner.unwrap_unchecked()) + } else { + get_nullifier_secret_key(owner.unwrap_unchecked()) + }; pedersen_hash( [storage_slot, secret.low, secret.high], GENERATOR_INDEX__INITIALIZATION_NULLIFIER @@ -37,7 +45,7 @@ struct Singleton { context: Option<&mut PrivateContext>, storage_slot: Field, note_interface: NoteInterface, - compute_initialization_nullifier: fn (Field, Option) -> Field, + compute_initialization_nullifier: fn (Field, Option, Option<&mut PrivateContext>) -> Field, } // docs:end:struct @@ -61,7 +69,7 @@ impl Singleton { // docs:start:is_initialized unconstrained pub fn is_initialized(self, owner: Option) -> bool { let compute_initialization_nullifier = self.compute_initialization_nullifier; - let nullifier = compute_initialization_nullifier(self.storage_slot, owner); + let nullifier = compute_initialization_nullifier(self.storage_slot, owner, Option::none()); check_nullifier_exists(nullifier) } // docs:end:is_initialized @@ -77,7 +85,7 @@ impl Singleton { // Nullify the storage slot. let compute_initialization_nullifier = self.compute_initialization_nullifier; - let nullifier = compute_initialization_nullifier(self.storage_slot, owner); + let nullifier = compute_initialization_nullifier(self.storage_slot, owner, self.context); context.push_new_nullifier(nullifier, 0); create_note(context, self.storage_slot, note, self.note_interface, broadcast); diff --git a/yarn-project/aztec-nr/field-note/src/field_note.nr b/yarn-project/aztec-nr/field-note/src/field_note.nr index 6267a7b6016..8aa8fbe5520 100644 --- a/yarn-project/aztec-nr/field-note/src/field_note.nr +++ b/yarn-project/aztec-nr/field-note/src/field_note.nr @@ -41,7 +41,12 @@ impl FieldNote { pedersen_hash(self.serialize(), 0) } - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(_self: Self, _context: &mut PrivateContext) -> Field { + // This note is expected to be shared between users and for this reason can't be nullified using a secret. + 0 + } + + pub fn compute_nullifier_without_context(_self: Self) -> Field { // This note is expected to be shared between users and for this reason can't be nullified using a secret. 0 } @@ -63,8 +68,12 @@ fn compute_note_hash(note: FieldNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: FieldNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: FieldNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: FieldNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: FieldNote) -> NoteHeader { @@ -86,6 +95,7 @@ global FieldNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/aztec-nr/value-note/src/value_note.nr b/yarn-project/aztec-nr/value-note/src/value_note.nr index d02037e4998..4cffea0d4fa 100644 --- a/yarn-project/aztec-nr/value-note/src/value_note.nr +++ b/yarn-project/aztec-nr/value-note/src/value_note.nr @@ -7,7 +7,7 @@ use dep::aztec::{ }, oracle::{ rand::rand, - get_secret_key::get_secret_key, + nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key, }, log::emit_encrypted_log, @@ -58,9 +58,9 @@ impl ValueNote { // docs:start:nullifier - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(ValueNoteMethods, self); - let secret = get_secret_key(self.owner); + let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ note_hash_for_nullify, @@ -71,6 +71,17 @@ impl ValueNote { // docs:end:nullifier + pub fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(ValueNoteMethods, self); + let secret = get_nullifier_secret_key(self.owner); + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash([ + note_hash_for_nullify, + secret.low, + secret.high, + ],0) + } + pub fn set_header(&mut self, header: NoteHeader) { self.header = header; } @@ -100,8 +111,12 @@ fn compute_note_hash(note: ValueNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: ValueNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: ValueNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: ValueNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: ValueNote) -> NoteHeader { @@ -122,6 +137,7 @@ global ValueNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/circuit-types/src/keys/key_store.ts b/yarn-project/circuit-types/src/keys/key_store.ts index 682aa89862e..d9da2be7d50 100644 --- a/yarn-project/circuit-types/src/keys/key_store.ts +++ b/yarn-project/circuit-types/src/keys/key_store.ts @@ -1,4 +1,4 @@ -import { GrumpkinPrivateKey, PublicKey } from '@aztec/circuits.js'; +import { AztecAddress, GrumpkinPrivateKey, PublicKey } from '@aztec/circuits.js'; /** * Represents a secure storage for managing keys. @@ -36,4 +36,29 @@ export interface KeyStore { * @deprecated We should not require a keystore to expose private keys in plain. */ getAccountPrivateKey(pubKey: PublicKey): Promise; + + /** + * Retrieves the nullifier secret key of the account associated with the specified AztecAddress. + * Throws an error if the provided public key is not found in the list of registered accounts. + * @param pubKey - The public key of the account for which the secret key is requested. + * @returns A Promise that resolves to the nullifier secret key. + */ + getNullifierSecretKey(pubKey: PublicKey): Promise; + + /** + * Retrieves the nullifier public key of the account associated with the specified AztecAddress. + * Throws an error if the provided public key is not found in the list of registered accounts. + * @param pubKey - The public key of the account for which the nullifier public key is requested. + * @returns A Promise that resolves to the nullifier public key. + */ + getNullifierPublicKey(pubKey: PublicKey): Promise; + + /** + * Retrieves the nullifier secret key for use in a specific contract. + * Throws an error if the provided public key is not found in the list of registered accounts. + * @param pubKey - The public key of the account for which the private key is requested. + * @param contractAddress - The address of the contract requesting the nullifier key. + * @returns A Promise that resolves to the nullifier secret key. + */ + getSiloedNullifierSecretKey(pubKey: PublicKey, contractAddress: AztecAddress): Promise; } diff --git a/yarn-project/circuits.js/src/index.ts b/yarn-project/circuits.js/src/index.ts index 91e7dd069b3..347db88ae51 100644 --- a/yarn-project/circuits.js/src/index.ts +++ b/yarn-project/circuits.js/src/index.ts @@ -1,4 +1,5 @@ export * from './structs/index.js'; export * from './contract/index.js'; +export * from './keys/index.js'; export * from './types/index.js'; export * from './constants.gen.js'; diff --git a/yarn-project/circuits.js/src/keys/index.ts b/yarn-project/circuits.js/src/keys/index.ts new file mode 100644 index 00000000000..09ec99767c4 --- /dev/null +++ b/yarn-project/circuits.js/src/keys/index.ts @@ -0,0 +1,44 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { pedersenHash } from '@aztec/foundation/crypto'; +import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; + +import { Grumpkin } from '../barretenberg/crypto/grumpkin/index.js'; +import { GrumpkinPrivateKey } from '../types/grumpkin_private_key.js'; + +/** + * Derives the public key of a secret key. + */ +export function derivePublicKey(secretKey: GrumpkinPrivateKey) { + const grumpkin = new Grumpkin(); + return grumpkin.mul(grumpkin.generator(), secretKey); +} + +/** + * Derives a new secret key from a secret key and an index. + */ +function _deriveSecretKey(secretKey: GrumpkinPrivateKey, index: Fr): GrumpkinPrivateKey { + // TODO: Temporary hack. Should replace it with a secure way to derive the secret key. + const hash = pedersenHash([secretKey.high, secretKey.low, index].map(v => v.toBuffer())); + return new GrumpkinScalar(hash); +} + +/** + * Computes the nullifier secret key from seed secret key. + */ +export function computeNullifierSecretKey(seedSecretKey: GrumpkinPrivateKey): GrumpkinPrivateKey { + // TODO + // return deriveSecretKey(seedSecretKey, new Fr(1)); + return seedSecretKey; +} + +/** + * Computes the nullifier secret key for a contract. + */ +export function computeSiloedNullifierSecretKey( + nullifierSecretKey: GrumpkinPrivateKey, + _contractAddress: AztecAddress, +): GrumpkinPrivateKey { + // TODO + // return deriveSecretKey(nullifierSecretKey, contractAddress); + return nullifierSecretKey; +} diff --git a/yarn-project/circuits.js/src/types/grumpkin_private_key.ts b/yarn-project/circuits.js/src/types/grumpkin_private_key.ts index 8a394773730..16ed3679587 100644 --- a/yarn-project/circuits.js/src/types/grumpkin_private_key.ts +++ b/yarn-project/circuits.js/src/types/grumpkin_private_key.ts @@ -1,4 +1,4 @@ -import { GrumpkinScalar } from '../index.js'; +import { GrumpkinScalar } from '@aztec/foundation/fields'; /** A type alias for private key which belongs to the scalar field of Grumpkin curve. */ export type GrumpkinPrivateKey = GrumpkinScalar; diff --git a/yarn-project/key-store/src/test_key_store.ts b/yarn-project/key-store/src/test_key_store.ts index 1a5a96c2d1b..f0af31ee6da 100644 --- a/yarn-project/key-store/src/test_key_store.ts +++ b/yarn-project/key-store/src/test_key_store.ts @@ -1,5 +1,13 @@ import { KeyPair, KeyStore, PublicKey } from '@aztec/circuit-types'; -import { GrumpkinPrivateKey, GrumpkinScalar, Point } from '@aztec/circuits.js'; +import { + AztecAddress, + GrumpkinPrivateKey, + GrumpkinScalar, + Point, + computeNullifierSecretKey, + computeSiloedNullifierSecretKey, + derivePublicKey, +} from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { AztecKVStore, AztecMap } from '@aztec/kv-store'; @@ -38,6 +46,21 @@ export class TestKeyStore implements KeyStore { return Promise.resolve(account.getPrivateKey()); } + public async getNullifierSecretKey(pubKey: PublicKey) { + const privateKey = await this.getAccountPrivateKey(pubKey); + return computeNullifierSecretKey(privateKey); + } + + public async getNullifierPublicKey(pubKey: PublicKey) { + const secretKey = await this.getNullifierSecretKey(pubKey); + return derivePublicKey(secretKey); + } + + public async getSiloedNullifierSecretKey(pubKey: PublicKey, contractAddress: AztecAddress) { + const secretKey = await this.getNullifierSecretKey(pubKey); + return computeSiloedNullifierSecretKey(secretKey, contractAddress); + } + /** * Retrieve the KeyPair object associated with a given pub key. * Searches through the 'accounts' array for a matching public key and returns the corresponding account (KeyPair). diff --git a/yarn-project/noir-contracts/contracts/card_game_contract/src/cards.nr b/yarn-project/noir-contracts/contracts/card_game_contract/src/cards.nr index 6672ad559bc..b0b17ef7ac1 100644 --- a/yarn-project/noir-contracts/contracts/card_game_contract/src/cards.nr +++ b/yarn-project/noir-contracts/contracts/card_game_contract/src/cards.nr @@ -12,7 +12,6 @@ use dep::aztec::{ note_viewer_options::NoteViewerOptions, note_getter::view_notes, }, - oracle::get_secret_key::get_secret_key, state_vars::set::Set, }; use dep::std; @@ -196,9 +195,9 @@ impl Deck { global PACK_CARDS = 3; // Limited by number of write requests (max 4) -pub fn get_pack_cards(seed: Field, owner: AztecAddress) -> [Card; PACK_CARDS] { +pub fn get_pack_cards(seed: Field, owner: AztecAddress, context: &mut PrivateContext) -> [Card; PACK_CARDS] { // generate pseudo randomness deterministically from 'seed' and user secret - let secret = get_secret_key(owner); + let secret = context.request_nullifier_secret_key(owner); let mix = secret.high + secret.low + seed; let random_bytes = std::hash::sha256(mix.to_le_bytes(32)); diff --git a/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr b/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr index f0f85b6e0cd..4aacb388d9a 100644 --- a/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr @@ -112,7 +112,7 @@ contract CardGame { fn buy_pack(seed: Field // The randomness used to generate the cards. Passed in for now. ) { let buyer = context.msg_sender(); - let mut cards = get_pack_cards(seed, buyer); + let mut cards = get_pack_cards(seed, buyer, &mut context); let mut collection = storage.collections.at(buyer); let _inserted_cards = collection.add_cards(cards, buyer); diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/account_contract_interface.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/account_contract_interface.nr deleted file mode 100644 index 0701b4819be..00000000000 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/account_contract_interface.nr +++ /dev/null @@ -1,11 +0,0 @@ -struct AccountContractInterface { - address: Field, -} - -impl AccountContractInterface { - pub fn at(address: Field) -> Self { - AccountContractInterface { address } - } - - pub fn send_rewards(_self: Self, _rewards: u8) {} -} diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/actions.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/actions.nr index 9c9423f31ea..e827e1cdede 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/actions.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/actions.nr @@ -1,70 +1,15 @@ -use dep::aztec::protocol_types::{ - address::AztecAddress, - constants::{MAX_NOTES_PER_PAGE, MAX_READ_REQUESTS_PER_CALL}, -}; -use dep::aztec::note::{ - note_getter_options::NoteGetterOptions, note_viewer_options::NoteViewerOptions, -}; use dep::aztec::state_vars::{ - immutable_singleton::ImmutableSingleton, map::Map, public_state::PublicState, set::Set, singleton::Singleton, }; -use dep::aztec::types::type_serialization::bool_serialization::BOOL_SERIALIZED_LEN; use dep::std::option::Option; use crate::types::{ card_note::{CardNote, CARD_NOTE_LEN}, - profile_note::{ProfileNote, PROFILE_NOTE_LEN}, - queen::{Queen, QUEEN_SERIALIZED_LEN}, - rules_note::{RulesNote, RULES_NOTE_LEN}, }; -// docs:start:state_vars-PublicStateRead -pub fn is_locked(state_var: PublicState) -> bool { - state_var.read() -} -// docs:end:state_vars-PublicStateRead - -// docs:start:state_vars-PublicStateWrite -pub fn lock(state_var: PublicState) { - state_var.write(true); -} -// docs:end:state_vars-PublicStateWrite - -pub fn unlock(state_var: PublicState) { - state_var.write(false); -} - -// docs:start:state_vars-PublicStateReadCustom -pub fn get_current_queen(state_var: PublicState) -> Queen { - state_var.read() -} -// docs:end:state_vars-PublicStateReadCustom - -pub fn can_replace_queen(state_var: PublicState, new_queen: Queen) -> bool { - let current_queen = get_current_queen(state_var); - new_queen.points > current_queen.points -} - -// docs:start:state_vars-PublicStateWriteCustom -pub fn replace_queen(state_var: PublicState, new_queen: Queen) { - state_var.write(new_queen); -} -// docs:end:state_vars-PublicStateWriteCustom - -// docs:start:state_vars-PublicStateReadWriteCustom -pub fn add_points_to_queen(state_var: PublicState, new_points: u8) { - let mut queen = state_var.read(); - queen.points += new_points; - state_var.write(queen); -} -// docs:end:state_vars-PublicStateReadWriteCustom - -// docs:start:state_vars-SingletonInit pub fn init_legendary_card(state_var: Singleton, card: &mut CardNote) { state_var.initialize(card, Option::some(card.owner), true); } -// docs:end:state_vars-SingletonInit // docs:start:state_vars-SingletonReplace pub fn update_legendary_card(state_var: Singleton, card: &mut CardNote) { @@ -77,80 +22,3 @@ pub fn get_legendary_card(state_var: Singleton) -> Card state_var.get_note(true) } // docs:end:state_vars-SingletonGet - -// docs:start:state_vars-ImmutableSingletonInit -pub fn init_game_rules(state_var: ImmutableSingleton, rules: &mut RulesNote) { - state_var.initialize(rules, Option::none(), true); -} -// docs:end:state_vars-ImmutableSingletonInit - -// docs:start:state_vars-ImmutableSingletonGet -pub fn is_valid_card(state_var: ImmutableSingleton, card: CardNote) -> bool { - let rules = state_var.get_note(); - card.points >= rules.min_points & card.points <= rules.max_points -} -// docs:end:state_vars-ImmutableSingletonGet - -// docs:start:state_vars-SetInsert -pub fn add_new_card(state_var: Set, card: &mut CardNote) { - state_var.insert(card, true); -} -// docs:end:state_vars-SetInsert - -// docs:start:state_vars-SetRemove -pub fn remove_card(state_var: Set, card: CardNote) { - state_var.remove(card); -} -// docs:end:state_vars-SetRemove - -// docs:start:state_vars-SetGet -pub fn get_cards( - state_var: Set, - options: NoteGetterOptions -) -> [Option; MAX_READ_REQUESTS_PER_CALL] { - state_var.get_notes(options) -} -// docs:end:state_vars-SetGet - -// docs:start:state_vars-SetView -unconstrained pub fn view_cards( - state_var: Set, - options: NoteViewerOptions -) -> [Option; MAX_NOTES_PER_PAGE] { - state_var.view_notes(options) -} -// docs:end:state_vars-SetView - -unconstrained pub fn get_total_points(state_var: Set, account: AztecAddress, offset: u32) -> u8 { - let options = NoteViewerOptions::new().select(2, account.to_field()).set_offset(offset); - let mut total_points = 0; - let notes = view_cards(state_var, options); - for i in 0..notes.len() { - if notes[i].is_some() { - total_points += notes[i].unwrap_unchecked().points; - } - } - if notes[notes.len() - 1].is_some() { - total_points += get_total_points(state_var, account, offset + notes.len() as u32); - } - total_points -} - -// docs:start:state_vars-MapAtSingletonInit -pub fn add_new_profile( - state_var: Map>, - account: AztecAddress, - profile: &mut ProfileNote -) { - state_var.at(account).initialize(profile, Option::some(account), true); -} -// docs:end:state_vars-MapAtSingletonInit - -// docs:start:state_vars-MapAtSingletonGet -pub fn get_profile( - state_var: Map>, - account: AztecAddress -) -> ProfileNote { - state_var.at(account).get_note(true) -} -// docs:end:state_vars-MapAtSingletonGet diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr index 8870c297267..3287e4df905 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -1,4 +1,3 @@ -mod account_contract_interface; mod actions; mod options; mod types; @@ -6,205 +5,65 @@ mod types; contract DocsExample { use dep::aztec::protocol_types::{ address::AztecAddress, - abis::function_selector::FunctionSelector, }; - use dep::std::option::Option; use dep::aztec::{ - context::{PrivateContext, PublicContext, Context}, + context::{PrivateContext, Context}, state_vars::{ - immutable_singleton::ImmutableSingleton, map::Map, public_state::PublicState, set::Set, + map::Map, singleton::Singleton, }, }; - // docs:start:state_vars-PublicStateBoolImport - use dep::aztec::types::type_serialization::bool_serialization::{ - BoolSerializationMethods, BOOL_SERIALIZED_LEN, - }; - // docs:end:state_vars-PublicStateBoolImport - use crate::account_contract_interface::AccountContractInterface; use crate::actions; use crate::options::create_account_card_getter_options; use crate::types::{ card_note::{CardNote, CardNoteMethods, CARD_NOTE_LEN}, - profile_note::{ProfileNote, ProfileNoteMethods, PROFILE_NOTE_LEN}, - queen::{Queen, QueenSerializationMethods, QUEEN_SERIALIZED_LEN}, - rules_note::{RulesNote, RulesNoteMethods, RULES_NOTE_LEN}, }; - // docs:start:storage-struct-declaration struct Storage { - locked: PublicState, - queen: PublicState, - game_rules: ImmutableSingleton, // docs:start:storage-singleton-declaration legendary_card: Singleton, // docs:end:storage-singleton-declaration - cards: Set, // docs:start:storage-map-singleton-declaration - profiles: Map>, + profiles: Map>, // docs:end:storage-map-singleton-declaration } - // docs:end:storage-struct-declaration - // docs:start:storage-declaration - // docs:start:state_vars-PublicState - // docs:start:state_vars-PublicStateCustomStruct - // docs:start:state_vars-Singleton - // docs:start:state_vars-ImmutableSingleton - // docs:start:state_vars-Set // docs:start:state_vars-MapSingleton impl Storage { fn init(context: Context) -> Self { Storage { - // highlight-next-line:state_vars-PublicState - locked: PublicState::new(context, 1, BoolSerializationMethods), - // highlight-next-line:state_vars-PublicStateCustomStruct - queen: PublicState::new( - context, - 2, - QueenSerializationMethods, - ), - // highlight-next-line:state_vars-ImmutableSingleton - game_rules: ImmutableSingleton::new(context, 3, RulesNoteMethods), - // highlight-next-line:state_vars-Singleton // docs:start:start_vars_singleton - legendary_card: Singleton::new(context, 4, CardNoteMethods), + legendary_card: Singleton::new(context, 1, CardNoteMethods), // docs:end:start_vars_singleton - // highlight-next-line:state_vars-Set - cards: Set::new(context, 5, CardNoteMethods), // highlight-next-line:state_vars-MapSingleton profiles: Map::new( context, - 6, + 2, |context, slot| { - Singleton::new(context, slot, ProfileNoteMethods) + Singleton::new(context, slot, CardNoteMethods) }, ), } } } - // docs:end:state_vars-PublicState - // docs:end:state_vars-PublicStateCustomStruct - // docs:end:state_vars-Singleton - // docs:end:state_vars-ImmutableSingleton - // docs:end:state_vars-Set // docs:end:state_vars-MapSingleton - // docs:end:storage-declaration - - global REPLACE_QUEEN_FUNCTION_SELECTOR = 11111111; - global GET_POINTS_OF_COMMON_CARD_FUNCTION_SELECTOR = 11111111; #[aztec(private)] - fn constructor(min_points: u8, max_points: u8, legendary_card_secret: Field) { - let mut game_rules = RulesNote::new(min_points, max_points, Option::some(AztecAddress::zero())); - actions::init_game_rules(storage.game_rules, &mut game_rules); - + fn constructor(legendary_card_secret: Field) { let mut legendary_card = CardNote::new(0, legendary_card_secret, AztecAddress::zero()); actions::init_legendary_card(storage.legendary_card, &mut legendary_card); } - // docs:start:storage-init - #[aztec(public)] - fn lock() { - // highlight-next-line:storage-init - - storage.locked.write(true); - } - // docs:end:storage-init - - // docs:start:functions-OpenFunction - #[aztec(public)] - fn unlock() { - actions::unlock(storage.locked); - } - // docs:end:functions-OpenFunction - - #[aztec(public)] - fn replace_queen(account: AztecAddress, points: u8) { - let new_queen = Queen { account, points }; - - assert(actions::can_replace_queen(storage.queen, new_queen)); - - actions::replace_queen(storage.queen, new_queen); - } - - // docs:start:state_vars-PublicStateWriteBeforeCall - #[aztec(public)] - fn replace_queen_unsafe() { - let account = context.msg_sender(); - let points = actions::get_total_points(storage.cards, account, 0); - - let current_queen = storage.queen.read(); - assert(!account.eq(current_queen.account)); - assert(points > current_queen.points); - - AccountContractInterface::at(account.to_field()).send_rewards(current_queen.points); - - let new_queen = Queen { account, points }; - storage.queen.write(new_queen); - } - // docs:end:state_vars-PublicStateWriteBeforeCall - - // docs:start:functions-SecretFunction - #[aztec(private)] - fn add_common_cards(secrets: [Field; 4]) { - for i in 0..secrets.len() as u8 { - let mut card = CardNote::new(0, secrets[i], AztecAddress::zero()); - actions::add_new_card(storage.cards, &mut card); - } - } - // docs:end:functions-SecretFunction - #[aztec(private)] fn update_legendary_card(new_points: u8, new_secret: Field) { let owner = inputs.call_context.msg_sender; let mut updated_card = CardNote::new(new_points, new_secret, owner); - - assert(actions::is_valid_card(storage.game_rules, updated_card)); - actions::update_legendary_card(storage.legendary_card, &mut updated_card); } - #[aztec(private)] - fn become_queen() { - let legendary_card = actions::get_legendary_card(storage.legendary_card); - - let owner = legendary_card.owner; - let result = context.call_private_function( - inputs.call_context.storage_contract_address, - FunctionSelector::from_field(GET_POINTS_OF_COMMON_CARD_FUNCTION_SELECTOR), - [owner.to_field(), 0] - ); - let total_points = legendary_card.points + result[0] as u8; - - context.call_public_function( - inputs.call_context.storage_contract_address, - FunctionSelector::from_field(REPLACE_QUEEN_FUNCTION_SELECTOR), - [owner.to_field(), total_points as Field] - ); - } - - #[aztec(private)] - fn get_points_of_common_cards(account: AztecAddress, offset: u32) { - let mut total_points = 0; - let options = create_account_card_getter_options(account, offset); - let cards = actions::get_cards(storage.cards, options); - for i in 0..cards.len() { - if (cards[i].is_some()) { - let card = cards[i].unwrap_unchecked(); - assert(card.owner.eq(account)); - total_points += card.points; - } - } - - context.return_values.push(total_points as Field); - } - - // docs:start:functions-UnconstrainedFunction - unconstrained fn get_total_points(account: AztecAddress) -> pub u8 { - actions::get_total_points(storage.cards, account, 0) + unconstrained fn get_legendary_card() -> pub CardNote { + actions::get_legendary_card(storage.legendary_card) } - // docs:end:functions-UnconstrainedFunction /// Macro equivalence section use dep::aztec::abi; diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types.nr index b8bf6dc7bfd..08035046df2 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types.nr @@ -1,4 +1 @@ mod card_note; -mod profile_note; -mod queen; -mod rules_note; diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index 97da78e6519..ba383755ca0 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -6,7 +6,7 @@ use dep::aztec::{ utils::compute_note_hash_for_read_or_nullify, }, oracle::{ - get_secret_key::get_secret_key, + nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key, }, log::emit_encrypted_log, @@ -56,9 +56,19 @@ impl CardNote { ],0) } - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self); - let secret = get_secret_key(self.owner); + let secret = context.request_nullifier_secret_key(self.owner); + pedersen_hash([ + note_hash_for_nullify, + secret.high, + secret.low, + ],0) + } + + pub fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self); + let secret = get_nullifier_secret_key(self.owner); pedersen_hash([ note_hash_for_nullify, secret.high, @@ -95,8 +105,12 @@ fn compute_note_hash(note: CardNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: CardNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: CardNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: CardNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: CardNote) -> NoteHeader { @@ -117,6 +131,7 @@ global CardNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/profile_note.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/profile_note.nr deleted file mode 100644 index 201c796c805..00000000000 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/profile_note.nr +++ /dev/null @@ -1,119 +0,0 @@ -use dep::std::option::Option; -use dep::aztec::{ - protocol_types::address::AztecAddress, - note::{ - note_header::NoteHeader, - note_interface::NoteInterface, - }, - oracle::get_public_key::get_public_key, - log::emit_encrypted_log, - hash::pedersen_hash, - context::PrivateContext, -}; - -global PROFILE_NOTE_LEN: Field = 2; - -struct ProfileNote { - avatar: Field, - xp: Field, - maybe_owner: Option, - header: NoteHeader, -} - -impl ProfileNote { - pub fn new(avatar: Field, xp: Field, maybe_owner: Option) -> Self { - ProfileNote { - avatar, - xp, - maybe_owner, - header: NoteHeader::empty(), - } - } - - pub fn serialize(self) -> [Field; PROFILE_NOTE_LEN] { - [self.avatar, self.xp] - } - - pub fn deserialize(serialized_note: [Field; PROFILE_NOTE_LEN]) -> Self { - ProfileNote { - avatar: serialized_note[0], - xp: serialized_note[1], - maybe_owner: Option::none(), - header: NoteHeader::empty(), - } - } - - pub fn compute_note_hash(self) -> Field { - pedersen_hash([ - self.avatar, - self.xp, - ],0) - } - - pub fn compute_nullifier(_self: Self) -> Field { - assert(false); // Not allowed. - 0 - } - - pub fn set_header(&mut self, header: NoteHeader) { - self.header = header; - } - - pub fn set_owner(&mut self, owner: AztecAddress) { - self.maybe_owner = Option::some(owner); - } - - // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { - assert(self.maybe_owner.is_some(), "Note owner must be set when the broadcast flow is triggered."); - let owner = self.maybe_owner.unwrap_unchecked(); - - let encryption_pub_key = get_public_key(owner); - emit_encrypted_log( - context, - (*context).this_address(), - slot, - encryption_pub_key, - self.serialize(), - ); - } -} - -fn deserialize(serialized_note: [Field; PROFILE_NOTE_LEN]) -> ProfileNote { - ProfileNote::deserialize(serialized_note) -} - -fn serialize(note: ProfileNote) -> [Field; PROFILE_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: ProfileNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: ProfileNote) -> Field { - note.compute_nullifier() -} - -fn get_header(note: ProfileNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut ProfileNote, header: NoteHeader) { - note.set_header(header) -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: ProfileNote) { - note.broadcast(context, slot); -} - -global ProfileNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/queen.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/queen.nr deleted file mode 100644 index acd4ed0f7ca..00000000000 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/queen.nr +++ /dev/null @@ -1,26 +0,0 @@ -use dep::aztec::protocol_types::address::AztecAddress; -use dep::aztec::types::type_serialization::TypeSerializationInterface; - -// docs:start:state_vars-CustomStruct -struct Queen { - account: AztecAddress, - points: u8, -} -// docs:end:state_vars-CustomStruct - -// docs:start:state_vars-PublicStateCustomStruct -global QUEEN_SERIALIZED_LEN: Field = 2; - -fn deserialize(fields: [Field; QUEEN_SERIALIZED_LEN]) -> Queen { - Queen { account: AztecAddress::from_field(fields[0]), points: fields[1] as u8 } -} - -fn serialize(queen: Queen) -> [Field; QUEEN_SERIALIZED_LEN] { - [queen.account.to_field(), queen.points as Field] -} - -global QueenSerializationMethods = TypeSerializationInterface { - deserialize, - serialize, -}; -// docs:end:state_vars-PublicStateCustomStruct diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/rules_note.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/rules_note.nr deleted file mode 100644 index d3abaecb5d1..00000000000 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/rules_note.nr +++ /dev/null @@ -1,119 +0,0 @@ -use dep::std::option::Option; -use dep::aztec::{ - protocol_types::address::AztecAddress, - note::{ - note_header::NoteHeader, - note_interface::NoteInterface, - }, - oracle::get_public_key::get_public_key, - log::emit_encrypted_log, - hash::pedersen_hash, - context::PrivateContext, -}; - -global RULES_NOTE_LEN: Field = 2; - -struct RulesNote { - min_points: u8, - max_points: u8, - maybe_owner: Option, - header: NoteHeader, -} - -impl RulesNote { - pub fn new(min_points: u8, max_points: u8, maybe_owner: Option) -> Self { - RulesNote { - min_points, - max_points, - maybe_owner, - header: NoteHeader::empty(), - } - } - - pub fn serialize(self) -> [Field; RULES_NOTE_LEN] { - [self.min_points as Field, self.max_points as Field] - } - - pub fn deserialize(serialized_note: [Field; RULES_NOTE_LEN]) -> Self { - RulesNote { - min_points: serialized_note[0] as u8, - max_points: serialized_note[1] as u8, - maybe_owner: Option::none(), - header: NoteHeader::empty(), - } - } - - pub fn compute_note_hash(self) -> Field { - pedersen_hash([ - self.min_points as Field, - self.max_points as Field, - ],0) - } - - pub fn compute_nullifier(_self: Self) -> Field { - // Not used - 0 - } - - pub fn set_header(&mut self, header: NoteHeader) { - self.header = header; - } - - pub fn set_owner(&mut self, owner: AztecAddress) { - self.maybe_owner = Option::some(owner); - } - - // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { - assert(self.maybe_owner.is_some(), "Note owner must be set when the broadcast flow is triggered."); - let owner = self.maybe_owner.unwrap_unchecked(); - - let encryption_pub_key = get_public_key(owner); - emit_encrypted_log( - context, - (*context).this_address(), - slot, - encryption_pub_key, - self.serialize(), - ); - } -} - -fn deserialize(serialized_note: [Field; RULES_NOTE_LEN]) -> RulesNote { - RulesNote::deserialize(serialized_note) -} - -fn serialize(note: RulesNote) -> [Field; RULES_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: RulesNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: RulesNote) -> Field { - note.compute_nullifier() -} - -fn get_header(note: RulesNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut RulesNote, header: NoteHeader) { - note.set_header(header) -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: RulesNote) { - note.broadcast(context, slot); -} - -global RulesNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr b/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr index 54db9e4017a..ceddb93c70c 100644 --- a/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr @@ -6,7 +6,6 @@ contract EasyPrivateVoting { address::AztecAddress, }, context::{PrivateContext, Context}, - oracle::get_secret_key::get_secret_key, // used to compute nullifier state_vars::{ map::Map, public_state::PublicState,}, types::type_serialization::{ // serialization methods for using booleans and aztec addresses bool_serialization::{BoolSerializationMethods, BOOL_SERIALIZED_LEN}, @@ -71,7 +70,7 @@ contract EasyPrivateVoting { // docs:start:cast_vote #[aztec(private)] // annotation to mark function as private and expose private context fn cast_vote(candidate: Field) { - let secret = get_secret_key(context.msg_sender()); // get secret key of caller of function + let secret = context.request_nullifier_secret_key(context.msg_sender()); // get secret key of caller of function let nullifier = dep::std::hash::pedersen_hash([context.msg_sender().to_field(), secret.low, secret.high]); // compute nullifier with this secret key so others can't descrypt it context.push_new_nullifier(nullifier, 0); // push nullifier context.call_public_function( diff --git a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index c0aea096b20..779c8debecc 100644 --- a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -6,7 +6,7 @@ use dep::aztec::{ utils::compute_unique_siloed_note_hash, }, oracle::{ - get_secret_key::get_secret_key, + nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key, }, log::emit_encrypted_log, @@ -60,9 +60,20 @@ impl EcdsaPublicKeyNote { [x, last_x, y, last_y, self.owner.to_field()] } - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let unique_siloed_note_hash = compute_unique_siloed_note_hash(EcdsaPublicKeyNoteInterface, self); - let secret = get_secret_key(self.owner); + let secret = context.request_nullifier_secret_key(self.owner); + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash([ + unique_siloed_note_hash, + secret.low, + secret.high, + ],0) + } + + pub fn compute_nullifier_without_context(self) -> Field { + let unique_siloed_note_hash = compute_unique_siloed_note_hash(EcdsaPublicKeyNoteInterface, self); + let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ unique_siloed_note_hash, @@ -116,8 +127,12 @@ fn compute_note_hash(note: EcdsaPublicKeyNote) -> Field { pedersen_hash(note.serialize(), 0) } -fn compute_nullifier(note: EcdsaPublicKeyNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: EcdsaPublicKeyNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: EcdsaPublicKeyNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: EcdsaPublicKeyNote) -> NoteHeader { @@ -138,6 +153,7 @@ global EcdsaPublicKeyNoteInterface = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index f0882caff1b..411fcaf5c35 100644 --- a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -144,7 +144,7 @@ contract InclusionProofs { ValueNoteMethods, maybe_note.unwrap_unchecked(), block_number, - context + &mut context ); } else { // Note was not found so we will use the spare nullifier @@ -164,7 +164,7 @@ contract InclusionProofs { let note = notes[0].unwrap(); // 2) Prove the note validity - prove_note_validity(ValueNoteMethods, note, block_number, context); + prove_note_validity(ValueNoteMethods, note, block_number, &mut context); } #[aztec(private)] diff --git a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index 3d3eb1ecfa1..2db16db6be3 100644 --- a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -6,7 +6,7 @@ use dep::aztec::{ }, hash::pedersen_hash, oracle::{ - get_secret_key::get_secret_key, + nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key, }, log::emit_encrypted_log, @@ -40,9 +40,20 @@ impl PublicKeyNote { [self.x, self.y, self.owner.to_field()] } - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let unique_siloed_note_hash = compute_unique_siloed_note_hash(PublicKeyNoteMethods, self); - let secret = get_secret_key(self.owner); + let secret = context.request_nullifier_secret_key(self.owner); + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash([ + unique_siloed_note_hash, + secret.low, + secret.high, + ],0) + } + + pub fn compute_nullifier_without_context(self) -> Field { + let unique_siloed_note_hash = compute_unique_siloed_note_hash(PublicKeyNoteMethods, self); + let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ unique_siloed_note_hash, @@ -86,8 +97,12 @@ fn compute_note_hash(note: PublicKeyNote) -> Field { pedersen_hash(note.serialize(), 0) } -fn compute_nullifier(note: PublicKeyNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: PublicKeyNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: PublicKeyNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: PublicKeyNote) -> NoteHeader { @@ -108,6 +123,7 @@ global PublicKeyNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr index f0ecb079729..e63efeab306 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr @@ -16,12 +16,6 @@ use dep::aztec::note::{ note_interface::NoteInterface, utils::compute_note_hash_for_read_or_nullify, }; -use dep::aztec::oracle::{ - rand::rand, - get_secret_key::get_secret_key, - get_public_key::get_public_key, -}; - use crate::types::token_note::{TokenNote, TOKEN_NOTE_LEN, TokenNoteMethods}; // A set implementing standard manipulation of balances. diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 8a227536191..41e921d16d0 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -13,7 +13,7 @@ use dep::aztec::{ }; use dep::aztec::oracle::{ rand::rand, - get_secret_key::get_secret_key, + nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key, }; use dep::safe_math::SafeU120; @@ -68,9 +68,9 @@ impl TokenNote { } // docs:start:nullifier - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); - let secret = get_secret_key(self.owner); + let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ note_hash_for_nullify, @@ -80,6 +80,17 @@ impl TokenNote { } // docs:end:nullifier + pub fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); + let secret = get_nullifier_secret_key(self.owner); + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash([ + note_hash_for_nullify, + secret.low, + secret.high, + ],0) + } + pub fn set_header(&mut self, header: NoteHeader) { self.header = header; } @@ -112,8 +123,12 @@ fn compute_note_hash(note: TokenNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: TokenNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: TokenNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: TokenNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: TokenNote) -> NoteHeader { @@ -134,6 +149,7 @@ global TokenNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr index 12772aed42f..98867d1225c 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr @@ -70,7 +70,11 @@ impl TransparentNote { ],0) } - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { + self.compute_nullifier_without_context() + } + + pub fn compute_nullifier_without_context(self) -> Field { // TODO(#1386): should use `compute_note_hash_for_read_or_nullify` once public functions inject nonce! let siloed_note_hash = compute_siloed_note_hash(TransparentNoteMethods, self); // TODO(#1205) Should use a non-zero generator index. @@ -102,8 +106,12 @@ fn compute_note_hash(note: TransparentNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: TransparentNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: TransparentNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: TransparentNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: TransparentNote) -> NoteHeader { @@ -123,6 +131,7 @@ global TransparentNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr index 80b6a2ceedd..fc2303da23b 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr @@ -18,12 +18,6 @@ use dep::aztec::note::{ note_interface::NoteInterface, utils::compute_note_hash_for_read_or_nullify, }; -use dep::aztec::oracle::{ - rand::rand, - get_secret_key::get_secret_key, - get_public_key::get_public_key, -}; - use crate::types::token_note::{TokenNote, TOKEN_NOTE_LEN, TokenNoteMethods}; // A set implementing standard manipulation of balances. diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr index d2d9ef68f1f..f87ccd3cef2 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -17,7 +17,7 @@ use dep::aztec::{ }; use dep::aztec::oracle::{ rand::rand, - get_secret_key::get_secret_key, + nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key, }; use dep::safe_math::SafeU120; @@ -72,9 +72,9 @@ impl TokenNote { } // docs:start:nullifier - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); - let secret = get_secret_key(self.owner); + let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ note_hash_for_nullify, @@ -84,6 +84,17 @@ impl TokenNote { } // docs:end:nullifier + pub fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); + let secret = get_nullifier_secret_key(self.owner); + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash([ + note_hash_for_nullify, + secret.low, + secret.high, + ],0) + } + pub fn set_header(&mut self, header: NoteHeader) { self.header = header; } @@ -116,8 +127,12 @@ fn compute_note_hash(note: TokenNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: TokenNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: TokenNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: TokenNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: TokenNote) -> NoteHeader { @@ -138,6 +153,7 @@ global TokenNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr index 034b4b3390f..deb2bcdf6f1 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr @@ -70,7 +70,11 @@ impl TransparentNote { ],0) } - pub fn compute_nullifier(self) -> Field { + pub fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { + self.compute_nullifier_without_context() + } + + pub fn compute_nullifier_without_context(self) -> Field { // TODO(#1386): should use `compute_note_hash_for_read_or_nullify` once public functions inject nonce! let siloed_note_hash = compute_siloed_note_hash(TransparentNoteMethods, self); // TODO(#1205) Should use a non-zero generator index. @@ -102,8 +106,12 @@ fn compute_note_hash(note: TransparentNote) -> Field { note.compute_note_hash() } -fn compute_nullifier(note: TransparentNote) -> Field { - note.compute_nullifier() +fn compute_nullifier(note: TransparentNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: TransparentNote) -> Field { + note.compute_nullifier_without_context() } fn get_header(note: TransparentNote) -> NoteHeader { @@ -123,6 +131,7 @@ global TransparentNoteMethods = NoteInterface { serialize, compute_note_hash, compute_nullifier, + compute_nullifier_without_context, get_header, set_header, broadcast, diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 1caa8fb8845..50d62d00996 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -1,4 +1,4 @@ -import { DBOracle, MessageLoadOracleInputs } from '@aztec/acir-simulator'; +import { DBOracle, KeyPair, MessageLoadOracleInputs } from '@aztec/acir-simulator'; import { KeyStore, L2Block, @@ -7,16 +7,7 @@ import { PublicDataWitness, StateInfoProvider, } from '@aztec/circuit-types'; -import { - AztecAddress, - BlockHeader, - CompleteAddress, - EthAddress, - Fr, - FunctionSelector, - GrumpkinPrivateKey, - PublicKey, -} from '@aztec/circuits.js'; +import { AztecAddress, BlockHeader, CompleteAddress, EthAddress, Fr, FunctionSelector } from '@aztec/circuits.js'; import { FunctionArtifactWithDebugMetadata } from '@aztec/foundation/abi'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -35,8 +26,11 @@ export class SimulatorOracle implements DBOracle { private log = createDebugLogger('aztec:pxe:simulator_oracle'), ) {} - getSecretKey(_contractAddress: AztecAddress, pubKey: PublicKey): Promise { - return this.keyStore.getAccountPrivateKey(pubKey); + async getNullifierKeyPair(accountAddress: AztecAddress, contractAddress: AztecAddress): Promise { + const accountPublicKey = (await this.db.getCompleteAddress(accountAddress))!.publicKey; + const publicKey = await this.keyStore.getNullifierPublicKey(accountPublicKey); + const secretKey = await this.keyStore.getSiloedNullifierSecretKey(accountPublicKey, contractAddress); + return { publicKey, secretKey }; } async getCompleteAddress(address: AztecAddress): Promise {