Skip to content

Commit

Permalink
feat: PublicKeys struct
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan committed May 10, 2024
1 parent 85958f5 commit 2a97317
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 106 deletions.
3 changes: 2 additions & 1 deletion noir-projects/aztec-nr/aztec/src/keys.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
mod getters;
mod point_to_symmetric_key;
mod public_keys;

// Add once enabled in key registry:
// get_ovpk_m,
// get_tpk_m
use crate::keys::getters::{get_npk_m, get_ivpk_m};
use crate::keys::{getters::{get_npk_m, get_ivpk_m}, public_keys::PublicKeys};
31 changes: 8 additions & 23 deletions noir-projects/aztec-nr/aztec/src/keys/getters.nr
Original file line number Diff line number Diff line change
@@ -1,24 +1,13 @@
use dep::protocol_types::{
address::{AztecAddress, PublicKeysHash}, constants::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, oracle::keys::get_public_keys_and_partial_address,
keys::public_keys::{PublicKeys, NULLIFIER_INDEX, INCOMING_INDEX},
state_vars::{
map::derive_storage_slot_in_map,
shared_mutable::shared_mutable_private_getter::SharedMutablePrivateGetter
}
};

// Note: In fetch_key_from_registry we expect that the shared mutable slot is index * 2 + 1 for the x coordinate and
// index * 2 + 2 for the y coordinate. For example, the npk_m x coordinates will be stored in a map at storage slot
// 0 * 2 + 1 = 1, and the npk_m y coordinates at 2 * 2 + 2 = 6. If 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;

global DELAY = 5;

