Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: aztec nr lib constraining nullifier key is fresh #5939

Merged
merged 38 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3acc686
Initial
sklppy88 Apr 22, 2024
1f53bd5
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 22, 2024
177445f
fix
sklppy88 Apr 22, 2024
fdbb60a
good
sklppy88 Apr 22, 2024
2d3a95f
asdf
sklppy88 Apr 22, 2024
58f9650
working tests
sklppy88 Apr 23, 2024
42a9c35
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 23, 2024
f82d1c8
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 23, 2024
ea93350
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 24, 2024
781bbe3
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 25, 2024
3201fd4
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 26, 2024
1b13804
cleanup
sklppy88 Apr 26, 2024
211a060
yarn format
sklppy88 Apr 26, 2024
29bfb80
fix
sklppy88 Apr 26, 2024
7d1620d
change some stuff
sklppy88 Apr 26, 2024
f0ece69
fix
sklppy88 Apr 26, 2024
0461006
fix
sklppy88 Apr 26, 2024
e4f6d15
asdf
sklppy88 Apr 26, 2024
f9a844b
fix
sklppy88 Apr 26, 2024
0e0b63e
fix
sklppy88 Apr 26, 2024
7572be2
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 26, 2024
9051c8e
format
sklppy88 Apr 26, 2024
6e3509a
test
sklppy88 Apr 26, 2024
6fbd1d9
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 26, 2024
711afac
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 29, 2024
7f5dbfc
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 Apr 29, 2024
0ca8578
Addressing comments
sklppy88 May 1, 2024
c852740
Apply suggestions from code review
sklppy88 May 1, 2024
3b9e7ba
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 May 1, 2024
b76ab1a
fix
sklppy88 May 1, 2024
7eb2366
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 May 1, 2024
e2a479a
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 May 1, 2024
59a4c18
format
sklppy88 May 1, 2024
194df19
Address comments
sklppy88 May 1, 2024
938761a
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 May 1, 2024
0baf281
Merge branch 'master' into ek/feat/constrain-keys-are-fresh-lib
sklppy88 May 1, 2024
a85f513
fix
sklppy88 May 1, 2024
94db9f5
Okay
sklppy88 May 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion noir-projects/aztec-nr/aztec/src/keys.nr
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
mod point_to_symmetric_key;
mod getters;
mod point_to_symmetric_key;

use crate::keys::getters::get_fresh_nullifier_public_key_hash;
79 changes: 79 additions & 0 deletions noir-projects/aztec-nr/aztec/src/keys/getters.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use dep::protocol_types::{
address::{
AztecAddress,
PartialAddress
},
constants::{
GENERATOR_INDEX__PUBLIC_KEYS_HASH,
GENERATOR_INDEX__CONTRACT_ADDRESS_V1,
CANONICAL_KEY_REGISTRY_ADDRESS
},
grumpkin_point::GrumpkinPoint,
};

use crate::context::PrivateContext;
use crate::hash::{
pedersen_hash,
poseidon2_hash,
};
use crate::oracle::keys::get_public_keys_and_partial_address;
use crate::state_vars::{
map::derive_storage_slot_in_map,
shared_mutable::shared_mutable_private_getter::SharedMutablePrivateGetter,
};

struct PublicKeyTypeEnum {
NULLIFIER: u8,
}

global PublicKeyType = PublicKeyTypeEnum {
NULLIFIER: 0,
};

pub fn get_fresh_nullifier_public_key_hash(
context: &mut PrivateContext,
address: AztecAddress,
) -> Field {
// This is the storage slot of the nullifier_public_key inside the key registry contract
// TODO: (#6133) We should have this be directly imported from the other contract if possible, or at least this should not be this brittle
let storage_slot_of_nullifier_public_key = 1;
LHerskind marked this conversation as resolved.
Show resolved Hide resolved

let derived_slot = derive_storage_slot_in_map(storage_slot_of_nullifier_public_key, address);

// We read from the canonical Key Registry
// TODO: (#6134) It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly.
// We should allow for this usecase without needing to hard code it here.
let registry_private_getter: SharedMutablePrivateGetter<Field, 5> = SharedMutablePrivateGetter::new(*context, AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS), derived_slot);
let nullifier_public_key_hash_in_registry = registry_private_getter.get_current_value_in_private();

let nullifier_public_key_hash = if nullifier_public_key_hash_in_registry == 0 {
let keys = get_original_public_keys_internal(address);
poseidon2_hash(keys[PublicKeyType.NULLIFIER].serialize())
LHerskind marked this conversation as resolved.
Show resolved Hide resolved
} else {
nullifier_public_key_hash_in_registry
};

