-
Notifications
You must be signed in to change notification settings - Fork 236
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
140 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
mod getters; | ||
mod point_to_symmetric_key; | ||
|
||
use crate::keys::getters::get_fresh_nullifier_public_key_hash; | ||
use crate::keys::getters::{get_npk_m, get_ivpk_m, get_ovpk_m, get_tpk_m}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,90 @@ | ||
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 dep::protocol_types::{address::AztecAddress, constants::CANONICAL_KEY_REGISTRY_ADDRESS, grumpkin_point::GrumpkinPoint}; | ||
use crate::{ | ||
context::PrivateContext, hash::poseidon2_hash, oracle::keys::get_public_keys_and_partial_address, | ||
state_vars::{ | ||
map::derive_storage_slot_in_map, | ||
shared_mutable::shared_mutable_private_getter::SharedMutablePrivateGetter | ||
} | ||
}; | ||
|
||
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, | ||
}; | ||
// Note: In fetch_hash_from_registry we expect that shared mutable slow is index + 1 (e.g. NULLIFIER_INDEX + 1), | ||
// this changes the function will need to be refactored | ||
global NULLIFIER_INDEX = 0; | ||
global INCOMING_INDEX = 1; | ||
global OUTGOING_INDEX = 2; | ||
global TAGGING_INDEX = 3; | ||
|
||
struct PublicKeyTypeEnum { | ||
NULLIFIER: u8, | ||
global DELAY = 5; | ||
|
||
pub fn get_npk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint { | ||
get_master_key(context, address, NULLIFIER_INDEX) | ||
} | ||
|
||
global PublicKeyType = PublicKeyTypeEnum { | ||
NULLIFIER: 0, | ||
}; | ||
pub fn get_ivpk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint { | ||
get_master_key(context, address, INCOMING_INDEX) | ||
} | ||
|
||
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; | ||
pub fn get_ovpk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint { | ||
get_master_key(context, address, OUTGOING_INDEX) | ||
} | ||
|
||
let derived_slot = derive_storage_slot_in_map(storage_slot_of_nullifier_public_key, address); | ||
pub fn get_tpk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint { | ||
get_master_key(context, address, TAGGING_INDEX) | ||
} | ||
|
||
// 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(); | ||
fn get_master_key( | ||
context: &mut PrivateContext, | ||
address: AztecAddress, | ||
key_index: Field | ||
) -> GrumpkinPoint { | ||
// 1. Get key hash from registry | ||
let key_hash = fetch_hash_from_registry(context, key_index, address); | ||
if key_hash == 0 { | ||
// i. Keys were not registered yet --> fetch address preimage and check that the hash matches | ||
fetch_and_constrain_keys(address)[key_index] | ||
} else { | ||
// ii. Keys were registered --> we fetch the key from oracle, hash it and check that it matches the hash in the registry | ||
let (keys, _) = get_public_keys_and_partial_address(address); | ||
let key = keys[key_index]; | ||
let key_hash_from_oracle = poseidon2_hash(key.serialize()); | ||
assert(key_hash_from_oracle == key_hash); | ||
key | ||
} | ||
} | ||
|
||
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()) | ||
} else { | ||
nullifier_public_key_hash_in_registry | ||
}; | ||
fn fetch_hash_from_registry( | ||
context: &mut PrivateContext, | ||
key_index: Field, | ||
address: AztecAddress | ||
) -> Field { | ||
let derived_slot = derive_storage_slot_in_map(key_index + 1, address); | ||
|
||
nullifier_public_key_hash | ||
let registry_private_getter: SharedMutablePrivateGetter<Field, DELAY> = SharedMutablePrivateGetter::new( | ||
*context, | ||
AztecAddress::from_field(CANONICAL_KEY_REGISTRY_ADDRESS), | ||
derived_slot | ||
); | ||
registry_private_getter.get_current_value_in_private() | ||
} | ||
|
||
// 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); | ||
// Passes only when keys were not rotated - is expected to be called only when keys were not registered yet | ||
fn fetch_and_constrain_keys(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 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, | ||
); | ||
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)); | ||
assert(computed_address.eq(address)); | ||
|
||
[nullifier_pub_key, incoming_pub_key, outgoing_pub_key, tagging_pub_key] | ||
[nullifier_pub_key, incoming_pub_key, outgoing_pub_key, tagging_pub_key] | ||
} |
151 changes: 64 additions & 87 deletions
151
noir-projects/noir-contracts/contracts/key_registry_contract/src/main.nr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,104 +1,81 @@ | ||
contract KeyRegistry { | ||
use dep::authwit::auth::assert_current_call_valid_authwit_public; | ||
use dep::authwit::auth::assert_current_call_valid_authwit_public; | ||
|
||
use dep::aztec::{ | ||
state_vars::{ | ||
SharedMutable, | ||
Map | ||
}, | ||
protocol_types::{ | ||
grumpkin_point::GrumpkinPoint, | ||
address::{ | ||
AztecAddress, | ||
PublicKeysHash, | ||
PartialAddress, | ||
}, | ||
constants::{ | ||
GENERATOR_INDEX__CONTRACT_ADDRESS_V1, | ||
GENERATOR_INDEX__PUBLIC_KEYS_HASH | ||
}, | ||
hash::poseidon2_hash, | ||
}, | ||
}; | ||
use dep::aztec::{ | ||
state_vars::{SharedMutable, Map}, | ||
protocol_types::{ | ||
grumpkin_point::GrumpkinPoint, address::{AztecAddress, PartialAddress}, | ||
hash::poseidon2_hash | ||
} | ||
}; | ||
|
||
global KEY_ROTATION_DELAY = 5; | ||
global KEY_ROTATION_DELAY = 5; | ||
|
||
#[aztec(storage)] | ||
#[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 | ||
// 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>>, | ||
// The following stores a hash of individual master public keys | ||
// If you change slots of vars bellow, you must update the slot in `SharedMutablePrivateGetter` in aztec-nr keys. | ||
nullifier_registry: Map<AztecAddress, SharedMutable<Field, KEY_ROTATION_DELAY>>, | ||
incoming_registry: Map<AztecAddress, SharedMutable<Field, KEY_ROTATION_DELAY>>, | ||
outgoing_registry: Map<AztecAddress, SharedMutable<Field, KEY_ROTATION_DELAY>>, | ||
tagging_registry: Map<AztecAddress, SharedMutable<Field, KEY_ROTATION_DELAY>>, | ||
} | ||
|
||
#[aztec(public)] | ||
#[aztec(public)] | ||
fn rotate_nullifier_public_key( | ||
address: AztecAddress, | ||
new_nullifier_public_key: GrumpkinPoint, | ||
nonce: Field, | ||
) { | ||
assert( | ||
!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); | ||
} else { | ||
assert(nonce == 0, "invalid nonce"); | ||
} | ||
address: AztecAddress, | ||
new_nullifier_public_key: GrumpkinPoint, | ||
nonce: Field | ||
) { | ||
assert(!new_nullifier_public_key.is_zero(), "New nullifier public key must be non-zero"); | ||
|
||
let nullifier_key_registry = storage.nullifier_public_key_hash_registry.at(address); | ||
// TODO: (#6137) | ||
if (!address.eq(context.msg_sender())) { | ||
assert_current_call_valid_authwit_public(&mut context, address); | ||
} else { | ||
assert(nonce == 0, "invalid nonce"); | ||
} | ||
|
||
nullifier_key_registry.schedule_value_change(poseidon2_hash(new_nullifier_public_key.serialize())); | ||
} | ||
let nullifier_registry = storage.nullifier_registry.at(address); | ||
nullifier_registry.schedule_value_change(poseidon2_hash(new_nullifier_public_key.serialize())); | ||
} | ||
|
||
#[aztec(public)] | ||
#[aztec(public)] | ||
fn register( | ||
address: AztecAddress, | ||
partial_address: PartialAddress, | ||
nullifier_public_key: GrumpkinPoint, | ||
incoming_public_key: GrumpkinPoint, | ||
outgoing_public_key: GrumpkinPoint, | ||
tagging_public_key: GrumpkinPoint, | ||
) { | ||
assert( | ||
!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" | ||
); | ||
address: AztecAddress, | ||
partial_address: PartialAddress, | ||
nullifier_public_key: GrumpkinPoint, | ||
incoming_public_key: GrumpkinPoint, | ||
outgoing_public_key: GrumpkinPoint, | ||
tagging_public_key: GrumpkinPoint | ||
) { | ||
assert( | ||
!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" | ||
); | ||
|
||
// 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 computed_address = AztecAddress::compute_from_public_keys_and_partial_address( | ||
nullifier_public_key, | ||
incoming_public_key, | ||
outgoing_public_key, | ||
tagging_public_key, | ||
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 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"); | ||
assert(computed_address.eq(address), "Computed address does not match supplied 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); | ||
let nullifier_registry = storage.nullifier_registry.at(address); | ||
let incoming_registry = storage.incoming_registry.at(address); | ||
let outgoing_registry = storage.outgoing_registry.at(address); | ||
let tagging_registry = storage.tagging_registry.at(address); | ||
|
||
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); | ||
// tagging_key_registry.schedule_value_change(new_tagging_public_key); | ||
} | ||
nullifier_registry.schedule_value_change(poseidon2_hash(nullifier_public_key.serialize())); | ||
incoming_registry.schedule_value_change(poseidon2_hash(incoming_public_key.serialize())); | ||
outgoing_registry.schedule_value_change(poseidon2_hash(outgoing_public_key.serialize())); | ||
tagging_registry.schedule_value_change(poseidon2_hash(tagging_public_key.serialize())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters