Skip to content

Commit

Permalink
feat: npk_m_hash in all notes + key rotation test (#6405)
Browse files Browse the repository at this point in the history
Resolves #6313
Resolves #6296
Resolves #5630

Have created #6417 to refactor the rotate call

---------

Co-authored-by: Jan Beneš <janbenes1234@gmail.com>
  • Loading branch information
sklppy88 and benesjan committed May 16, 2024
1 parent 921a7f4 commit 74e98d4
Show file tree
Hide file tree
Showing 76 changed files with 1,154 additions and 570 deletions.
4 changes: 2 additions & 2 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ library Constants {
uint256 internal constant MAX_NOTE_HASH_READ_REQUESTS_PER_CALL = 32;
uint256 internal constant MAX_NULLIFIER_READ_REQUESTS_PER_CALL = 2;
uint256 internal constant MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL = 2;
uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL = 1;
uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL = 16;
uint256 internal constant MAX_ENCRYPTED_LOGS_PER_CALL = 4;
uint256 internal constant MAX_UNENCRYPTED_LOGS_PER_CALL = 4;
uint256 internal constant MAX_NEW_NOTE_HASHES_PER_TX = 64;
Expand All @@ -38,7 +38,7 @@ library Constants {
uint256 internal constant MAX_NOTE_HASH_READ_REQUESTS_PER_TX = 128;
uint256 internal constant MAX_NULLIFIER_READ_REQUESTS_PER_TX = 8;
uint256 internal constant MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX = 8;
uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX = 4;
uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX = 64;
uint256 internal constant MAX_ENCRYPTED_LOGS_PER_TX = 8;
uint256 internal constant MAX_UNENCRYPTED_LOGS_PER_TX = 8;
uint256 internal constant NUM_ENCRYPTED_LOGS_HASHES_PER_TX = 1;
Expand Down
23 changes: 12 additions & 11 deletions noir-projects/aztec-nr/address-note/src/address_note.nr
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use dep::aztec::{
keys::getters::get_ivpk_m,
protocol_types::{address::AztecAddress, traits::Empty, constants::GENERATOR_INDEX__NOTE_NULLIFIER},
protocol_types::{
address::AztecAddress, traits::Empty, constants::GENERATOR_INDEX__NOTE_NULLIFIER,
grumpkin_point::GrumpkinPoint, hash::poseidon2_hash
},
note::{note_header::NoteHeader, note_interface::NoteInterface, utils::compute_note_hash_for_consumption},
oracle::{unsafe_rand::unsafe_rand, nullifier_key::get_app_nullifier_secret_key},
context::PrivateContext, hash::poseidon2_hash
oracle::unsafe_rand::unsafe_rand, keys::getters::get_nsk_app, context::PrivateContext
};

global ADDRESS_NOTE_LEN: Field = 3;
Expand All @@ -13,15 +14,16 @@ global ADDRESS_NOTE_LEN: Field = 3;
#[aztec(note)]
struct AddressNote {
address: AztecAddress,
owner: AztecAddress,
// The nullifying public key hash is used with the nsk_app to ensure that the note can be privately spent.
npk_m_hash: Field,
randomness: Field,
}

impl NoteInterface<ADDRESS_NOTE_LEN> for AddressNote {

fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
let note_hash_for_nullify = compute_note_hash_for_consumption(self);
let secret = context.request_app_nullifier_secret_key(self.owner);
let secret = context.request_nsk_app(self.npk_m_hash);
poseidon2_hash([
note_hash_for_nullify,
secret,
Expand All @@ -31,7 +33,7 @@ impl NoteInterface<ADDRESS_NOTE_LEN> for AddressNote {

fn compute_nullifier_without_context(self) -> Field {
let note_hash_for_nullify = compute_note_hash_for_consumption(self);
let secret = get_app_nullifier_secret_key(self.owner);
let secret = get_nsk_app(self.npk_m_hash);
poseidon2_hash([
note_hash_for_nullify,
secret,
Expand All @@ -40,8 +42,7 @@ impl NoteInterface<ADDRESS_NOTE_LEN> for AddressNote {
}

// Broadcasts the note as an encrypted log on L1.
fn broadcast(self, context: &mut PrivateContext, slot: Field) {
let ivpk_m = get_ivpk_m(context, self.owner);
fn broadcast(self, context: &mut PrivateContext, slot: Field, ivpk_m: GrumpkinPoint) {
// docs:start:encrypted
context.emit_encrypted_log(
(*context).this_address(),
Expand All @@ -55,9 +56,9 @@ impl NoteInterface<ADDRESS_NOTE_LEN> for AddressNote {
}

impl AddressNote {
pub fn new(address: AztecAddress, owner: AztecAddress) -> Self {
pub fn new(address: AztecAddress, npk_m_hash: Field) -> Self {
let randomness = unsafe_rand();
AddressNote { address, owner, randomness, header: NoteHeader::empty() }
AddressNote { address, npk_m_hash, randomness, header: NoteHeader::empty() }
}
// docs:end:address_note_def
}
56 changes: 17 additions & 39 deletions noir-projects/aztec-nr/aztec/src/context/private_context.nr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::{
context::{inputs::PrivateContextInputs, interface::ContextInterface},
messaging::process_l1_to_l2_message,
keys::getters::get_nullifier_keys, messaging::process_l1_to_l2_message,
hash::{hash_args_array, ArgsHasher, compute_encrypted_log_hash, compute_unencrypted_log_hash},
oracle::{
arguments, returns, call_private_function::call_private_function_internal, header::get_header_at,
nullifier_keys::NullifierKeys, arguments, returns,
call_private_function::call_private_function_internal, header::get_header_at,
logs::emit_encrypted_log, logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog},
nullifier_key::{get_nullifier_keys, get_nullifier_keys_with_npk_m_hash, NullifierKeys},
enqueue_public_function_call::{
enqueue_public_function_call_internal, set_public_teardown_function_call_internal,
parse_public_call_stack_item_from_oracle
Expand All @@ -31,7 +31,7 @@ use dep::protocol_types::{
contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest},
grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint, header::Header,
messaging::l2_to_l1_message::L2ToL1Message, utils::reader::Reader,
traits::{is_empty, Deserialize, Empty}, hash::poseidon2_hash
traits::{is_empty, Deserialize, Empty}
};

// When finished, one can call .finish() to convert back to the abi
Expand Down Expand Up @@ -203,47 +203,25 @@ impl PrivateContext {
self.nullifier_read_requests.push(request);
}

pub fn request_app_nullifier_secret_key(&mut self, account: AztecAddress) -> Field {
let keys = if self.nullifier_key.is_none() {
let keys = get_nullifier_keys(account);
let request = NullifierKeyValidationRequest {
master_nullifier_public_key: keys.master_nullifier_public_key,
app_nullifier_secret_key: keys.app_nullifier_secret_key
};
self.nullifier_key_validation_requests.push(request);
self.nullifier_key = Option::some(keys);
keys
} else {
let keys = self.nullifier_key.unwrap_unchecked();
// If MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL is larger than 1, need to update the way the key pair is cached.
assert(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL == 1);
assert(keys.account == account, "Cannot query nullifier key for more than one account per call");
keys
};
keys.app_nullifier_secret_key
}
pub fn request_nsk_app(&mut self, npk_m_hash: Field) -> Field {
// A value of empty nullifier keys will fail the key validation request.
let cached_nullifier_keys = self.nullifier_key.unwrap_or(NullifierKeys::empty());

// TODO(#5630) Replace request_app_nullifier_secret_key above with this once we no longer get app nullifier secret key with address
pub fn request_nsk_app_with_npk_m_hash(&mut self, npk_m_hash: Field) -> Field {
let keys = if self.nullifier_key.is_none() {
let keys = get_nullifier_keys_with_npk_m_hash(npk_m_hash);
let nullifier_keys = if cached_nullifier_keys.master_nullifier_public_key.hash() == npk_m_hash {
cached_nullifier_keys
} else {
let fetched_nullifier_keys = get_nullifier_keys(npk_m_hash);
let request = NullifierKeyValidationRequest {
master_nullifier_public_key: keys.master_nullifier_public_key,
app_nullifier_secret_key: keys.app_nullifier_secret_key
master_nullifier_public_key: fetched_nullifier_keys.master_nullifier_public_key,
app_nullifier_secret_key: fetched_nullifier_keys.app_nullifier_secret_key
};
self.nullifier_key_validation_requests.push(request);
self.nullifier_key = Option::some(keys);
keys
} else {
let keys = self.nullifier_key.unwrap_unchecked();
// If MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL is larger than 1, need to update the way the key pair is cached.
assert(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL == 1);
keys
self.nullifier_key = Option::some(fetched_nullifier_keys);
fetched_nullifier_keys
};

// We have to check if the key that was requested or cached corresponds to the one we request for
assert_eq(poseidon2_hash(keys.master_nullifier_public_key.serialize()), npk_m_hash);
keys.app_nullifier_secret_key
assert_eq(nullifier_keys.master_nullifier_public_key.hash(), npk_m_hash);
nullifier_keys.app_nullifier_secret_key
}

// docs:start:context_message_portal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ mod test {

use crate::{
note::{note_header::NoteHeader, note_interface::NoteInterface, utils::compute_note_hash_for_consumption},
oracle::{unsafe_rand::unsafe_rand, nullifier_key::get_app_nullifier_secret_key},
context::PrivateContext, hash::poseidon2_hash
oracle::unsafe_rand::unsafe_rand, context::PrivateContext
};

struct AddressNote {
Expand All @@ -93,7 +92,7 @@ mod test {

fn compute_nullifier_without_context(self) -> Field {1}

fn broadcast(self, context: &mut PrivateContext, slot: Field) {}
fn broadcast(self, context: &mut PrivateContext, slot: Field, ivpk_m: GrumpkinPoint) {}

fn serialize_content(self) -> [Field; ADDRESS_NOTE_LEN] { [self.address.to_field(), self.owner.to_field(), self.randomness]}

Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/hash.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use dep::protocol_types::{
GENERATOR_INDEX__SECRET_HASH, GENERATOR_INDEX__MESSAGE_NULLIFIER, ARGS_HASH_CHUNK_COUNT,
GENERATOR_INDEX__FUNCTION_ARGS, ARGS_HASH_CHUNK_LENGTH
},
traits::Hash, hash::{pedersen_hash, poseidon2_hash, silo_nullifier, sha256_to_field}
traits::Hash, hash::{pedersen_hash, silo_nullifier, sha256_to_field}
};
use crate::oracle::logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog};

Expand Down
26 changes: 17 additions & 9 deletions noir-projects/aztec-nr/aztec/src/keys/getters.nr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use dep::protocol_types::{
address::AztecAddress, constants::CANONICAL_KEY_REGISTRY_ADDRESS, grumpkin_point::GrumpkinPoint,
hash::poseidon2_hash
};
use dep::protocol_types::{address::AztecAddress, constants::CANONICAL_KEY_REGISTRY_ADDRESS, grumpkin_point::GrumpkinPoint};
use crate::{
context::PrivateContext,
oracle::keys::{get_public_keys_and_partial_address, get_public_keys_and_partial_address_with_npk_m_hash},
oracle::{
keys::get_public_keys_and_partial_address,
nullifier_keys::{NullifierKeys, get_nullifier_keys as get_nullifier_keys_oracle}
},
keys::public_keys::{PublicKeys, NULLIFIER_INDEX, INCOMING_INDEX},
state_vars::{
map::derive_storage_slot_in_map,
Expand All @@ -20,7 +20,7 @@ pub fn get_npk_m(context: &mut PrivateContext, address: AztecAddress) -> Grumpki
}

pub fn get_npk_m_hash(context: &mut PrivateContext, address: AztecAddress) -> Field {
poseidon2_hash(get_master_key(context, address, NULLIFIER_INDEX).serialize())
get_master_key(context, address, NULLIFIER_INDEX).hash()
}

pub fn get_ivpk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint {
Expand Down Expand Up @@ -91,7 +91,15 @@ fn fetch_and_constrain_keys(address: AztecAddress) -> PublicKeys {
public_keys
}

pub fn get_ivpk_m_with_npk_m_hash(npk_m_hash: Field) -> GrumpkinPoint {
let result = get_public_keys_and_partial_address_with_npk_m_hash(npk_m_hash);
result.0.ivpk_m
// We get the full struct Nullifier Keys here
pub fn get_nullifier_keys(npk_m_hash: Field) -> NullifierKeys {
let nullifier_keys = get_nullifier_keys_oracle(npk_m_hash);
assert_eq(nullifier_keys.master_nullifier_public_key.hash(), npk_m_hash);

nullifier_keys
}

// We are only getting the app_nullifier_secret_key here
pub fn get_nsk_app(npk_m_hash: Field) -> Field {
get_nullifier_keys(npk_m_hash).app_nullifier_secret_key
}
6 changes: 4 additions & 2 deletions noir-projects/aztec-nr/aztec/src/note/lifecycle.nr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use dep::protocol_types::grumpkin_point::GrumpkinPoint;
use crate::context::{PrivateContext, PublicContext};
use crate::note::{
note_header::NoteHeader, note_interface::NoteInterface,
Expand All @@ -9,7 +10,8 @@ pub fn create_note<Note, N>(
context: &mut PrivateContext,
storage_slot: Field,
note: &mut Note,
broadcast: bool
broadcast: bool,
ivpk_m: GrumpkinPoint
) where Note: NoteInterface<N> {
let contract_address = (*context).this_address();

Expand All @@ -36,7 +38,7 @@ pub fn create_note<Note, N>(
context.push_new_note_hash(inner_note_hash);

if broadcast {
Note::broadcast(*note, context, storage_slot);
Note::broadcast(*note, context, storage_slot, ivpk_m);
}
}

Expand Down
3 changes: 2 additions & 1 deletion noir-projects/aztec-nr/aztec/src/note/note_interface.nr
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use crate::context::PrivateContext;
use crate::note::note_header::NoteHeader;
use dep::protocol_types::grumpkin_point::GrumpkinPoint;

// docs:start:note_interface
trait NoteInterface<N> {
fn compute_nullifier(self, context: &mut PrivateContext) -> Field;

fn compute_nullifier_without_context(self) -> Field;

fn broadcast(self, context: &mut PrivateContext, slot: Field) -> ();
fn broadcast(self, context: &mut PrivateContext, slot: Field, ivpk_m: GrumpkinPoint) -> ();

// Autogenerated by the #[aztec(note)] macro unless it is overridden by a custom implementation
fn serialize_content(self) -> [Field; N];
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/oracle.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod get_nullifier_membership_witness;
mod get_public_data_witness;
mod get_membership_witness;
mod keys;
mod nullifier_key;
mod nullifier_keys;
mod get_sibling_path;
mod unsafe_rand;
mod enqueue_public_function_call;
Expand Down
22 changes: 0 additions & 22 deletions noir-projects/aztec-nr/aztec/src/oracle/keys.nr
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,3 @@ fn get_public_keys_and_partial_address(address: AztecAddress) -> (PublicKeys, Pa

(keys, partial_address)
}

#[oracle(getPublicKeysAndPartialAddressWithNpkMHash)]
fn get_public_keys_and_partial_address_with_npk_m_hash_oracle(_npk_m_hash: Field) -> [Field; 9] {}

unconstrained fn get_public_keys_and_partial_address_with_npk_m_hash_oracle_wrapper(npk_m_hash: Field) -> [Field; 9] {
get_public_keys_and_partial_address_with_npk_m_hash_oracle(npk_m_hash)
}

fn get_public_keys_and_partial_address_with_npk_m_hash(npk_m_hash: Field) -> (PublicKeys, PartialAddress) {
let result = get_public_keys_and_partial_address_with_npk_m_hash_oracle_wrapper(npk_m_hash);

let keys = PublicKeys {
npk_m: GrumpkinPoint::new(result[0], result[1]),
ivpk_m: GrumpkinPoint::new(result[2], result[3]),
ovpk_m: GrumpkinPoint::new(result[4], result[5]),
tpk_m: GrumpkinPoint::new(result[6], result[7])
};

let partial_address = PartialAddress::from_field(result[8]);

(keys, partial_address)
}
52 changes: 0 additions & 52 deletions noir-projects/aztec-nr/aztec/src/oracle/nullifier_key.nr

This file was deleted.

Loading

0 comments on commit 74e98d4

Please sign in to comment.