nullifier_public_key_hash
}

// This constraint only works on keys that have not been rotated, otherwise this call will fail as the public keys are not constrained
fn get_original_public_keys_internal(address: AztecAddress) -> [GrumpkinPoint; 4] {
let (public_keys, partial_address) = get_public_keys_and_partial_address(address);

let nullifier_pub_key = public_keys[0];
let incoming_pub_key = public_keys[1];
let outgoing_pub_key = public_keys[2];
let tagging_pub_key = public_keys[3];

let computed_address = AztecAddress::compute_from_public_keys_and_partial_address(
nullifier_pub_key,
incoming_pub_key,
outgoing_pub_key,
tagging_pub_key,
partial_address,
);

assert(computed_address.eq(address));

[nullifier_pub_key, incoming_pub_key, outgoing_pub_key, tagging_pub_key]
}
1 change: 1 addition & 0 deletions noir-projects/aztec-nr/aztec/src/oracle.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod get_nullifier_membership_witness;
mod get_public_data_witness;
mod get_membership_witness;
mod get_public_key;
mod keys;
mod nullifier_key;
mod get_sibling_path;
mod unsafe_rand;
Expand Down
28 changes: 28 additions & 0 deletions noir-projects/aztec-nr/aztec/src/oracle/keys.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use dep::protocol_types::{
address::{
AztecAddress,
PartialAddress,
},
grumpkin_point::GrumpkinPoint,
};

use crate::hash::poseidon2_hash;

#[oracle(getPublicKeysAndPartialAddress)]
fn get_public_keys_and_partial_address_oracle(_address: AztecAddress) -> [Field; 9] {}

unconstrained fn get_public_keys_and_partial_address_oracle_wrapper(address: AztecAddress) -> [Field; 9] {
get_public_keys_and_partial_address_oracle(address)
}

fn get_public_keys_and_partial_address(address: AztecAddress) -> ([GrumpkinPoint; 4], PartialAddress) {
let result = get_public_keys_and_partial_address_oracle_wrapper(address);

let nullifier_pub_key = GrumpkinPoint::new(result[0], result[1]);
let incoming_pub_key = GrumpkinPoint::new(result[2], result[3]);
let outgoing_pub_key = GrumpkinPoint::new(result[4], result[5]);
let tagging_pub_key = GrumpkinPoint::new(result[6], result[7]);
let partial_address = PartialAddress::from_field(result[8]);

([nullifier_pub_key, incoming_pub_key, outgoing_pub_key, tagging_pub_key], partial_address)
}
6 changes: 5 additions & 1 deletion noir-projects/aztec-nr/aztec/src/state_vars/map.nr
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ impl<K, V> Map<K, V> {
// docs:start:at
pub fn at(self, key: K) -> V where K: ToField {
// TODO(#1204): use a generator index for the storage slot
let derived_storage_slot = pedersen_hash([self.storage_slot, key.to_field()], 0);
let derived_storage_slot = derive_storage_slot_in_map(self.storage_slot, key);

let state_var_constructor = self.state_var_constructor;
state_var_constructor(self.context, derived_storage_slot)
}
// docs:end:at
}

