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: PublicKeys struct #6333

Merged
merged 7 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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));
}
}
Loading
Loading