pub fn get_npk_m(context: &mut PrivateContext, address: AztecAddress) -> GrumpkinPoint {
Expand Down Expand Up @@ -46,7 +35,9 @@ fn get_master_key(
let key = fetch_key_from_registry(context, key_index, address);
if key.is_zero() {
// Keys were not registered in registry yet --> fetch key from PXE
fetch_and_constrain_keys(address)[key_index]
let keys = fetch_and_constrain_keys(address);
// Return the corresponding to index
keys.get_key_by_index(key_index)
} else {
// Keys were registered --> return the key
key
Expand Down Expand Up @@ -80,18 +71,12 @@ fn fetch_key_from_registry(
}

// 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] {
fn fetch_and_constrain_keys(address: AztecAddress) -> PublicKeys {
let (public_keys, partial_address) = get_public_keys_and_partial_address(address);

let npk_m = public_keys[0];
let ivpk_m = public_keys[1];
let ovpk_m = public_keys[2];
let tpk_m = public_keys[3];

let public_keys_hash = PublicKeysHash::compute(npk_m, ivpk_m, ovpk_m, tpk_m);
let computed_address = AztecAddress::compute(public_keys_hash, partial_address);
let computed_address = AztecAddress::compute(public_keys.hash(), partial_address);

assert(computed_address.eq(address));

[npk_m, ivpk_m, ovpk_m, tpk_m]
public_keys
}
117 changes: 117 additions & 0 deletions noir-projects/aztec-nr/aztec/src/keys/public_keys.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use dep::protocol_types::{
address::PublicKeysHash, constants::GENERATOR_INDEX__PUBLIC_KEYS_HASH, hash::poseidon2_hash,
grumpkin_point::GrumpkinPoint, traits::{Deserialize, Serialize}
};

// Note: In fetch_key_from_registry we expect that the shared mutable slot is index * 2 + 1 for the x coordinate and
// index * 2 + 2 for the y coordinate. For example, the npk_m x coordinates will be stored in a map at storage slot
// 0 * 2 + 1 = 1, and the npk_m y coordinates at 2 * 2 + 2 = 6. If 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;

global PUBLIC_KEYS_LENGTH = 8;

struct PublicKeys {
npk_m: GrumpkinPoint,
ivpk_m: GrumpkinPoint,
ovpk_m: GrumpkinPoint,
tpk_m: GrumpkinPoint,
}

impl PublicKeys {
pub fn hash(self) -> PublicKeysHash {
PublicKeysHash::from_field(
poseidon2_hash(
[
self.npk_m.x,
self.npk_m.y,
self.ivpk_m.x,
self.ivpk_m.y,
self.ovpk_m.x,
self.ovpk_m.y,
self.tpk_m.x,
self.tpk_m.y,
GENERATOR_INDEX__PUBLIC_KEYS_HASH
]
)
)
}

pub fn get_key_by_index(self, index: Field) -> GrumpkinPoint {
assert(index as u8 <= 4, "Invalid key index");
if index == NULLIFIER_INDEX {
self.npk_m
} else if index == INCOMING_INDEX {
self.ivpk_m
} else if index == OUTGOING_INDEX {
self.ovpk_m
} else {
self.tpk_m
}
}
}

impl Serialize<PUBLIC_KEYS_LENGTH> for PublicKeys {
fn serialize(self) -> [Field; PUBLIC_KEYS_LENGTH] {
[
self.npk_m.x,
self.npk_m.y,
self.ivpk_m.x,
self.ivpk_m.y,
self.ovpk_m.x,
self.ovpk_m.y,
self.tpk_m.x,
self.tpk_m.y,
]
}
}

impl Deserialize<PUBLIC_KEYS_LENGTH> for PublicKeys {
fn deserialize(serialized: [Field; PUBLIC_KEYS_LENGTH]) -> PublicKeys {
PublicKeys {
npk_m: GrumpkinPoint { x: serialized[0], y: serialized[1] },
ivpk_m: GrumpkinPoint { x: serialized[2], y: serialized[3] },
ovpk_m: GrumpkinPoint { x: serialized[4], y: serialized[5] },
tpk_m: GrumpkinPoint { x: serialized[6], y: serialized[7] },
}
}
}

#[test]
fn compute_public_keys_hash() {
let keys = PublicKeys {
npk_m: GrumpkinPoint { x: 1, y: 2 },
ivpk_m: GrumpkinPoint { x: 3, y: 4 },
ovpk_m: GrumpkinPoint { x: 5, y: 6 },
tpk_m: GrumpkinPoint { x: 7, y: 8 }
};

let actual = keys.hash();
let expected_public_keys_hash = 0x1936abe4f6a920d16a9f6917f10a679507687e2cd935dd1f1cdcb1e908c027f3;
assert(actual.to_field() == expected_public_keys_hash);
}

#[test]
fn test_public_keys_serialization() {
let keys = PublicKeys {
npk_m: GrumpkinPoint { x: 1, y: 2 },
ivpk_m: GrumpkinPoint { x: 3, y: 4 },
ovpk_m: GrumpkinPoint { x: 5, y: 6 },
tpk_m: GrumpkinPoint { x: 7, y: 8 }
};

let serialized = keys.serialize();
let deserialized = PublicKeys::deserialize(serialized);

assert_eq(keys.npk_m.x, deserialized.npk_m.x);
assert_eq(keys.npk_m.y, deserialized.npk_m.y);
assert_eq(keys.ivpk_m.x, deserialized.ivpk_m.x);
assert_eq(keys.ivpk_m.y, deserialized.ivpk_m.y);
assert_eq(keys.ovpk_m.x, deserialized.ovpk_m.x);
assert_eq(keys.ovpk_m.y, deserialized.ovpk_m.y);
assert_eq(keys.tpk_m.x, deserialized.tpk_m.x);
assert_eq(keys.tpk_m.y, deserialized.tpk_m.y);
}
16 changes: 10 additions & 6 deletions noir-projects/aztec-nr/aztec/src/oracle/keys.nr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::keys::PublicKeys;
use dep::protocol_types::{address::{AztecAddress, PartialAddress}, grumpkin_point::GrumpkinPoint};

#[oracle(getPublicKeysAndPartialAddress)]
Expand All @@ -7,14 +8,17 @@ unconstrained fn get_public_keys_and_partial_address_oracle_wrapper(address: Azt
get_public_keys_and_partial_address_oracle(address)
}

fn get_public_keys_and_partial_address(address: AztecAddress) -> ([GrumpkinPoint; 4], PartialAddress) {
fn get_public_keys_and_partial_address(address: AztecAddress) -> (PublicKeys, 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 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]);

([nullifier_pub_key, incoming_pub_key, outgoing_pub_key, tagging_pub_key], partial_address)
(keys, partial_address)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ contract KeyRegistry {
use dep::authwit::auth::assert_current_call_valid_authwit_public;

use dep::aztec::{
state_vars::{SharedMutable, Map},
protocol_types::{grumpkin_point::GrumpkinPoint, address::{AztecAddress, PartialAddress, PublicKeysHash}}
keys::PublicKeys, state_vars::{SharedMutable, Map},
protocol_types::{grumpkin_point::GrumpkinPoint, address::{AztecAddress, PartialAddress}}
};

global KEY_ROTATION_DELAY = 5;
Expand Down Expand Up @@ -42,16 +42,8 @@ contract KeyRegistry {
}

#[aztec(public)]
fn register(
address: AztecAddress,
partial_address: PartialAddress,
npk_m: GrumpkinPoint,
ivpk_m: GrumpkinPoint,
ovpk_m: GrumpkinPoint,
tpk_m: GrumpkinPoint
) {
let public_keys_hash = PublicKeysHash::compute(npk_m, ivpk_m, ovpk_m, tpk_m);
let computed_address = AztecAddress::compute(public_keys_hash, partial_address);
fn register(address: AztecAddress, partial_address: PartialAddress, keys: PublicKeys) {
let computed_address = AztecAddress::compute(keys.hash(), partial_address);

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

Expand All @@ -64,10 +56,10 @@ contract KeyRegistry {
// let tpk_m_x_registry = storage.tpk_m_x_registry.at(address);
// let tpk_m_y_registry = storage.tpk_m_y_registry.at(address);

npk_m_x_registry.schedule_value_change(npk_m.x);
npk_m_y_registry.schedule_value_change(npk_m.y);
ivpk_m_x_registry.schedule_value_change(ivpk_m.x);
ivpk_m_y_registry.schedule_value_change(ivpk_m.y);
npk_m_x_registry.schedule_value_change(keys.npk_m.x);
npk_m_y_registry.schedule_value_change(keys.npk_m.y);
ivpk_m_x_registry.schedule_value_change(keys.ivpk_m.x);
ivpk_m_y_registry.schedule_value_change(keys.ivpk_m.y);
// Commented out as we hit the max enqueued public calls limit when not done so
// ovpk_m_x_registry.schedule_value_change(ovpk_m.x);
// ovpk_m_y_registry.schedule_value_change(ovpk_m.y);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use dep::authwit::auth_witness;
use dep::aztec::protocol_types::{address::PartialAddress, grumpkin_point::GrumpkinPoint};
use dep::aztec::{protocol_types::{address::PartialAddress, grumpkin_point::GrumpkinPoint}, keys::PublicKeys};

struct AuthWitness {
npk_m: GrumpkinPoint, ivpk_m: GrumpkinPoint, ovpk_m: GrumpkinPoint, tpk_m: GrumpkinPoint,
keys: PublicKeys,
signature: [u8; 64],
partial_address: PartialAddress,
}
Expand All @@ -14,10 +14,12 @@ impl AuthWitness {
signature[i] = values[i + 8] as u8;
}
Self {
npk_m: GrumpkinPoint::new(values[0], values[1]),
ivpk_m: GrumpkinPoint::new(values[2], values[3]),
ovpk_m: GrumpkinPoint::new(values[4], values[5]),
tpk_m: GrumpkinPoint::new(values[6], values[7]),
keys: PublicKeys {
npk_m: GrumpkinPoint::new(values[0], values[1]),
ivpk_m: GrumpkinPoint::new(values[2], values[3]),
ovpk_m: GrumpkinPoint::new(values[4], values[5]),
tpk_m: GrumpkinPoint::new(values[6], values[7])
},
signature,
partial_address: PartialAddress::from_field(values[72])
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
use dep::aztec::prelude::AztecAddress;
use dep::aztec::protocol_types::address::PublicKeysHash;
use dep::std::{schnorr::verify_signature_slice};
use dep::aztec::prelude::AztecAddress;
use crate::auth_oracle::AuthWitness;

pub fn recover_address(message_hash: Field, witness: AuthWitness) -> AztecAddress {
let message_bytes = message_hash.to_be_bytes(32);
// In a single key account contract we re-used ivpk_m as signing key
let verification = verify_signature_slice(
witness.ivpk_m.x,
witness.ivpk_m.y,
witness.keys.ivpk_m.x,
witness.keys.ivpk_m.y,
witness.signature,
message_bytes
);
assert(verification == true);

AztecAddress::compute(
PublicKeysHash::compute(witness.npk_m, witness.ivpk_m, witness.ovpk_m, witness.tpk_m),
witness.partial_address
)
AztecAddress::compute(witness.keys.hash(), witness.partial_address)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use crate::{
constants::{GENERATOR_INDEX__PARTIAL_ADDRESS, GENERATOR_INDEX__PUBLIC_KEYS_HASH},
hash::pedersen_hash, grumpkin_point::GrumpkinPoint, traits::{ToField, Serialize, Deserialize},
hash::poseidon2_hash
};
use crate::traits::{ToField, Serialize, Deserialize};

// Public keys hash. Used in the computation of an address.
struct PublicKeysHash {
Expand Down Expand Up @@ -38,24 +34,6 @@ impl PublicKeysHash {
Self { inner: field }
}

pub fn compute(npk_m: GrumpkinPoint, ivpk_m: GrumpkinPoint, ovpk_m: GrumpkinPoint, tpk_m: GrumpkinPoint) -> Self {
PublicKeysHash::from_field(
poseidon2_hash(
[
npk_m.x,
npk_m.y,
ivpk_m.x,
ivpk_m.y,
ovpk_m.x,
ovpk_m.y,
tpk_m.x,
tpk_m.y,
GENERATOR_INDEX__PUBLIC_KEYS_HASH
]
)
)
}

pub fn to_field(self) -> Field {
self.inner
}
Expand All @@ -64,15 +42,3 @@ impl PublicKeysHash {
assert(self.to_field() == 0);
}
}

#[test]
fn compute_public_keys_hash() {
let npk_m = GrumpkinPoint { x: 1, y: 2 };
let ivpk_m = GrumpkinPoint { x: 3, y: 4 };
let ovpk_m = GrumpkinPoint { x: 5, y: 6 };
let tpk_m = GrumpkinPoint { x: 7, y: 8 };

let actual = PublicKeysHash::compute(npk_m, ivpk_m, ovpk_m, tpk_m);
let expected_public_keys_hash = 0x1936abe4f6a920d16a9f6917f10a679507687e2cd935dd1f1cdcb1e908c027f3;
assert(actual.to_field() == expected_public_keys_hash);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, constants::PARTIAL_STATE_REFERENCE_LENGTH,
traits::{Deserialize, Empty, Hash, Serialize}
traits::{Deserialize, Empty, Serialize}
};

struct PartialStateReference {
Expand Down
6 changes: 5 additions & 1 deletion yarn-project/accounts/src/single_key/account_contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ class SingleKeyAuthWitnessProvider implements AuthWitnessProvider {
createAuthWit(messageHash: Fr): Promise<AuthWitness> {
const schnorr = new Schnorr();
const signature = schnorr.constructSignature(messageHash.toBuffer(), this.privateKey);
const witness = [...this.account.publicKeys.flatMap(pk => pk.toFields()), ...signature.toBuffer(), this.account.partialAddress];
const witness = [
...this.account.publicKeys.flatMap(pk => pk.toFields()),
...signature.toBuffer(),
this.account.partialAddress,
];
return Promise.resolve(new AuthWitness(messageHash, witness));
}
}
Loading

0 comments on commit 2a97317

Please sign in to comment.