pub fn derive_storage_slot_in_map<K>(storage_slot: Field, key: K) -> Field where K: ToField {
pedersen_hash([storage_slot, key.to_field()], 0)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ contract KeyRegistry {
Map
},
protocol_types::{
grumpkin_point::GrumpkinPoint,
address::{
AztecAddress,
PublicKeysHash,
Expand All @@ -24,9 +25,12 @@ contract KeyRegistry {

#[aztec(storage)]
struct Storage {
//! This should stay at storage slot 1. If you change this, make sure you change the hardcoded value in keys/assert_public_key_freshness.
//! We use this hardcoded storage slot with derive_storage_slot_in_map and the SharedMutablePrivateGetter to directly read the value at an address in this contract.
nullifier_public_key_hash_registry: Map<AztecAddress, SharedMutable<Field, KEY_ROTATION_DELAY>>,

// We are not supporting rotating / changing keys other than the nullifier public in the registry at the moment, but will in the future.
// Uncomment lines below to enable that functionality
nullifier_public_key_registry: Map<AztecAddress, SharedMutable<Field, KEY_ROTATION_DELAY>>,
// incoming_public_key_registry: Map<AztecAddress, SharedMutable<Field, KEY_ROTATION_DELAY>>,
// outgoing_public_key_registry: Map<AztecAddress, SharedMutable<Field, KEY_ROTATION_DELAY>>,
// tagging_public_key_registry: Map<AztecAddress, SharedMutable<Field, KEY_ROTATION_DELAY>>,
Expand All @@ -35,71 +39,63 @@ contract KeyRegistry {
#[aztec(public)]
fn rotate_nullifier_public_key(
address: AztecAddress,
new_nullifier_public_key: Field,
new_nullifier_public_key: GrumpkinPoint,
nonce: Field,
) {
assert(
new_nullifier_public_key != 0,
!new_nullifier_public_key.is_zero(),
"New nullifier public key must be non-zero"
);

// TODO: (#6137)
if (!address.eq(context.msg_sender())) {
assert_current_call_valid_authwit_public(&mut context, address);
LHerskind marked this conversation as resolved.
Show resolved Hide resolved
} else {
assert(nonce == 0, "invalid nonce");
}

let nullifier_key_registry = storage.nullifier_public_key_registry.at(address);
let nullifier_key_registry = storage.nullifier_public_key_hash_registry.at(address);

nullifier_key_registry.schedule_value_change(new_nullifier_public_key);
nullifier_key_registry.schedule_value_change(poseidon2_hash(new_nullifier_public_key.serialize()));
}

#[aztec(public)]
fn register(
address: AztecAddress,
partial_address: PartialAddress,
nullifier_public_key: Field,
incoming_public_key: Field,
outgoing_public_key: Field,
tagging_public_key: Field,
nullifier_public_key: GrumpkinPoint,
incoming_public_key: GrumpkinPoint,
outgoing_public_key: GrumpkinPoint,
tagging_public_key: GrumpkinPoint,
) {
assert(
(nullifier_public_key != 0) &
(incoming_public_key != 0) &
(outgoing_public_key != 0) &
(tagging_public_key != 0),
!partial_address.is_zero() &
!nullifier_public_key.is_zero() &
!incoming_public_key.is_zero() &
!outgoing_public_key.is_zero() &
!tagging_public_key.is_zero(),
"All public keys must be non-zero"
);

// TODO (ek): Do it below after refactoring all public_keys_hash_elemtns
// let public_keys_hash = PublicKeysHash::compute(nullifier_public_key, tagging_public_key, incoming_public_key, outgoing_public_key);
// let address = AztecAddress::compute(public_keys_hash, partial_address);
// We could also pass in original_public_keys_hash instead of computing it here, if all we need the original one is for being able to prove ownership of address
let public_keys_hash = poseidon2_hash([
nullifier_public_key,
incoming_public_key,
outgoing_public_key,
tagging_public_key,
GENERATOR_INDEX__PUBLIC_KEYS_HASH,
]
);

let computed_address = AztecAddress::from_field(
poseidon2_hash([
partial_address.to_field(),
public_keys_hash.to_field(),
GENERATOR_INDEX__CONTRACT_ADDRESS_V1 as Field,
]
)
let computed_address = AztecAddress::compute_from_public_keys_and_partial_address(
nullifier_public_key,
incoming_public_key,
outgoing_public_key,
tagging_public_key,
partial_address,
);

assert(computed_address.eq(address), "Computed address does not match supplied address");

let nullifier_key_registry = storage.nullifier_public_key_registry.at(address);
let nullifier_key_hash_registry = storage.nullifier_public_key_hash_registry.at(address);
// We are not supporting rotating / changing keys other than the nullifier public in the registry at the moment, but will in the future.
// Uncomment lines below to enable that functionality
// let incoming_key_registry = storage.incoming_public_key_registry.at(address);
// let outgoing_key_registry = storage.outgoing_public_key_registry.at(address);
// let tagging_key_registry = storage.taggin_public_key_registry.at(address);

nullifier_key_registry.schedule_value_change(nullifier_public_key);
nullifier_key_hash_registry.schedule_value_change(poseidon2_hash(nullifier_public_key.serialize()));
// We are not supporting rotating / changing keys other than the nullifier public in the registry at the moment, but will in the future.
// Uncomment lines below to enable that functionality // incoming_key_registry.schedule_value_change(new_incoming_public_key);
// outgoing_key_registry.schedule_value_change(new_outgoing_public_key);
Expand Down
57 changes: 34 additions & 23 deletions noir-projects/noir-contracts/contracts/test_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ contract Test {

use dep::aztec::protocol_types::{
abis::private_circuit_public_inputs::PrivateCircuitPublicInputs,
constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, traits::Serialize
constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, CANONICAL_KEY_REGISTRY_ADDRESS}, traits::{Serialize, ToField, FromField},
grumpkin_point::GrumpkinPoint
};

use dep::aztec::note::constants::MAX_NOTES_PER_PAGE;

use dep::aztec::state_vars::shared_mutable::SharedMutablePrivateGetter;
use dep::aztec::state_vars::{shared_mutable::SharedMutablePrivateGetter, map::derive_storage_slot_in_map};

use dep::aztec::{
keys::getters::get_fresh_nullifier_public_key_hash,
context::{Context, inputs::private_context_inputs::PrivateContextInputs},
hash::{pedersen_hash, compute_secret_hash, ArgsHasher},
hash::{pedersen_hash, poseidon2_hash, compute_secret_hash, ArgsHasher},
note::{
lifecycle::{create_note, destroy_note}, note_getter::{get_notes, view_notes},
note_getter_options::NoteStatus
Expand Down Expand Up @@ -375,38 +377,47 @@ contract Test {
constant.value
}

// This function is used in the e2e_state_vars to test the SharedMutablePrivateGetter in isolation
#[aztec(private)]
fn test_shared_mutable_private_getter_for_registry_contract(
fn test_shared_mutable_private_getter<T>(
contract_address_to_read: AztecAddress,
storage_slot_of_shared_mutable: Field
) -> Field where T: FromField, T: ToField {
// It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly
let test: SharedMutablePrivateGetter<T, 5> = SharedMutablePrivateGetter::new(
context,
contract_address_to_read,
storage_slot_of_shared_mutable
);

let ret = test.get_current_value_in_private();

ret.to_field()
}

// This function is used for testing the registry contract and fresh public key getters. If nothing exists in the registry, but we have added public
// keys to the pxe, this function will return nothing, but the public key getters will return the correct value
#[aztec(private)]
fn test_shared_mutable_private_getter_for_registry_contract(
storage_slot_of_shared_mutable: Field,
address_to_get_in_registry: AztecAddress
) {
) -> Field {
// We have to derive this slot to get the location of the shared mutable inside the Map
let derived_slot = dep::aztec::hash::pedersen_hash(
[storage_slot_of_shared_mutable, address_to_get_in_registry.to_field()],
0
);
let derived_slot = derive_storage_slot_in_map(storage_slot_of_shared_mutable, address_to_get_in_registry);

// It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly
let registry_private_getter: SharedMutablePrivateGetter<AztecAddress, 5> = SharedMutablePrivateGetter::new(context, contract_address_to_read, derived_slot);
let registry_private_getter: SharedMutablePrivateGetter<Field, 5> = SharedMutablePrivateGetter::new(context, AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS), derived_slot);
LHerskind marked this conversation as resolved.
Show resolved Hide resolved
let nullifier_public_key = registry_private_getter.get_current_value_in_private();

context.emit_unencrypted_log(nullifier_public_key);
nullifier_public_key
}

#[aztec(private)]
fn test_shared_mutable_private_getter(
contract_address_to_read: AztecAddress,
storage_slot_of_shared_mutable: Field
fn test_nullifier_key_freshness(
address: AztecAddress,
public_nullifying_key: GrumpkinPoint,
) {
// It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly
let test: SharedMutablePrivateGetter<AztecAddress, 5> = SharedMutablePrivateGetter::new(
context,
contract_address_to_read,
storage_slot_of_shared_mutable
);
let authorized = test.get_current_value_in_private();

context.emit_unencrypted_log(authorized);
assert_eq(get_fresh_nullifier_public_key_hash(&mut context, address), poseidon2_hash(public_nullifying_key.serialize()));
}

#[aztec(public)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ impl AztecAddress {
)
}

pub fn compute_from_public_keys_and_partial_address(
nullifier_public_key: GrumpkinPoint,
incoming_public_key: GrumpkinPoint,
outgoing_public_key: GrumpkinPoint,
tagging_public_key: GrumpkinPoint,
partial_address: PartialAddress,
) -> AztecAddress {
let public_keys_hash = PublicKeysHash::compute_new(
nullifier_public_key,
incoming_public_key,
outgoing_public_key,
tagging_public_key,
);

let computed_address = AztecAddress::compute(public_keys_hash, partial_address);

computed_address
}

pub fn is_zero(self) -> bool {
self.inner == 0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ impl PartialAddress {
self.inner
}

pub fn is_zero(self) -> bool {
self.to_field() == 0
}

pub fn assert_is_zero(self) {
assert(self.to_field() == 0);
}
Expand Down
Loading
Loading