Skip to content

Commit

Permalink
feat: PublicKeys struct (#6333)
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan committed May 13, 2024
1 parent e7d2dd6 commit 2633cfc
Show file tree
Hide file tree
Showing 42 changed files with 405 additions and 354 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const partialAddress = Fr.fromString(
"0x200e9a6c2d2e8352012e51c6637659713d336405c29386c7c4ac56779ab54fa7"
);

const completeAddress = CompleteAddress.create(
const completeAddress = new CompleteAddress(
aztecAddress,
publicKey,
partialKey
Expand Down
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: 1 addition & 5 deletions yarn-project/accounts/src/single_key/account_contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ 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.toFields(), ...signature.toBuffer(), this.account.partialAddress];
return Promise.resolve(new AuthWitness(messageHash, witness));
}
}

0 comments on commit 2633cfc

Please sign in to comment.