diff --git a/boxes/token/src/contracts/src/types/balance_set.nr b/boxes/token/src/contracts/src/types/balance_set.nr index f81c33db5d48..231320de558d 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 ed22cfc0e98f..f8adf31959db 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 034b4b3390f7..deb2bcdf6f1b 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 21d0f240c818..e5d897d0ffcc 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 b5e6f0847211..c8ab1e70f641 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 49449ffcbcb8..10a78d708756 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 995a285b7b1a..621a62db10f7 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 eb4f59fae84e..7090aa52300f 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 becd6a0f73c6..01b8c34ac75d 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 b1e0d95a4bcc..7c5a3e830e28 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 92430a19969a..74e6c1ae4eed 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 b029833f2ea3..3b6128e32210 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 dc5e0fa47d6e..199cbd7b078a 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 b89e570f53fd..6329a9d541f1 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 d98a9e878318..30abac51c749 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 47036d2f9f3e..f4cb297d17ad 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 000000000000..3ea8deca7544 --- /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 000000000000..3f07dba4b2ce --- /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 39c5b6c290ed..9bf7bf42c8f5 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 0eb21346f8d4..ec741e6dbae1 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 b56d2030645a..614c20d27770 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 d9286fad1232..5d88d904a77f 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 edd47519d2b3..81eb3571a492 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 a4da1c2a5857..000000000000 --- 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 000000000000..b97b2d651a3e --- /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 e87555d5a82d..699cecae2503 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 28cd3ae23b8f..d564512a1ab5 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 6267a7b6016e..8aa8fbe5520c 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 d02037e49982..4cffea0d4fa3 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 682aa89862e5..d9da2be7d50b 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 91e7dd069b3b..347db88ae51b 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 000000000000..09ec99767c4d --- /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 8a394773730d..16ed36795871 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 1a5a96c2d1b8..f0af31ee6da9 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 6672ad559bc4..b0b17ef7ac15 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 f0f85b6e0cd9..4aacb388d9ab 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 0701b4819be2..000000000000 --- 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 9c9423f31ea8..e827e1cdede0 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 8870c2972670..3287e4df9054 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 b8bf6dc7bfd8..08035046df2b 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 97da78e65192..ba383755ca09 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 201c796c8052..000000000000 --- 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 acd4ed0f7caf..000000000000 --- 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 d3abaecb5d18..000000000000 --- 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 54db9e4017a9..ceddb93c70c2 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 c0aea096b20b..779c8debeccc 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 f0882caff1b5..411fcaf5c35d 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 3d3eb1ecfa1a..2db16db6be3b 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 f0ecb0797296..e63efeab3064 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 8a2275361911..41e921d16d0c 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 12772aed42fe..98867d1225cc 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 80b6a2ceeddd..fc2303da23b2 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 d2d9ef68f1fb..f87ccd3cef2d 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 034b4b3390f7..deb2bcdf6f1b 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 1caa8fb88452..50d62d00996f 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 {