diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/standard_affine_point.hpp b/barretenberg/cpp/src/barretenberg/vm2/common/standard_affine_point.hpp index 680f318007a7..1f0b271724fd 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/common/standard_affine_point.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/common/standard_affine_point.hpp @@ -92,9 +92,8 @@ template class StandardAffinePoint { } private: - // TODO(MW): Clarify - docs here no longer true // The affine point for operations, this will always match the raw coordinates unless the point is infinity. - // In that case, the point will be set to barretenberg's infinity representation - which is not (0,0). + // In that case, the point will be set to AffinePoint's infinity representation - which may not be (0,0). AffinePoint point; // These are the raw x and y coordinates, that are set when constructing the point. When an operation results // in infinity, these will be set to (0,0) to match noir's expected representation. diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/ecc.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/ecc.cpp index c6a204a1c560..5d4244697de8 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/ecc.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/ecc.cpp @@ -138,10 +138,10 @@ void Ecc::add(MemoryInterface& memory, .result = result, .dst_address = dst_address }); } catch (const InternalEccException& e) { - // Note this point is not on the curve, but corresponds - // to default values the circuit will assign. - // TODO(MW): This is now a point on the curve technically (inf) - check this doesnt cause issues now res.is_inf - // is true: + // Note this point is technically infinity, but we are treating it as 'empty' to corresponds + // to default values the circuit will assign. Since we have caught an InternalEccException, + // we have an error which the circuit should recognise and assign sel_should_exec == 0, so res will not be + // treated as inf. EmbeddedCurvePoint res = EmbeddedCurvePoint(0, 0); add_memory_events.emit({ .execution_clk = execution_clk, .space_id = space_id, diff --git a/noir-projects/aztec-nr/aztec/src/keys/ecdh_shared_secret.nr b/noir-projects/aztec-nr/aztec/src/keys/ecdh_shared_secret.nr index 4770d3e7cc65..dc07036bd1d0 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/ecdh_shared_secret.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/ecdh_shared_secret.nr @@ -2,7 +2,7 @@ use crate::protocol::{ address::aztec_address::AztecAddress, constants::{DOM_SEP__APP_SILOED_ECDH_SHARED_SECRET, DOM_SEP__ECDH_FIELD_MASK, DOM_SEP__ECDH_SUBKEY}, hash::poseidon2_hash_with_separator, - point::Point, + point::EmbeddedCurvePoint, scalar::Scalar, traits::{FromField, ToField}, }; @@ -18,16 +18,14 @@ use std::{embedded_curve_ops::multi_scalar_mul, ops::Neg}; /// Shared secret S = esk * Pk = sk * Epk /// /// See also: https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman -pub fn derive_ecdh_shared_secret(secret: Scalar, public_key: Point) -> Point { - // TODO(F-553): Drop the `.to_embedded()` / `.into()` round-trip once the custom `Point` wrapper is removed and we - // use `EmbeddedCurvePoint` directly. - multi_scalar_mul([public_key.to_embedded()], [secret]).into() +pub fn derive_ecdh_shared_secret(secret: Scalar, public_key: EmbeddedCurvePoint) -> EmbeddedCurvePoint { + multi_scalar_mul([public_key], [secret]) } /// Computes an app-siloed shared secret from a raw ECDH shared secret point and a contract address. /// /// `s_app = h(DOM_SEP__APP_SILOED_ECDH_SHARED_SECRET, S.x, S.y, contract_address)` -pub fn compute_app_siloed_shared_secret(shared_secret: Point, contract_address: AztecAddress) -> Field { +pub fn compute_app_siloed_shared_secret(shared_secret: EmbeddedCurvePoint, contract_address: AztecAddress) -> Field { poseidon2_hash_with_separator( [shared_secret.x, shared_secret.y, contract_address.to_field()], DOM_SEP__APP_SILOED_ECDH_SHARED_SECRET, @@ -54,10 +52,9 @@ unconstrained fn test_consistency_with_typescript() { lo: 0x00000000000000000000000000000000649e7ca01d9de27b21624098b897babd, hi: 0x0000000000000000000000000000000023b3127c127b1f29a7adff5cccf8fb06, }; - let point = Point { + let point = EmbeddedCurvePoint { x: 0x2688431c705a5ff3e6c6f2573c9e3ba1c1026d2251d0dbbf2d810aa53fd1d186, y: 0x1e96887b117afca01c00468264f4f80b5bb16d94c1808a448595f115556e5c8e, - is_infinite: false, }; let shared_secret = derive_ecdh_shared_secret(secret, point); @@ -65,10 +62,9 @@ unconstrained fn test_consistency_with_typescript() { // This is just pasted from a test run. The original typescript code from which this could be generated seems to // have been deleted by someone, and soon the typescript code for encryption and decryption won't be needed, so // this will have to do. - let hard_coded_shared_secret = Point { + let hard_coded_shared_secret = EmbeddedCurvePoint { x: 0x15d55a5b3b2caa6a6207f313f05c5113deba5da9927d6421bcaa164822b911bc, y: 0x0974c3d0825031ae933243d653ebb1a0b08b90ee7f228f94c5c74739ea3c871e, - is_infinite: false, }; assert_eq(shared_secret, hard_coded_shared_secret); } @@ -78,8 +74,8 @@ unconstrained fn test_shared_secret_computation_in_both_directions() { let secret_a = Scalar { lo: 0x1234, hi: 0x2345 }; let secret_b = Scalar { lo: 0x3456, hi: 0x4567 }; - let pk_a: Point = std::embedded_curve_ops::fixed_base_scalar_mul(secret_a).into(); - let pk_b: Point = std::embedded_curve_ops::fixed_base_scalar_mul(secret_b).into(); + let pk_a: EmbeddedCurvePoint = std::embedded_curve_ops::fixed_base_scalar_mul(secret_a); + let pk_b: EmbeddedCurvePoint = std::embedded_curve_ops::fixed_base_scalar_mul(secret_b); let shared_secret = derive_ecdh_shared_secret(secret_a, pk_b); let shared_secret_alt = derive_ecdh_shared_secret(secret_b, pk_a); @@ -92,8 +88,8 @@ unconstrained fn test_shared_secret_computation_from_address_in_both_directions( let secret_a = Scalar { lo: 0x1234, hi: 0x2345 }; let secret_b = Scalar { lo: 0x3456, hi: 0x4567 }; - let mut pk_a: Point = std::embedded_curve_ops::fixed_base_scalar_mul(secret_a).into(); - let mut pk_b: Point = std::embedded_curve_ops::fixed_base_scalar_mul(secret_b).into(); + let mut pk_a: EmbeddedCurvePoint = std::embedded_curve_ops::fixed_base_scalar_mul(secret_a); + let mut pk_b: EmbeddedCurvePoint = std::embedded_curve_ops::fixed_base_scalar_mul(secret_b); let address_b = AztecAddress::from_field(pk_b.x); @@ -120,7 +116,7 @@ unconstrained fn test_shared_secret_computation_from_address_in_both_directions( #[test] unconstrained fn test_app_siloed_shared_secret_differs_per_contract() { let secret_a = Scalar { lo: 0x1234, hi: 0x2345 }; - let pk_b: Point = std::embedded_curve_ops::fixed_base_scalar_mul(Scalar { lo: 0x3456, hi: 0x4567 }).into(); + let pk_b: EmbeddedCurvePoint = std::embedded_curve_ops::fixed_base_scalar_mul(Scalar { lo: 0x3456, hi: 0x4567 }); let shared_secret = derive_ecdh_shared_secret(secret_a, pk_b); diff --git a/noir-projects/aztec-nr/aztec/src/keys/ephemeral.nr b/noir-projects/aztec-nr/aztec/src/keys/ephemeral.nr index e7758e43579a..7b2be095ca34 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/ephemeral.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/ephemeral.nr @@ -1,11 +1,11 @@ use std::embedded_curve_ops::{EmbeddedCurveScalar, fixed_base_scalar_mul}; -use crate::protocol::{point::Point, scalar::Scalar}; +use crate::protocol::{point::EmbeddedCurvePoint, scalar::Scalar}; use crate::{oracle::random::random, utils::point::get_sign_of_point}; /// Generates a random ephemeral key pair. -pub fn generate_ephemeral_key_pair() -> (Scalar, Point) { +pub fn generate_ephemeral_key_pair() -> (Scalar, EmbeddedCurvePoint) { // @todo Need to draw randomness from the full domain of Fq not only Fr // Safety: we use the randomness to preserve the privacy of both the sender and recipient via encryption, so a @@ -17,9 +17,7 @@ pub fn generate_ephemeral_key_pair() -> (Scalar, Point) { // TODO(#12757): compute the key pair without constraining eph_sk twice (once in from_field, once in the black box // called by fixed_base_scalar_mul). let eph_sk = EmbeddedCurveScalar::from_field(randomness); - // TODO(F-553): Drop the `.into()` once the custom `Point` wrapper is removed and we use `EmbeddedCurvePoint` - // directly. Applies to the other `fixed_base_scalar_mul(...).into()` call sites in this file as well. - let eph_pk: Point = fixed_base_scalar_mul(eph_sk).into(); + let eph_pk: EmbeddedCurvePoint = fixed_base_scalar_mul(eph_sk); (eph_sk, eph_pk) } @@ -31,13 +29,13 @@ pub fn generate_ephemeral_key_pair() -> (Scalar, Point) { /// /// This is useful as it means it is possible to just broadcast the x-coordinate as a single `Field` and then /// reconstruct the original public key using [`crate::utils::point::point_from_x_coord_and_sign`] with `sign: true`. -pub fn generate_positive_ephemeral_key_pair() -> (Scalar, Point) { +pub fn generate_positive_ephemeral_key_pair() -> (Scalar, EmbeddedCurvePoint) { // Safety: we use the randomness to preserve the privacy of both the sender and recipient via encryption, so a // malicious sender could use non-random values to reveal the plaintext. But they already know it themselves // anyway, and so the recipient already trusts them to not disclose this information. We can therefore assume that // the sender will cooperate in the random value generation. let eph_sk = unsafe { generate_secret_key_for_positive_public_key() }; - let eph_pk: Point = fixed_base_scalar_mul(eph_sk).into(); + let eph_pk: EmbeddedCurvePoint = fixed_base_scalar_mul(eph_sk); assert(get_sign_of_point(eph_pk), "Got an ephemeral public key with a negative y coordinate"); @@ -53,7 +51,7 @@ unconstrained fn generate_secret_key_for_positive_public_key() -> EmbeddedCurveS // @todo Need to draw randomness from the full domain of Fq not only Fr sk = EmbeddedCurveScalar::from_field(random()); - let pk: Point = fixed_base_scalar_mul(sk).into(); + let pk: EmbeddedCurvePoint = fixed_base_scalar_mul(sk); if get_sign_of_point(pk) { break; } diff --git a/noir-projects/aztec-nr/aztec/src/messages/encryption/poseidon2.nr b/noir-projects/aztec-nr/aztec/src/messages/encryption/poseidon2.nr index 1704be0162b0..c2ecac779d3f 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/encryption/poseidon2.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/encryption/poseidon2.nr @@ -1,7 +1,7 @@ use std::hash::poseidon2_permutation; use std::option::Option; -use crate::protocol::point::Point; +use crate::protocol::point::EmbeddedCurvePoint; global TWO_POW_128: Field = 0x100000000000000000000000000000000; @@ -27,7 +27,7 @@ global TWO_POW_128: Field = 0x100000000000000000000000000000000; /// @param encryption_nonce is only needed if your use case needs to protect against replay attacks. pub fn poseidon2_encrypt( msg: [Field; L], - shared_secret: Point, + shared_secret: EmbeddedCurvePoint, encryption_nonce: Field, ) -> [Field; ((L + 2) / 3) * 3 + 1] { // TODO: assert(encryption_nonce < 2^128), assert(L < 2^120); @@ -78,7 +78,7 @@ pub fn poseidon2_encrypt( pub fn poseidon2_decrypt( ciphertext: [Field; ((L + 3 - 1) / 3) * 3 + 1], - shared_secret: Point, + shared_secret: EmbeddedCurvePoint, encryption_nonce: Field, ) -> Option<[Field; L]> { let mut s = [0, shared_secret.x, shared_secret.y, encryption_nonce + (L as Field) * TWO_POW_128]; @@ -139,7 +139,6 @@ pub fn poseidon2_decrypt( } mod test { - use crate::protocol::point::Point; use super::{poseidon2_decrypt, poseidon2_encrypt, TWO_POW_128}; use std::embedded_curve_ops::{EmbeddedCurveScalar, fixed_base_scalar_mul, multi_scalar_mul}; @@ -152,7 +151,7 @@ mod test { let eph_sk = 0x5678; let eph_pk = fixed_base_scalar_mul(EmbeddedCurveScalar::from_field(eph_sk)); - let shared_secret: Point = multi_scalar_mul([bob_pk], [EmbeddedCurveScalar::from_field(eph_sk)]).into(); + let shared_secret = multi_scalar_mul([bob_pk], [EmbeddedCurveScalar::from_field(eph_sk)]); let encryption_nonce = 3; // TODO. Can even be a timestamp. Why is this even needed? @@ -162,7 +161,7 @@ mod test { // Bob sees: [Epk, ciphertext, encryption_nonce]: - let shared_secret: Point = multi_scalar_mul([eph_pk], [EmbeddedCurveScalar::from_field(bob_sk)]).into(); + let shared_secret = multi_scalar_mul([eph_pk], [EmbeddedCurveScalar::from_field(bob_sk)]); let result = poseidon2_decrypt(ciphertext, shared_secret, encryption_nonce); @@ -193,7 +192,7 @@ mod test { let eph_sk = 0x5678; let eph_pk = fixed_base_scalar_mul(EmbeddedCurveScalar::from_field(eph_sk)); - let shared_secret: Point = multi_scalar_mul([bob_pk], [EmbeddedCurveScalar::from_field(eph_sk)]).into(); + let shared_secret = multi_scalar_mul([bob_pk], [EmbeddedCurveScalar::from_field(eph_sk)]); let msg = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; @@ -205,7 +204,7 @@ mod test { // Bob sees: [Epk, ciphertext, encryption_nonce]: - let mut shared_secret: Point = multi_scalar_mul([eph_pk], [EmbeddedCurveScalar::from_field(bob_sk)]).into(); + let mut shared_secret = multi_scalar_mul([eph_pk], [EmbeddedCurveScalar::from_field(bob_sk)]); // Let's intentionally corrupt the shared secret, so that decryption should fail shared_secret.x += 1; @@ -222,7 +221,7 @@ mod test { let bob_pk = fixed_base_scalar_mul(EmbeddedCurveScalar::from_field(bob_sk)); let eph_sk = 0x5678; - let shared_secret: Point = multi_scalar_mul([bob_pk], [EmbeddedCurveScalar::from_field(eph_sk)]).into(); + let shared_secret = multi_scalar_mul([bob_pk], [EmbeddedCurveScalar::from_field(eph_sk)]); let encryption_nonce = 3; // TODO. Can even be a timestamp. Why is this even needed? diff --git a/noir-projects/aztec-nr/aztec/src/oracle/keys.nr b/noir-projects/aztec-nr/aztec/src/oracle/keys.nr index 45a733f6da06..c1ba98a4794c 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/keys.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/keys.nr @@ -1,4 +1,8 @@ -use crate::protocol::{address::{AztecAddress, PartialAddress}, point::Point, public_keys::{IvpkM, PublicKeys}}; +use crate::protocol::{ + address::{AztecAddress, PartialAddress}, + point::EmbeddedCurvePoint, + public_keys::{IvpkM, PublicKeys}, +}; // TODO(F-498): review naming consistency pub unconstrained fn get_public_keys_and_partial_address(address: AztecAddress) -> (PublicKeys, PartialAddress) { @@ -20,7 +24,7 @@ pub unconstrained fn try_get_public_keys_and_partial_address( get_public_keys_and_partial_address_oracle(address).map(|result: [Field; 6]| { let keys = PublicKeys { npk_m_hash: result[0], - ivpk_m: IvpkM { inner: Point { x: result[1], y: result[2], is_infinite: false } }, + ivpk_m: IvpkM { inner: EmbeddedCurvePoint { x: result[1], y: result[2] } }, ovpk_m_hash: result[3], tpk_m_hash: result[4], }; diff --git a/noir-projects/aztec-nr/aztec/src/oracle/shared_secret.nr b/noir-projects/aztec-nr/aztec/src/oracle/shared_secret.nr index 04a0cd198cda..b8044fa82f93 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/shared_secret.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/shared_secret.nr @@ -1,5 +1,5 @@ use crate::ephemeral::EphemeralArray; -use crate::protocol::{address::AztecAddress, hash::sha256_to_field, point::Point}; +use crate::protocol::{address::AztecAddress, hash::sha256_to_field, point::EmbeddedCurvePoint}; global GET_SHARED_SECRETS_REQUEST_SLOT: Field = sha256_to_field("AZTEC_NR::GET_SHARED_SECRETS_REQUEST_SLOT".as_bytes()); @@ -11,7 +11,11 @@ unconstrained fn get_shared_secrets_oracle( ) -> Field {} /// Convenience wrapper around [`get_shared_secrets`] for a single ephemeral public key. -pub unconstrained fn get_shared_secret(address: AztecAddress, eph_pk: Point, contract_address: AztecAddress) -> Field { +pub unconstrained fn get_shared_secret( + address: AztecAddress, + eph_pk: EmbeddedCurvePoint, + contract_address: AztecAddress, +) -> Field { get_shared_secrets::<1>(address, BoundedVec::from_array([eph_pk]), contract_address).get(0) } @@ -36,10 +40,10 @@ pub unconstrained fn get_shared_secret(address: AztecAddress, eph_pk: Point, con /// [`derive_shared_secret_subkey`](crate::keys::ecdh_shared_secret::derive_shared_secret_subkey). pub unconstrained fn get_shared_secrets( address: AztecAddress, - eph_pks: BoundedVec, + eph_pks: BoundedVec, contract_address: AztecAddress, ) -> BoundedVec { - let request_array: EphemeralArray = EphemeralArray::at(GET_SHARED_SECRETS_REQUEST_SLOT).clear(); + let request_array: EphemeralArray = EphemeralArray::at(GET_SHARED_SECRETS_REQUEST_SLOT).clear(); eph_pks.for_each(|pk| request_array.push(pk)); let response_slot = get_shared_secrets_oracle(address, request_array.slot, contract_address); diff --git a/noir-projects/aztec-nr/aztec/src/publish_contract_instance.nr b/noir-projects/aztec-nr/aztec/src/publish_contract_instance.nr index c841288e56c4..07556ba42a95 100644 --- a/noir-projects/aztec-nr/aztec/src/publish_contract_instance.nr +++ b/noir-projects/aztec-nr/aztec/src/publish_contract_instance.nr @@ -19,17 +19,16 @@ pub fn publish_contract_instance_for_public_execution(context: &mut PrivateConte } // Adapted from - // noir-contracts/contracts/protocol/contract_instance_registry_contract/src/interface/ContractInstanceRegistry.nr + // noir-contracts/contracts/protocol/contract_instance_registry_contract/src/interface/ContractInstanceRegistry.ts // That file // was autogenerated running the following command from noir-projects/noir-contracts: - // ../../yarn-project/node_modules/.bin/aztec-cli codegen - // target/contract_instance_registry_contract-ContractInstanceRegistry.json --nr -o + // ../../yarn-project/node_modules/.bin/aztec codegen + // target/contract_instance_registry_contract-ContractInstanceRegistry.json -o // ./contracts/contract_instance_registry_contract/src/interface // - // PublicKeys serializes to 6 fields (npk_m_hash, ivpk_m as a 3-field point with - // is_infinite, ovpk_m_hash, tpk_m_hash). Total args: 4 (salt, class_id, init_hash, - // immutables_hash) + 6 (public_keys) + 1 (universal_deploy) = 11. - let mut serialized_args = [0; 11]; + // PublicKeys serializes to 5 fields (npk_m_hash, ivpk_m as a 2-field point, ovpk_m_hash, tpk_m_hash). Total args: 4 + // (salt, class_id, init_hash, immutables_hash) + 5 (public_keys) + 1 (universal_deploy) = 10. + let mut serialized_args = [0; 10]; serialized_args[0] = instance.salt; serialized_args[1] = instance.contract_class_id.to_field(); serialized_args[2] = instance.initialization_hash; @@ -37,17 +36,17 @@ pub fn publish_contract_instance_for_public_execution(context: &mut PrivateConte let serialized_public_keys = instance.public_keys.serialize(); - for i in 0..6 { + for i in 0..5 { serialized_args[i + 4] = serialized_public_keys[i]; } - serialized_args[10] = universal_deploy as Field; + serialized_args[9] = universal_deploy as Field; let _call_result = context.call_private_function( CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, comptime { FunctionSelector::from_signature( - "publish_for_public_execution(Field,(Field),Field,Field,(Field,((Field,Field,bool)),Field,Field),bool)", + "publish_for_public_execution(Field,(Field),Field,Field,(Field,((Field,Field)),Field,Field),bool)", ) }, serialized_args, diff --git a/noir-projects/aztec-nr/aztec/src/utils/point.nr b/noir-projects/aztec-nr/aztec/src/utils/point.nr index ab5b90614ee8..7f2b3b54a7a0 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/point.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/point.nr @@ -1,11 +1,11 @@ -use crate::protocol::{point::Point, utils::field::sqrt}; +use crate::protocol::{point::EmbeddedCurvePoint, utils::field::sqrt}; // I am storing the modulus minus 1 divided by 2 here because full modulus would throw "String literal too large" error // Full modulus is 21888242871839275222246405745257275088548364400416034343698204186575808495617 global BN254_FR_MODULUS_DIV_2: Field = 10944121435919637611123202872628637544274182200208017171849102093287904247808; /// Returns: true if p.y <= MOD_DIV_2, else false. -pub fn get_sign_of_point(p: Point) -> bool { +pub fn get_sign_of_point(p: EmbeddedCurvePoint) -> bool { // We store only a "sign" of the y coordinate because the rest can be derived from the x coordinate. To get the // sign we check if the y coordinate is less or equal than the field's modulus minus 1 divided by 2. Ideally we'd // do `y <= MOD_DIV_2`, but there's no `lte` function, so instead we do `!(y > MOD_DIV_2)`, which is equivalent, @@ -13,18 +13,18 @@ pub fn get_sign_of_point(p: Point) -> bool { !BN254_FR_MODULUS_DIV_2.lt(p.y) } -/// Returns a `Point` in the Grumpkin curve given its x coordinate. +/// Returns an `EmbeddedCurvePoint` in the Grumpkin curve given its x coordinate. /// /// Because not all values in the field are valid x coordinates of points in the curve (i.e. there is no corresponding /// y value in the field that satisfies the curve equation), it may not be possible to reconstruct a `Point`. /// `Option::none()` is returned in such cases. -pub fn point_from_x_coord(x: Field) -> Option { +pub fn point_from_x_coord(x: Field) -> Option { // y ^ 2 = x ^ 3 - 17 let rhs = x * x * x - 17; - sqrt(rhs).map(|y| Point { x, y, is_infinite: false }) + sqrt(rhs).map(|y| EmbeddedCurvePoint { x, y }) } -/// Returns a `Point` in the Grumpkin curve given its x coordinate and sign for the y coordinate. +/// Returns an `EmbeddedCurvePoint` in the Grumpkin curve given its x coordinate and sign for the y coordinate. /// /// Because not all values in the field are valid x coordinates of points in the curve (i.e. there is no corresponding /// y value in the field that satisfies the curve equation), it may not be possible to reconstruct a `Point`. @@ -32,7 +32,7 @@ pub fn point_from_x_coord(x: Field) -> Option { /// /// @param x - The x coordinate of the point @param sign - The "sign" of the y coordinate - determines whether y <= /// (Fr.MODULUS - 1) / 2 -pub fn point_from_x_coord_and_sign(x: Field, sign: bool) -> Option { +pub fn point_from_x_coord_and_sign(x: Field, sign: bool) -> Option { // y ^ 2 = x ^ 3 - 17 let rhs = x * x * x - 17; @@ -40,12 +40,12 @@ pub fn point_from_x_coord_and_sign(x: Field, sign: bool) -> Option { // If there is a square root, we need to ensure it has the correct "sign" let y_is_positive = !BN254_FR_MODULUS_DIV_2.lt(y); let final_y = if y_is_positive == sign { y } else { -y }; - Point { x, y: final_y, is_infinite: false } + EmbeddedCurvePoint { x, y: final_y } }) } mod test { - use crate::protocol::point::Point; + use crate::protocol::point::EmbeddedCurvePoint; use crate::utils::point::{ BN254_FR_MODULUS_DIV_2, get_sign_of_point, point_from_x_coord, point_from_x_coord_and_sign, }; @@ -59,7 +59,7 @@ mod test { assert_eq(p.x, x); assert_eq(p.y, 0x07fc22c7f2c7057571f137fe46ea9c95114282bc95d37d71ec4bfb88de457d4a); - assert_eq(p.is_infinite, false); + assert_eq(p.is_infinite(), false); // Test negative y coordinate let x2 = 0x247371652e55dd74c9af8dbe9fb44931ba29a9229994384bd7077796c14ee2b5; @@ -68,7 +68,7 @@ mod test { assert_eq(p2.x, x2); assert_eq(p2.y, 0x26441aec112e1ae4cee374f42556932001507ad46e255ffb27369c7e3766e5c0); - assert_eq(p2.is_infinite, false); + assert_eq(p2.is_infinite(), false); } #[test] @@ -123,7 +123,7 @@ mod test { unconstrained fn test_get_sign_of_point() { // Derive a point from x = 8, then test both possible y values let point = point_from_x_coord(8).unwrap(); - let neg_point = Point { x: point.x, y: 0 - point.y, is_infinite: false }; + let neg_point = EmbeddedCurvePoint { x: point.x, y: 0 - point.y }; // One should be "positive" (y <= MOD_DIV_2) and one "negative" let sign1 = get_sign_of_point(point); @@ -131,15 +131,15 @@ mod test { assert(sign1 != sign2); // y = 0 should return true (0 <= MOD_DIV_2) - let zero_y_point = Point { x: 0, y: 0, is_infinite: false }; + let zero_y_point = EmbeddedCurvePoint { x: 0, y: 0 }; assert(get_sign_of_point(zero_y_point) == true); // y = MOD_DIV_2 should return true (exactly at boundary) - let boundary_point = Point { x: 0, y: BN254_FR_MODULUS_DIV_2, is_infinite: false }; + let boundary_point = EmbeddedCurvePoint { x: 0, y: BN254_FR_MODULUS_DIV_2 }; assert(get_sign_of_point(boundary_point) == true); // y = MOD_DIV_2 + 1 should return false (just over boundary) - let over_boundary_point = Point { x: 0, y: BN254_FR_MODULUS_DIV_2 + 1, is_infinite: false }; + let over_boundary_point = EmbeddedCurvePoint { x: 0, y: BN254_FR_MODULUS_DIV_2 + 1 }; assert(get_sign_of_point(over_boundary_point) == false); } diff --git a/noir-projects/noir-contracts/contracts/message_discovery/handshake_registry_contract/src/handshake_note.nr b/noir-projects/noir-contracts/contracts/message_discovery/handshake_registry_contract/src/handshake_note.nr index af84b0e17cf0..ded38bc11126 100644 --- a/noir-projects/noir-contracts/contracts/message_discovery/handshake_registry_contract/src/handshake_note.nr +++ b/noir-projects/noir-contracts/contracts/message_discovery/handshake_registry_contract/src/handshake_note.nr @@ -1,7 +1,7 @@ use aztec::{ keys::ecdh_shared_secret::compute_app_siloed_shared_secret, macros::notes::note, - protocol::{address::AztecAddress, point::Point, traits::{Deserialize, Packable, Serialize}}, + protocol::{address::AztecAddress, point::EmbeddedCurvePoint, traits::{Deserialize, Packable, Serialize}}, }; /// A record of a handshake established by the note's owner (the sender). @@ -20,11 +20,11 @@ pub struct HandshakeNote { recipient: AztecAddress, /// The raw ECDH shared-secret point `S = eph_sk * recipient_address_point`. Only this module can read it for /// siloing, and it is never returned by an external function. - secret: Point, + secret: EmbeddedCurvePoint, } impl HandshakeNote { - pub(crate) fn new(shared_secret: Point, handshake_type: u8, recipient: AztecAddress) -> Self { + pub(crate) fn new(shared_secret: EmbeddedCurvePoint, handshake_type: u8, recipient: AztecAddress) -> Self { Self { handshake_type, recipient, secret: shared_secret } } diff --git a/noir-projects/noir-contracts/contracts/protocol/contract_instance_registry_contract/src/main.nr b/noir-projects/noir-contracts/contracts/protocol/contract_instance_registry_contract/src/main.nr index ba22dec62c62..48dafc745fd9 100644 --- a/noir-projects/noir-contracts/contracts/protocol/contract_instance_registry_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/protocol/contract_instance_registry_contract/src/main.nr @@ -106,9 +106,9 @@ pub contract ContractInstanceRegistry { // body, I have removed that check. assert_compatible_oracle_version(); - // 4 prefix fields (salt, class_id, init_hash, immutables_hash) + 6 public-key fields + 1 - // universal_deploy flag = 11. - let serialized_params: [Field; 11] = [salt, contract_class_id.to_field(), initialization_hash, immutables_hash] + // 4 prefix fields (salt, class_id, init_hash, immutables_hash) + 5 public-key fields + 1 + // universal_deploy flag = 10. + let serialized_params: [Field; 10] = [salt, contract_class_id.to_field(), initialization_hash, immutables_hash] .concat(public_keys.serialize()) .concat([universal_deploy.to_field()]); diff --git a/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr index 610f8176a3a0..a48955fcb6fe 100644 --- a/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr @@ -19,7 +19,7 @@ pub contract AvmTest { get_contract_instance_immutables_hash_avm, get_contract_instance_initialization_hash_avm, }; use aztec::protocol::abis::function_selector::FunctionSelector; - use aztec::protocol::{address::{AztecAddress, EthAddress}, point::Point, scalar::Scalar}; + use aztec::protocol::{address::{AztecAddress, EthAddress}, point::EmbeddedCurvePoint, scalar::Scalar}; use aztec::protocol::{ constants::{ CANONICAL_AUTH_REGISTRY_ADDRESS, CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS, FEE_JUICE_ADDRESS, @@ -195,13 +195,13 @@ pub contract AvmTest { } #[external("public")] - fn elliptic_curve_add(lhs: Point, rhs: Point) -> Point { + fn elliptic_curve_add(lhs: EmbeddedCurvePoint, rhs: EmbeddedCurvePoint) -> EmbeddedCurvePoint { lhs + rhs } #[external("public")] - fn elliptic_curve_add_and_double() -> Point { - let g = Point { x: GRUMPKIN_ONE_X, y: GRUMPKIN_ONE_Y, is_infinite: false }; + fn elliptic_curve_add_and_double() -> EmbeddedCurvePoint { + let g = EmbeddedCurvePoint { x: GRUMPKIN_ONE_X, y: GRUMPKIN_ONE_Y }; let doubled = g + g; let added = g + doubled; @@ -209,20 +209,23 @@ pub contract AvmTest { } #[external("public")] - fn variable_base_msm(scalar_lo: Field, scalar_hi: Field, scalar2_lo: Field, scalar2_hi: Field) -> Point { - let g = Point { x: GRUMPKIN_ONE_X, y: GRUMPKIN_ONE_Y, is_infinite: false }; + fn variable_base_msm( + scalar_lo: Field, + scalar_hi: Field, + scalar2_lo: Field, + scalar2_hi: Field, + ) -> EmbeddedCurvePoint { + let g = EmbeddedCurvePoint { x: GRUMPKIN_ONE_X, y: GRUMPKIN_ONE_Y }; - let triple_g = multi_scalar_mul( - [g.to_embedded(), g.to_embedded()], + multi_scalar_mul( + [g, g], [Scalar { lo: scalar_lo, hi: scalar_hi }, Scalar { lo: scalar2_lo, hi: scalar2_hi }], - ); - triple_g.into() + ) } #[external("public")] - fn pedersen_commit(x: Field, y: Field) -> Point { - let commitment = ::std::hash::pedersen_commitment_with_separator([x, y], 20); - commitment.into() + fn pedersen_commit(x: Field, y: Field) -> EmbeddedCurvePoint { + ::std::hash::pedersen_commitment_with_separator([x, y], 20) } #[external("public")] diff --git a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr index b5887569e14e..0b7dee4ec40d 100644 --- a/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/returning_tuple_contract/src/main.nr @@ -5,7 +5,7 @@ use aztec::macros::aztec; pub contract ReturningTuple { use aztec::{ macros::functions::{external, view}, - protocol::{address::AztecAddress, point::Point, traits::{Deserialize, FromField}}, + protocol::{address::AztecAddress, point::EmbeddedCurvePoint, traits::{Deserialize, FromField}}, }; #[external("private")] @@ -40,8 +40,8 @@ pub contract ReturningTuple { #[external("private")] #[view] - fn fn_that_returns_6() -> (Field, u128, bool, str<3>, AztecAddress, Point) { - (1, 2, false, "xyz", AztecAddress::from_field(1), Point::deserialize([1, 2, 0])) + fn fn_that_returns_6() -> (Field, u128, bool, str<3>, AztecAddress, EmbeddedCurvePoint) { + (1, 2, false, "xyz", AztecAddress::from_field(1), EmbeddedCurvePoint::deserialize([1, 2])) } #[external("public")] @@ -76,8 +76,8 @@ pub contract ReturningTuple { #[external("public")] #[view] - fn fn_that_returns_6_public() -> (Field, u128, bool, str<3>, AztecAddress, Point) { - (1, 2, false, "xyz", AztecAddress::from_field(1), Point::deserialize([1, 2, 0])) + fn fn_that_returns_6_public() -> (Field, u128, bool, str<3>, AztecAddress, EmbeddedCurvePoint) { + (1, 2, false, "xyz", AztecAddress::from_field(1), EmbeddedCurvePoint::deserialize([1, 2])) } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/reset/key_validation_request/tests/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/reset/key_validation_request/tests/mod.nr index 53a2d043c696..e871d29a6a4e 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/reset/key_validation_request/tests/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/reset/key_validation_request/tests/mod.nr @@ -10,7 +10,7 @@ use types::{ abis::validation_requests::{KeyValidationRequest, KeyValidationRequestAndSeparator}, address::AztecAddress, hash::compute_app_siloed_secret_key, - point::Point, + point::EmbeddedCurvePoint, public_keys::hash_public_key, scalar::Scalar, side_effect::Scoped, @@ -74,7 +74,7 @@ impl TestBuilder { let contract_address = AztecAddress::from_field(456654); let sk_m = Scalar::from_field(sk); - let pk_m: Point = derive_public_key(sk_m).into(); + let pk_m: EmbeddedCurvePoint = derive_public_key(sk_m); let pk_m_hash = hash_public_key(pk_m); let key_type_domain_separator = 123321; diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/reset/key_validation_request/validate_key_validation_request.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/reset/key_validation_request/validate_key_validation_request.nr index 62ead8fc6133..500602519dea 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/reset/key_validation_request/validate_key_validation_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/reset/key_validation_request/validate_key_validation_request.nr @@ -1,8 +1,8 @@ use std::embedded_curve_ops::fixed_base_scalar_mul as derive_public_key; use types::{ abis::validation_requests::KeyValidationRequestAndSeparator, - hash::compute_app_siloed_secret_key, point::Point, public_keys::hash_public_key, scalar::Scalar, - side_effect::Scoped, + hash::compute_app_siloed_secret_key, point::EmbeddedCurvePoint, public_keys::hash_public_key, + scalar::Scalar, side_effect::Scoped, }; /// Validates a Key Validation Request that an app circuit has submitted to the kernel. @@ -55,10 +55,14 @@ pub fn validate_key_validation_request( let key_type_domain_separator = request_and_separator.key_type_domain_separator; // First we check that the hash of the derived public key matches the requested pk_m_hash. - let pk_m: Point = derive_public_key(sk_m).into(); + let pk_m: EmbeddedCurvePoint = derive_public_key(sk_m); // Safeguard against using a secret key equals to zero. - assert_eq(pk_m.is_infinite, false, "Derived master public key cannot be the point at infinity"); + assert_eq( + pk_m.is_infinite(), + false, + "Derived master public key cannot be the point at infinity", + ); assert_eq( hash_public_key(pk_m), diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_init/private_call_data/validate_contract_address_tests.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_init/private_call_data/validate_contract_address_tests.nr index 574ae4cceeef..ab3428dedccb 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_init/private_call_data/validate_contract_address_tests.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_init/private_call_data/validate_contract_address_tests.nr @@ -89,7 +89,7 @@ fn incorrect_address_preimage() { let mut builder = TestBuilder::new_with_regular_contract(); builder.private_call.public_keys.ivpk_m.inner = - derive_public_key(EmbeddedCurveScalar::from_field(69)).into(); + derive_public_key(EmbeddedCurveScalar::from_field(69)); builder.execute_and_fail(); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_reset/mod.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_reset/mod.nr index 04dcbe088ff2..59a99317b066 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_reset/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_reset/mod.nr @@ -34,7 +34,7 @@ use types::{ merkle_tree::{ LeafPreimage, MembershipWitness, nullifier_merkle_hash, test_utils::SingleSubtreeMerkleTree, }, - point::Point, + point::EmbeddedCurvePoint, public_keys::hash_public_key, side_effect::{Counted, Scoped}, traits::Empty, @@ -200,7 +200,7 @@ impl TestBuilder { pub fn add_key_validation_request(&mut self, sk: Field) { let sk_m = EmbeddedCurveScalar::from_field(sk); - let pk_m: Point = derive_public_key(sk_m).into(); + let pk_m: EmbeddedCurvePoint = derive_public_key(sk_m); let pk_m_hash = hash_public_key(pk_m); let key_type_domain_separator = 123321; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr index c9658582aae3..5227ae894ce5 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/aztec_address.nr @@ -10,7 +10,7 @@ use crate::{ utils::field::sqrt, }; -use crate::point::Point; +use crate::point::EmbeddedCurvePoint; use crate::public_keys::AddressPoint; use std::{ @@ -66,7 +66,7 @@ impl AztecAddress { // positive one (where y <= (r - 1) / 2) by negating it. let final_y = if Self::is_positive(y) { y } else { -y }; - AddressPoint { inner: Point { x: self.inner, y: final_y, is_infinite: false } } + AddressPoint { inner: EmbeddedCurvePoint { x: self.inner, y: final_y } } }) } @@ -138,11 +138,10 @@ impl AztecAddress { ); // Note: `.add()` will fail within the blackbox fn if either of the points are not on the curve. (See tests below). - // TODO(F-553): Drop the `.to_embedded()` / `.into()` round-trip once the custom `Point` wrapper is removed and - // we use `EmbeddedCurvePoint` directly. - let address_point: Point = derive_public_key(EmbeddedCurveScalar::from_field(pre_address)) - .add(public_keys.ivpk_m.to_point().to_embedded()) - .into(); + let address_point: EmbeddedCurvePoint = derive_public_key(EmbeddedCurveScalar::from_field( + pre_address, + )) + .add(public_keys.ivpk_m.to_point()); // Note that our address is only the x-coordinate of the full address_point. This is okay because when people want to encrypt something and send it to us // they can recover our full point using the x-coordinate (our address itself). To do this, they recompute the y-coordinate according to the equation y^2 = x^3 - 17. @@ -195,47 +194,33 @@ fn check_is_positive() { // because the blackbox function does this check for us. #[test(should_fail_with = "is not on curve")] fn check_embedded_curve_point_add() { - // Choose a point not on the curve: - let p1 = Point { x: 1, y: 1, is_infinite: false }; - let p2 = Point::generator(); - let _ = p1 + p2; -} - -// Gives us confidence that we don't need to manually check that the input public keys need to be on the curve for `add`, -// because the blackbox function does this check for us. -#[test(should_fail_with = "is not on curve")] -fn check_embedded_curve_point_add_2() { // Choose a point not on the curve in the 2nd position. - let p1 = Point::generator(); - let p2 = Point { x: 1, y: 1, is_infinite: false }; - let _ = p1 + p2; + let p1 = EmbeddedCurvePoint::generator(); + let key = IvpkM { inner: EmbeddedCurvePoint { x: 1, y: 1 } }; + let _ = p1 + key.to_point(); } #[test] fn compute_address_from_partial_and_pub_keys() { - let npk_m_point = Point { + let npk_m_point = EmbeddedCurvePoint { x: 0x22f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab, y: 0x0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7, - is_infinite: false, }; - let ovpk_m_point = Point { + let ovpk_m_point = EmbeddedCurvePoint { x: 0x09115c96e962322ffed6522f57194627136b8d03ac7469109707f5e44190c484, y: 0x0c49773308a13d740a7f0d4f0e6163b02c5a408b6f965856b6a491002d073d5b, - is_infinite: false, }; - let tpk_m_point = Point { + let tpk_m_point = EmbeddedCurvePoint { x: 0x00d3d81beb009873eb7116327cf47c612d5758ef083d4fda78e9b63980b2a762, y: 0x2f567d22d2b02fe1f4ad42db9d58a36afd1983e7e2909d1cab61cafedad6193a, - is_infinite: false, }; let public_keys = PublicKeys { npk_m_hash: hash_public_key(npk_m_point), ivpk_m: IvpkM { - inner: Point { + inner: EmbeddedCurvePoint { x: 0x111223493147f6785514b1c195bb37a2589f22a6596d30bb2bb145fdc9ca8f1e, y: 0x273bbffd678edce8fe30e0deafc4f66d58357c06fd4a820285294b9746c3be95, - is_infinite: false, }, }, ovpk_m_hash: hash_public_key(ovpk_m_point), diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index cc57fe6f9cff..d204616a106b 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -311,7 +311,7 @@ pub global GAS_SETTINGS_LENGTH: u32 = GAS_LENGTH /* gas_limits */ + GAS_FEES_LENGTH /* max_fees_per_gas */ + GAS_FEES_LENGTH /* max_priority_fees_per_gas */; pub global CALL_CONTEXT_LENGTH: u32 = 4; -pub global CONTRACT_INSTANCE_LENGTH: u32 = 11; +pub global CONTRACT_INSTANCE_LENGTH: u32 = 10; pub global CONTRACT_STORAGE_READ_LENGTH: u32 = 3; pub global CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH: u32 = 3; pub global ETH_ADDRESS_LENGTH: u32 = 1; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/point.nr b/noir-projects/noir-protocol-circuits/crates/types/src/point.nr index 289ee5fcb2b1..c5ea3b076b2c 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/point.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/point.nr @@ -1,174 +1,12 @@ -use crate::{ - hash::poseidon2_hash, - traits::{Deserialize, Empty, Hash, Packable, Serialize}, - utils::{reader::Reader, writer::Writer}, -}; -use std::embedded_curve_ops::EmbeddedCurvePoint; +pub use std::embedded_curve_ops::EmbeddedCurvePoint; -pub global POINT_LENGTH: u32 = 3; - -// TODO(F-553): This custom `Point` struct is a temporary workaround. A newer version of Noir dropped the `is_infinite` -// field from `EmbeddedCurvePoint` and changed its serialization from 3 fields to 2. To preserve backwards compatibility -// of our onchain serialization format we re-introduced our own `Point` that wraps `EmbeddedCurvePoint` and keeps the -// old 3-field layout. Once we're ready to take the breaking change (e.g. alongside the v5 oracle changes), delete this -// struct, use `EmbeddedCurvePoint` directly and reduce `POINT_LENGTH` to 2. -pub struct Point { - pub x: Field, - pub y: Field, - pub is_infinite: bool, -} - -impl Point { - pub fn generator() -> Self { - let g = EmbeddedCurvePoint::generator(); - Point { x: g.x, y: g.y, is_infinite: false } - } - - pub fn point_at_infinity() -> Self { - Point { x: 0, y: 0, is_infinite: true } - } - - pub fn double(self) -> Self { - self.to_embedded().double().into() - } - - pub fn to_embedded(self) -> EmbeddedCurvePoint { - EmbeddedCurvePoint { x: self.x, y: self.y } - } -} - -impl From for Point { - fn from(p: EmbeddedCurvePoint) -> Self { - Point { x: p.x, y: p.y, is_infinite: p.is_infinite() } - } -} - -impl std::ops::Add for Point { - fn add(self, other: Point) -> Point { - (self.to_embedded() + other.to_embedded()).into() - } -} - -impl std::ops::Sub for Point { - fn sub(self, other: Point) -> Point { - (self.to_embedded() - other.to_embedded()).into() - } -} - -impl std::ops::Neg for Point { - fn neg(self) -> Point { - Point { x: self.x, y: -self.y, is_infinite: self.is_infinite } - } -} - -impl Eq for Point { - fn eq(self, other: Point) -> bool { - (self.x == other.x) & (self.y == other.y) & (self.is_infinite == other.is_infinite) - } -} - -impl Hash for Point { - fn hash(self) -> Field { - poseidon2_hash(self.serialize()) - } -} - -impl Empty for Point { - /// Note: Does not return a valid point on curve - instead represents an empty/"unpopulated" point struct (e.g. - /// empty/unpopulated value in an array of points). - fn empty() -> Self { - Point { x: 0, y: 0, is_infinite: false } - } -} - -impl Serialize for Point { - let N: u32 = POINT_LENGTH; - - fn serialize(self) -> [Field; Self::N] { - [self.x, self.y, self.is_infinite as Field] - } - - fn stream_serialize(self, writer: &mut Writer) { - writer.write(self.x); - writer.write(self.y); - writer.write(self.is_infinite as Field); - } -} - -impl Deserialize for Point { - let N: u32 = POINT_LENGTH; - - fn deserialize(fields: [Field; Self::N]) -> Self { - Point { x: fields[0], y: fields[1], is_infinite: fields[2] != 0 } - } - - #[inline_always] - fn stream_deserialize(reader: &mut Reader) -> Self { - Point { x: reader.read(), y: reader.read(), is_infinite: reader.read_bool() } - } -} - -pub fn validate_on_curve(p: Point) { +/// Validates that the given point exists on the Grumpkin curve. +pub fn validate_on_curve(p: EmbeddedCurvePoint) { // y^2 == x^3 - 17 let x = p.x; let y = p.y; - if p.is_infinite { - // Assert the canonical representation of infinity - assert_eq(x, 0, "Point at infinity should have canonical representation (0 0)"); - assert_eq(y, 0, "Point at infinity should have canonical representation (0 0)"); - } else { + // p.is_infinite() <==> x == y == 0, considered on the curve: + if !p.is_infinite() { assert_eq(y * y, x * x * x - 17, "Point not on curve"); } } - -// TODO(#11356): use compact representation here. -impl Packable for Point { - let N: u32 = POINT_LENGTH; - - fn pack(self) -> [Field; Self::N] { - self.serialize() - } - - fn unpack(packed: [Field; Self::N]) -> Self { - Self::deserialize(packed) - } -} - -mod tests { - use super::{Point, validate_on_curve}; - - #[test] - unconstrained fn test_validate_on_curve_generator() { - // The generator point should be on the curve - let generator = Point::generator(); - validate_on_curve(generator); - } - - #[test] - unconstrained fn test_validate_on_curve_infinity() { - // Canonical infinite point (x=0, y=0) should pass - let infinity = Point { x: 0, y: 0, is_infinite: true }; - validate_on_curve(infinity); - } - - #[test(should_fail_with = "Point not on curve")] - unconstrained fn test_validate_on_curve_invalid_point() { - // A point not on the curve should fail - let invalid = Point { x: 1, y: 1, is_infinite: false }; - validate_on_curve(invalid); - } - - #[test(should_fail_with = "Point at infinity should have canonical representation (0 0)")] - unconstrained fn test_validate_on_curve_infinity_non_canonical_x() { - // Infinite point with non-zero x should fail - let invalid_infinity = Point { x: 1, y: 0, is_infinite: true }; - validate_on_curve(invalid_infinity); - } - - #[test(should_fail_with = "Point at infinity should have canonical representation (0 0)")] - unconstrained fn test_validate_on_curve_infinity_non_canonical_y() { - // Infinite point with non-zero y should fail - let invalid_infinity = Point { x: 0, y: 1, is_infinite: true }; - validate_on_curve(invalid_infinity); - } -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/public_keys.nr b/noir-projects/noir-protocol-circuits/crates/types/src/public_keys.nr index 5a813b07010c..c312b1a557f8 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/public_keys.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/public_keys.nr @@ -5,31 +5,30 @@ use crate::{ DEFAULT_TPK_M_HASH, DOM_SEP__PUBLIC_KEYS_HASH, DOM_SEP__SINGLE_PUBLIC_KEY_HASH, }, hash::poseidon2_hash_with_separator, - point::validate_on_curve, + point::{EmbeddedCurvePoint, validate_on_curve}, traits::{Deserialize, Hash, Serialize}, }; -use crate::point::Point; use std::{default::Default, meta::derive}; pub trait ToPoint { - fn to_point(self) -> Point; + fn to_point(self) -> EmbeddedCurvePoint; } /// Hashes a public key point under the canonical single-public-key domain separator. /// /// Defined as `Poseidon2(DOM_SEP__SINGLE_PUBLIC_KEY_HASH, x, y)`. -pub fn hash_public_key(p: Point) -> Field { +pub fn hash_public_key(p: EmbeddedCurvePoint) -> Field { poseidon2_hash_with_separator([p.x, p.y], DOM_SEP__SINGLE_PUBLIC_KEY_HASH as Field) } #[derive(Deserialize, Eq, Serialize)] pub struct IvpkM { - pub inner: Point, + pub inner: EmbeddedCurvePoint, } impl ToPoint for IvpkM { - fn to_point(self) -> Point { + fn to_point(self) -> EmbeddedCurvePoint { self.inner } } @@ -59,7 +58,7 @@ impl Default for PublicKeys { PublicKeys { npk_m_hash: DEFAULT_NPK_M_HASH, ivpk_m: IvpkM { - inner: Point { x: DEFAULT_IVPK_M_X, y: DEFAULT_IVPK_M_Y, is_infinite: false }, + inner: EmbeddedCurvePoint { x: DEFAULT_IVPK_M_X, y: DEFAULT_IVPK_M_Y }, }, ovpk_m_hash: DEFAULT_OVPK_M_HASH, tpk_m_hash: DEFAULT_TPK_M_HASH, @@ -88,16 +87,16 @@ impl PublicKeys { /// As with [`Self::validate_on_curve`], the other three keys are now exposed only as hashes /// and this property must be enforced PXE-side. pub fn validate_non_infinity(self) { - assert_eq(self.ivpk_m.inner.is_infinite, false, "IvpkM is the point at infinity"); + assert_eq(self.ivpk_m.inner.is_infinite(), false, "IvpkM is the point at infinity"); } } pub struct AddressPoint { - pub inner: Point, + pub inner: EmbeddedCurvePoint, } impl ToPoint for AddressPoint { - fn to_point(self) -> Point { + fn to_point(self) -> EmbeddedCurvePoint { self.inner } } @@ -107,14 +106,13 @@ mod test { DEFAULT_NPK_M_HASH, DEFAULT_NPK_M_X, DEFAULT_NPK_M_Y, DEFAULT_OVPK_M_HASH, DEFAULT_OVPK_M_X, DEFAULT_OVPK_M_Y, DEFAULT_TPK_M_HASH, DEFAULT_TPK_M_X, DEFAULT_TPK_M_Y, }; - use crate::point::Point; use crate::{ + point::EmbeddedCurvePoint, public_keys::{hash_public_key, IvpkM, PublicKeys}, traits::{Deserialize, Serialize}, }; - use std::ops::Neg; - global PUBLIC_KEYS_LENGTH: u32 = 6; + global PUBLIC_KEYS_LENGTH: u32 = 5; /// Catches drift between the precomputed `DEFAULT_*_M_HASH` constants and the /// `DEFAULT_*_M_X/Y` curve points they're derived from. If anyone updates the X/Y @@ -123,17 +121,17 @@ mod test { #[test] unconstrained fn default_hashes_match_default_points() { let npk = hash_public_key( - Point { x: DEFAULT_NPK_M_X, y: DEFAULT_NPK_M_Y, is_infinite: false }, + EmbeddedCurvePoint { x: DEFAULT_NPK_M_X, y: DEFAULT_NPK_M_Y }, ); assert_eq(npk, DEFAULT_NPK_M_HASH); let ovpk = hash_public_key( - Point { x: DEFAULT_OVPK_M_X, y: DEFAULT_OVPK_M_Y, is_infinite: false }, + EmbeddedCurvePoint { x: DEFAULT_OVPK_M_X, y: DEFAULT_OVPK_M_Y }, ); assert_eq(ovpk, DEFAULT_OVPK_M_HASH); let tpk = hash_public_key( - Point { x: DEFAULT_TPK_M_X, y: DEFAULT_TPK_M_Y, is_infinite: false }, + EmbeddedCurvePoint { x: DEFAULT_TPK_M_X, y: DEFAULT_TPK_M_Y }, ); assert_eq(tpk, DEFAULT_TPK_M_HASH); } @@ -142,7 +140,7 @@ mod test { unconstrained fn compute_public_keys_hash() { let keys = PublicKeys { npk_m_hash: 11, - ivpk_m: IvpkM { inner: Point { x: 3, y: 4, is_infinite: false } }, + ivpk_m: IvpkM { inner: EmbeddedCurvePoint { x: 3, y: 4 } }, ovpk_m_hash: 22, tpk_m_hash: 33, }; @@ -159,7 +157,7 @@ mod test { unconstrained fn test_validate_on_curve() { let keys = PublicKeys { npk_m_hash: 0, - ivpk_m: IvpkM { inner: Point::generator().double() }, + ivpk_m: IvpkM { inner: EmbeddedCurvePoint::generator().double() }, ovpk_m_hash: 0, tpk_m_hash: 0, }; @@ -171,7 +169,7 @@ mod test { unconstrained fn test_validate_not_on_curve() { let keys = PublicKeys { npk_m_hash: 0, - ivpk_m: IvpkM { inner: Point { x: 3, y: 4, is_infinite: false } }, + ivpk_m: IvpkM { inner: EmbeddedCurvePoint { x: 3, y: 4 } }, ovpk_m_hash: 0, tpk_m_hash: 0, }; @@ -183,7 +181,7 @@ mod test { unconstrained fn test_validate_non_infinity() { let keys = PublicKeys { npk_m_hash: 0, - ivpk_m: IvpkM { inner: Point::generator().double() }, + ivpk_m: IvpkM { inner: EmbeddedCurvePoint::generator().double() }, ovpk_m_hash: 0, tpk_m_hash: 0, }; @@ -195,7 +193,7 @@ mod test { unconstrained fn test_validate_infinity() { let keys = PublicKeys { npk_m_hash: 0, - ivpk_m: IvpkM { inner: Point::point_at_infinity() }, + ivpk_m: IvpkM { inner: EmbeddedCurvePoint::point_at_infinity() }, ovpk_m_hash: 0, tpk_m_hash: 0, }; @@ -219,7 +217,7 @@ mod test { unconstrained fn serde() { let keys = PublicKeys { npk_m_hash: 11, - ivpk_m: IvpkM { inner: Point { x: 3, y: 4, is_infinite: false } }, + ivpk_m: IvpkM { inner: EmbeddedCurvePoint { x: 3, y: 4 } }, ovpk_m_hash: 22, tpk_m_hash: 33, }; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/type_packing.nr b/noir-projects/noir-protocol-circuits/crates/types/src/type_packing.nr index bee6737454fd..932691b0b577 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/type_packing.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/type_packing.nr @@ -1,4 +1,4 @@ -use crate::traits::Packable; +use crate::traits::{Deserialize, Packable, Serialize}; global BOOL_PACKED_LEN: u32 = 1; global U8_PACKED_LEN: u32 = 1; @@ -11,6 +11,7 @@ global I8_PACKED_LEN: u32 = 1; global I16_PACKED_LEN: u32 = 1; global I32_PACKED_LEN: u32 = 1; global I64_PACKED_LEN: u32 = 1; +global POINT_PACKED_LEN: u32 = 2; impl Packable for bool { let N: u32 = BOOL_PACKED_LEN; @@ -166,6 +167,17 @@ impl Packable for i64 { } } +impl Packable for super::point::EmbeddedCurvePoint { + let N: u32 = POINT_PACKED_LEN; + fn pack(self) -> [Field; Self::N] { + self.serialize() + } + + fn unpack(packed: [Field; Self::N]) -> Self { + Self::deserialize(packed) + } +} + impl Packable for [T; M] where T: Packable, diff --git a/yarn-project/CLAUDE.md b/yarn-project/CLAUDE.md index 070a5398537a..a0654c7d2b27 100644 --- a/yarn-project/CLAUDE.md +++ b/yarn-project/CLAUDE.md @@ -131,7 +131,7 @@ LOG_LEVEL='info; debug:sequencer,archiver' yarn workspace @aztec/ ### Style -- **Line width**: 120 characters (`printWidth: 120` in `.prettierrc.json`). Wrap comments and code at 120, not 80. +- **Line width**: 120 characters (`printWidth: 120` in `.prettierrc.json`). Wrap **everything** at 120 — code, inline comments, and JSDoc/block comments alike. Do not wrap at 80, 90, or 100 out of habit. Prettier does not reflow comment bodies, so an under-wrapped JSDoc paragraph will sit at ~90 chars forever unless you wrote it at 120 to begin with. ### Format diff --git a/yarn-project/constants/src/constants.gen.ts b/yarn-project/constants/src/constants.gen.ts index 81942058d57e..ae24ee193e19 100644 --- a/yarn-project/constants/src/constants.gen.ts +++ b/yarn-project/constants/src/constants.gen.ts @@ -144,7 +144,7 @@ export const GAS_FEES_LENGTH = 2; export const GAS_LENGTH = 2; export const GAS_SETTINGS_LENGTH = 8; export const CALL_CONTEXT_LENGTH = 4; -export const CONTRACT_INSTANCE_LENGTH = 11; +export const CONTRACT_INSTANCE_LENGTH = 10; export const CONTRACT_STORAGE_READ_LENGTH = 3; export const CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH = 3; export const ETH_ADDRESS_LENGTH = 1; diff --git a/yarn-project/foundation/src/curves/grumpkin/point.test.ts b/yarn-project/foundation/src/curves/grumpkin/point.test.ts index 2adf80cf4543..e3569ae45b45 100644 --- a/yarn-project/foundation/src/curves/grumpkin/point.test.ts +++ b/yarn-project/foundation/src/curves/grumpkin/point.test.ts @@ -8,7 +8,7 @@ describe('Point', () => { it('always returns a valid point', async () => { for (let i = 0; i < 100; ++i) { const point = await Point.random(); - expect(point.isOnGrumpkin()).toEqual(true); + expect(point.isOnCurve()).toEqual(true); } }); @@ -26,7 +26,6 @@ describe('Point', () => { const p = new Point( new Fr(0x30426e64aee30e998c13c8ceecda3a77807dbead52bc2f3bf0eae851b4b710c1n), new Fr(0x113156a068f603023240c96b4da5474667db3b8711c521c748212a15bc034ea6n), - false, ); const [x, sign] = p.toXAndSign(); @@ -53,7 +52,6 @@ describe('Point', () => { const p = new Point( new Fr(0x1af41f5de96446dc3776a1eb2d98bb956b7acd9979a67854bec6fa7c2973bd73n), new Fr(0x07fc22c7f2c7057571f137fe46ea9c95114282bc95d37d71ec4bfb88de457d4an), - false, ); expect(p.toXAndSign()[1]).toBe(true); @@ -74,7 +72,6 @@ describe('Point', () => { const p = new Point( new Fr(0x247371652e55dd74c9af8dbe9fb44931ba29a9229994384bd7077796c14ee2b5n), new Fr(0x26441aec112e1ae4cee374f42556932001507ad46e255ffb27369c7e3766e5c0n), - false, ); expect(p.toXAndSign()[1]).toBe(false); diff --git a/yarn-project/foundation/src/curves/grumpkin/point.ts b/yarn-project/foundation/src/curves/grumpkin/point.ts index e0bfe5b57846..708fafe42193 100644 --- a/yarn-project/foundation/src/curves/grumpkin/point.ts +++ b/yarn-project/foundation/src/curves/grumpkin/point.ts @@ -1,5 +1,4 @@ import { toBigIntBE } from '../../bigint-buffer/index.js'; -import { poseidon2Hash } from '../../crypto/poseidon/index.js'; import { randomBoolean } from '../../crypto/random/index.js'; import { hexSchemaFor } from '../../schemas/utils.js'; import { BufferReader, FieldReader, serializeToBuffer } from '../../serialize/index.js'; @@ -13,13 +12,16 @@ import { Fr } from '../bn254/field.js'; * TODO(#7386): Clean up this class. */ export class Point { - static ZERO = new Point(Fr.ZERO, Fr.ZERO, false); + static ZERO = new Point(Fr.ZERO, Fr.ZERO); static SIZE_IN_BYTES = Fr.SIZE_IN_BYTES * 2; static COMPRESSED_SIZE_IN_BYTES = Fr.SIZE_IN_BYTES; /** Used to differentiate this class from AztecAddress */ public readonly kind = 'point'; + /** Whether the point is at infinity */ + public readonly isInfinite: boolean; + constructor( /** * The point's x coordinate @@ -29,12 +31,10 @@ export class Point { * The point's y coordinate */ public readonly y: Fr, - /** - * Whether the point is at infinity - */ - public readonly isInfinite: boolean, ) { // TODO(#7386): check if on curve + // NOTE: now there is no isInfinite in the struct, empty == inf, so an empty class would pass an on-curve check. This may be fine depending on the usage. + this.isInfinite = x.isZero() && y.isZero(); } toJSON() { @@ -61,7 +61,7 @@ export class Point { if (obj instanceof Buffer || Buffer.isBuffer(obj)) { return Point.fromBuffer(obj); } - return new Point(Fr.fromPlainObject(obj.x), Fr.fromPlainObject(obj.y), obj.isInfinite ?? false); + return new this(Fr.fromPlainObject(obj.x), Fr.fromPlainObject(obj.y)); } /** @@ -92,7 +92,7 @@ export class Point { */ static fromBuffer(buffer: Buffer | BufferReader) { const reader = BufferReader.asReader(buffer); - return new this(Fr.fromBuffer(reader), Fr.fromBuffer(reader), false); + return new this(Fr.fromBuffer(reader), Fr.fromBuffer(reader)); } /** @@ -125,19 +125,17 @@ export class Point { } /** - * Returns the contents of the point as an array of 3 fields. - * @returns The point as an array of 3 fields + * Returns the contents of the point as an array of 2 fields. + * @returns The point as an array of 2 fields */ - // TODO(F-553): Once we take the breaking change and drop the custom Noir `Point` wrapper in - // `noir-projects/noir-protocol-circuits/crates/types/src/point.nr`, revert this to serialize as `[x, y]` (2 fields) - // and drop the `is_infinite` flag from `fromFields` / `toNoirStruct` below. toFields() { - return [this.x, this.y, new Fr(this.isInfinite)]; + return [this.x, this.y]; } static fromFields(fields: Fr[] | FieldReader) { const reader = FieldReader.asReader(fields); - return new this(reader.readField(), reader.readField(), reader.readBoolean()); + const [x, y] = [reader.readField(), reader.readField()]; + return new this(x, y); } /** @@ -162,11 +160,12 @@ export class Point { const finalY = sign ? new Fr(yPositiveBigInt) : new Fr(yNegativeBigInt); // Create and return the new Point - return new this(x, finalY, false); + return new this(x, finalY); } /** - * @returns + * @param x - The x coordinate of the point + * @returns y^2 such that y^2 = x^3 - 17 */ static YFromX(x: Fr): Promise { // Calculate y^2 = x^3 - 17 (i.e. the Grumpkin curve equation) @@ -194,24 +193,16 @@ export class Point { return { x: this.x.toBigInt(), y: this.y.toBigInt(), - isInfinite: this.isInfinite ? 1n : 0n, }; } /** * Converts the Point instance to a Buffer representation of the coordinates. * @returns A Buffer representation of the Point instance. - * @dev Note that toBuffer does not include the isInfinite flag and other serialization methods do (e.g. toFields). - * This is because currently when we work with point as bytes we don't want to populate the extra bytes for - * isInfinite flag because: - * 1. Our Grumpkin BB API currently does not handle point at infinity, - * 2. we use toBuffer when serializing notes and events and there we only work with public keys and point at infinity - * is not considered a valid public key and the extra byte would raise DA cost. + * @dev Note that toBuffer does not include the isInfinite flag. The point at infinity is serialized as (0, 0). + * With the removal of is_infinite from EmbeddedCurvePoint in Noir, the point at infinity is simply (0, 0). */ toBuffer() { - if (this.isInfinite) { - throw new Error('Cannot serialize infinite point with isInfinite flag'); - } const buf = serializeToBuffer([this.x, this.y]); if (buf.length !== Point.SIZE_IN_BYTES) { throw new Error(`Invalid buffer length for Point: ${buf.length}`); @@ -261,9 +252,7 @@ export class Point { } toNoirStruct() { - /* eslint-disable camelcase */ - return { x: this.x, y: this.y, is_infinite: this.isInfinite }; - /* eslint-enable camelcase */ + return { x: this.x, y: this.y }; } // Used for IvpkM. TODO(#8124): Consider removing this method. @@ -286,10 +275,6 @@ export class Point { return this.x.isZero() && this.y.isZero(); } - hash() { - return poseidon2Hash(this.toFields()); - } - /** * Check if this is point at infinity. * Check this is consistent with how bb is encoding the point at infinity @@ -298,8 +283,12 @@ export class Point { return this.isInfinite; } - isOnGrumpkin() { - // TODO: Check this against how bb handles curve check and infinity point check + /** + * @param x - The x coordinate of the point + * @param y - The y coordinate of the point + * @returns Whether the point exists on Grumpkin + */ + isOnCurve() { if (this.inf) { return true; } diff --git a/yarn-project/noir-protocol-circuits-types/src/conversion/common.ts b/yarn-project/noir-protocol-circuits-types/src/conversion/common.ts index a9a99d52f050..5868f473ca49 100644 --- a/yarn-project/noir-protocol-circuits-types/src/conversion/common.ts +++ b/yarn-project/noir-protocol-circuits-types/src/conversion/common.ts @@ -74,7 +74,7 @@ import type { AztecAddress as NoirAztecAddress, EthAddress as NoirEthAddress, Field as NoirField, - Point as NoirPoint, + EmbeddedCurvePoint as NoirPoint, NullifierLeafPreimage as NullifierLeafPreimageNoir, PartialStateReference as PartialStateReferenceNoir, Log as PrivateLogNoir, @@ -175,7 +175,6 @@ export function mapPointToNoir(point: Point): NoirPoint { return { x: mapFieldToNoir(point.x), y: mapFieldToNoir(point.y), - is_infinite: point.isInfinite, }; } @@ -185,7 +184,7 @@ export function mapPointToNoir(point: Point): NoirPoint { * @returns The point. */ export function mapPointFromNoir(point: NoirPoint): Point { - return new Point(mapFieldFromNoir(point.x), mapFieldFromNoir(point.y), point.is_infinite); + return new Point(mapFieldFromNoir(point.x), mapFieldFromNoir(point.y)); } /** diff --git a/yarn-project/noir-protocol-circuits-types/src/conversion/type_conversion.test.ts b/yarn-project/noir-protocol-circuits-types/src/conversion/type_conversion.test.ts index c6c569c61f83..f207bc9e3f1a 100644 --- a/yarn-project/noir-protocol-circuits-types/src/conversion/type_conversion.test.ts +++ b/yarn-project/noir-protocol-circuits-types/src/conversion/type_conversion.test.ts @@ -29,7 +29,7 @@ describe('Noir<>stdlib type conversion test suite', () => { expect(mapFieldFromNoir(mapFieldToNoir(field))).toEqual(field); }); - const point = new Point(new Fr(27n), new Fr(28n), false); + const point = new Point(new Fr(27n), new Fr(28n)); it('should map points', () => { expect(mapPointFromNoir(mapPointToNoir(point))).toEqual(point); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts index 1adb61d80285..3371e39c7f07 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts @@ -3,7 +3,6 @@ import type { BlockNumber } from '@aztec/foundation/branded-types'; import { uniqueBy } from '@aztec/foundation/collection'; import { Aes128 } from '@aztec/foundation/crypto/aes128'; import { Fr } from '@aztec/foundation/curves/bn254'; -import { Point } from '@aztec/foundation/curves/grumpkin'; import { LogLevels, type Logger, createLogger } from '@aztec/foundation/log'; import type { MembershipWitness } from '@aztec/foundation/trees'; import type { KeyStore } from '@aztec/key-store'; @@ -23,7 +22,7 @@ import type { CompleteAddress, ContractInstance, PartialAddress } from '@aztec/s import { siloNullifier } from '@aztec/stdlib/hash'; import type { AztecNode } from '@aztec/stdlib/interfaces/server'; import type { KeyValidationRequest } from '@aztec/stdlib/kernel'; -import { type PublicKeys, computeAddressSecret, hashPublicKey } from '@aztec/stdlib/keys'; +import { PublicKey, type PublicKeys, computeAddressSecret, hashPublicKey } from '@aztec/stdlib/keys'; import { MessageContext, deriveAppSiloedSharedSecret } from '@aztec/stdlib/logs'; import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging'; import type { NoteStatus } from '@aztec/stdlib/note'; @@ -697,7 +696,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra const ephPkFields = this.ephemeralArrayService.readArrayAt(ephPksSlot); const secrets = await Promise.all( ephPkFields.map(fields => - deriveAppSiloedSharedSecret(addressSecret, Point.fromFields(fields), this.contractAddress), + deriveAppSiloedSharedSecret(addressSecret, PublicKey.fromFields(fields), this.contractAddress), ), ); diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts index 9808d6568fe4..d54678092928 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts @@ -2,7 +2,6 @@ import { CONTRACT_CLASS_LOG_SIZE_IN_FIELDS, PRIVATE_LOG_SIZE_IN_FIELDS } from '@aztec/constants'; import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; -import { Point } from '@aztec/foundation/curves/grumpkin'; import { EthAddress } from '@aztec/foundation/eth-address'; import type { Tuple } from '@aztec/foundation/serialize'; import { KeyStore } from '@aztec/key-store'; @@ -15,7 +14,7 @@ import { BlockHash, Body, GENESIS_BLOCK_HEADER_HASH, L2Block } from '@aztec/stdl import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; import { CompleteAddress, SerializableContractInstance } from '@aztec/stdlib/contract'; import { GasFees } from '@aztec/stdlib/gas'; -import { PublicKeys } from '@aztec/stdlib/keys'; +import { PublicKey, PublicKeys } from '@aztec/stdlib/keys'; import { ContractClassLog, ContractClassLogFields, @@ -197,7 +196,7 @@ export const SCHEMA_TESTS: readonly SchemaTest[] = [ // Only `ivpk_m` is exposed as a curve point; the other three master keys // are exposed as `hash_public_key` digests. Constructor signature is now // `(npkMHash, ivpkM, ovpkMHash, tpkMHash)`. - publicKeys: new PublicKeys(new Fr(41n), new Point(new Fr(47n), new Fr(53n), false), new Fr(59n), new Fr(67n)), + publicKeys: new PublicKeys(new Fr(41n), new PublicKey(new Fr(47n), new Fr(53n)), new Fr(59n), new Fr(67n)), }).withAddress(AztecAddress.fromBigInt(101n)), ); }, diff --git a/yarn-project/simulator/src/public/avm/avm_simulator.test.ts b/yarn-project/simulator/src/public/avm/avm_simulator.test.ts index 0ab606e1fcda..9239ee27c398 100644 --- a/yarn-project/simulator/src/public/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/public/avm/avm_simulator.test.ts @@ -6,7 +6,6 @@ import { pedersenCommit, pedersenHash } from '@aztec/foundation/crypto/pedersen' import { poseidon2Hash } from '@aztec/foundation/crypto/poseidon'; import { sha256 } from '@aztec/foundation/crypto/sha256'; import { Fq, Fr } from '@aztec/foundation/curves/bn254'; -import { Point } from '@aztec/foundation/curves/grumpkin'; import type { Fieldable } from '@aztec/foundation/serialize'; import { AvmGadgetsTestContract } from '@aztec/noir-test-contracts.js/AvmGadgetsTest'; import { AvmTestContract } from '@aztec/noir-test-contracts.js/AvmTest'; @@ -23,7 +22,7 @@ import { siloNoteHash, siloNullifier, } from '@aztec/stdlib/hash'; -import { PublicKeys } from '@aztec/stdlib/keys'; +import { PublicKey, PublicKeys } from '@aztec/stdlib/keys'; import { makeContractClassPublic, makeContractInstanceFromClassId } from '@aztec/stdlib/testing'; import { NativeWorldStateService } from '@aztec/world-state'; @@ -257,10 +256,8 @@ describe('AVM simulator: transpiled Noir contracts', () => { const calldata = new CallDataArray([ new Fr(1), // P1x new Fr(17631683881184975370165255887551781615748388533673675138860n), // P1y - new Fr(0), // P1inf new Fr(1), // P2x new Fr(17631683881184975370165255887551781615748388533673675138860n), // P2y - new Fr(0), // P2inf ]); const context = initContext({ env: initExecutionEnvironment({ calldata }) }); @@ -278,7 +275,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect(results.reverted).toBe(false); const g3 = await Grumpkin.mul(Grumpkin.generator, new Fq(3)); - expect(results.output.readAll()).toEqual([g3.x, g3.y, Fr.ZERO]); + expect(results.output.readAll()).toEqual([g3.x, g3.y]); }); describe('msm', () => { @@ -298,7 +295,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { const g3 = await Grumpkin.mul(Grumpkin.generator, new Fq(3)); const g20 = await Grumpkin.mul(Grumpkin.generator, new Fq(20)); const expectedResult = await Grumpkin.add(g3, g20); - expect(results.output.readAll()).toEqual([expectedResult.x, expectedResult.y, Fr.ZERO]); + expect(results.output.readAll()).toEqual([expectedResult.x, expectedResult.y]); }); it('with a zero', async () => { @@ -315,7 +312,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect(results.reverted).toBe(false); const expectedResult = await Grumpkin.mul(Grumpkin.generator, new Fq(3)); - expect(results.output.readAll()).toEqual([expectedResult.x, expectedResult.y, Fr.ZERO]); + expect(results.output.readAll()).toEqual([expectedResult.x, expectedResult.y]); }); const fqToLimbs = (fq: Fq): [bigint, bigint] => { @@ -340,7 +337,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { const g1 = await Grumpkin.mul(Grumpkin.generator, scalar); const g2 = await Grumpkin.mul(Grumpkin.generator, scalar2); const expectedResult = await Grumpkin.add(g1, g2); - expect(results.output.readAll()).toEqual([expectedResult.x, expectedResult.y, Fr.ZERO]); + expect(results.output.readAll()).toEqual([expectedResult.x, expectedResult.y]); }); }); @@ -352,11 +349,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { const results = await new AvmSimulator(context).executeBytecode(bytecode); expect(results.reverted).toBe(false); - // This doesnt include infinites const expectedResult = (await pedersenCommit([Buffer.from([100]), Buffer.from([1])], 20)).map(f => new Fr(f)); - // TODO: Come back to the handling of infinities when we confirm how they're handled in bb - const isInf = expectedResult[0] === new Fr(0) && expectedResult[1] === new Fr(0); - expectedResult.push(new Fr(isInf)); expect(results.output.readAll()).toEqual(expectedResult); }); @@ -923,7 +916,7 @@ describe('AVM simulator: transpiled Noir contracts', () => { immutablesHash: new Fr(0x202221), publicKeys: new PublicKeys( new Fr(0x131415), - new Point(new Fr(0x192021), new Fr(0x222324), false), + new PublicKey(new Fr(0x192021), new Fr(0x222324)), new Fr(0x252627), new Fr(0x313233), ), diff --git a/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts b/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts index ec05d289e2d7..8c12fa4a9039 100644 --- a/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts +++ b/yarn-project/simulator/src/public/avm/opcodes/ec_add.test.ts @@ -55,11 +55,7 @@ describe('EC Instructions', () => { context, ); - const actual = new Point( - context.machineState.memory.get(4).toFr(), - context.machineState.memory.get(5).toFr(), - false, - ); + const actual = new Point(context.machineState.memory.get(4).toFr(), context.machineState.memory.get(5).toFr()); const expected = await Grumpkin.add(Grumpkin.generator, Grumpkin.generator); expect(actual).toEqual(expected); }); @@ -82,11 +78,7 @@ describe('EC Instructions', () => { context, ); - const actual = new Point( - context.machineState.memory.get(4).toFr(), - context.machineState.memory.get(5).toFr(), - false, - ); + const actual = new Point(context.machineState.memory.get(4).toFr(), context.machineState.memory.get(5).toFr()); const G3 = await Grumpkin.add(Grumpkin.generator, G2); expect(actual).toEqual(G3); }); diff --git a/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts b/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts index 9fe17d84128c..8052b910777a 100644 --- a/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts +++ b/yarn-project/simulator/src/public/avm/opcodes/ec_add.ts @@ -52,8 +52,8 @@ export class EcAdd extends Instruction { const p1XFr = p1X.toFr(); const p1YFr = p1Y.toFr(); const p1IsInfinite = p1XFr.isZero() && p1YFr.isZero(); - const p1 = new Point(p1XFr, p1YFr, p1IsInfinite); - if (!p1.isOnGrumpkin()) { + const p1 = new Point(p1XFr, p1YFr); + if (!p1.isOnCurve()) { throw new EcAddPointNotOnCurveError(/*pointIndex=*/ 1, p1); } @@ -62,8 +62,8 @@ export class EcAdd extends Instruction { const p2XFr = p2X.toFr(); const p2YFr = p2Y.toFr(); const p2IsInfinite = p2XFr.isZero() && p2YFr.isZero(); - const p2 = new Point(p2XFr, p2YFr, p2IsInfinite); - if (!p2.isOnGrumpkin()) { + const p2 = new Point(p2XFr, p2YFr); + if (!p2.isOnCurve()) { throw new EcAddPointNotOnCurveError(/*pointIndex=*/ 2, p2); } diff --git a/yarn-project/stdlib/src/abi/decoder.test.ts b/yarn-project/stdlib/src/abi/decoder.test.ts index 6a19c8483296..2992dd4aeb30 100644 --- a/yarn-project/stdlib/src/abi/decoder.test.ts +++ b/yarn-project/stdlib/src/abi/decoder.test.ts @@ -236,12 +236,6 @@ describe('decoder', () => { kind: 'field', }, }, - { - name: 'is_infinite', - type: { - kind: 'boolean', - }, - }, ], }, ], @@ -257,19 +251,10 @@ describe('decoder', () => { Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')), // address Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')), // point.x Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000002', 'hex')), // point.y - Fr.fromBuffer(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')), // point.is_infinite ], ); - expect(decoded).toEqual([ - 1n, - 2n, - false, - 'xyz', - AztecAddress.fromBigInt(1n), - // eslint-disable-next-line camelcase - { x: 1n, y: 2n, is_infinite: false }, - ]); + expect(decoded).toEqual([1n, 2n, false, 'xyz', AztecAddress.fromBigInt(1n), { x: 1n, y: 2n }]); }); it('decodes Option::Some as the wrapped value', () => { diff --git a/yarn-project/stdlib/src/avm/message_pack.ts b/yarn-project/stdlib/src/avm/message_pack.ts index f07953876e2c..774f109df691 100644 --- a/yarn-project/stdlib/src/avm/message_pack.ts +++ b/yarn-project/stdlib/src/avm/message_pack.ts @@ -64,13 +64,22 @@ function setUpMessagePackExtensions() { addExtension({ Class: Point, write: (p: Point) => { + // TODO: Now that we use a 2 elt point representation, we should be able to handle infs here. + // However this opens possible bad paths with public keys and requires sanitised conversion between + // BB's inf representation (see below), and ours/Noir's (0, 0), and empty points from BB, when inf + // does not actually pass through. assert(!p.inf, 'Cannot serialize infinity'); - // TODO: should these be Frs? return { x: new Fq(p.x.toBigInt()), y: new Fq(p.y.toBigInt()) }; }, read: (data: { x: Fq; y: Fq }) => { + // Note: BB encodes infinity as x == y == Buffer of all ones. + // Infinity should never pass through here, but for correctness: + const ALL_ONES = (1n << 256n) - 1n; + if (data.x.toBigInt() === ALL_ONES && data.y.toBigInt() === ALL_ONES) { + return Point.ZERO; + } // Convert Fq back to Fr for Point constructor - return new Point(new Fr(data.x.toBigInt()), new Fr(data.y.toBigInt()), false); + return new Point(new Fr(data.x.toBigInt()), new Fr(data.y.toBigInt())); }, }); // EthAddress is a class that has a buffer in TS, but is itself just a field in C++. diff --git a/yarn-project/stdlib/src/aztec-address/aztec-address.test.ts b/yarn-project/stdlib/src/aztec-address/aztec-address.test.ts index 2efab96544e7..d00329824a02 100644 --- a/yarn-project/stdlib/src/aztec-address/aztec-address.test.ts +++ b/yarn-project/stdlib/src/aztec-address/aztec-address.test.ts @@ -40,7 +40,7 @@ describe('aztec-address', () => { it("reconstructs an address's point", async () => { const address = await AztecAddress.random(); const point = await address.toAddressPoint(); - expect(point.isOnGrumpkin()).toEqual(true); + expect(point.isOnCurve()).toEqual(true); }); it('throws for an invalid address', async () => { diff --git a/yarn-project/stdlib/src/keys/derivation.test.ts b/yarn-project/stdlib/src/keys/derivation.test.ts index bf0b5a1a4856..558c1cd32676 100644 --- a/yarn-project/stdlib/src/keys/derivation.test.ts +++ b/yarn-project/stdlib/src/keys/derivation.test.ts @@ -1,16 +1,15 @@ import { Fr } from '@aztec/foundation/curves/bn254'; -import { Point } from '@aztec/foundation/curves/grumpkin'; import { updateInlineTestData } from '@aztec/foundation/testing/files'; import { computeAddress, computePreaddress } from './derivation.js'; -import { hashPublicKey } from './public_key.js'; +import { PublicKey, hashPublicKey } from './public_key.js'; import { PublicKeys } from './public_keys.js'; describe('🔑', () => { it('computing public keys hash matches Noir', async () => { const publicKeysHash = await new PublicKeys( new Fr(11n), - new Point(new Fr(3n), new Fr(4n), false), + new PublicKey(new Fr(3n), new Fr(4n)), new Fr(22n), new Fr(33n), ).hash(); @@ -41,16 +40,16 @@ describe('🔑', () => { ); }); it('Address matches Noir', async () => { - const npkM = Point.fromString( + const npkM = PublicKey.fromString( '0x22f7fcddfa3ce3e8f0cc8e82d7b94cdd740afa3e77f8e4a63ea78a239432dcab0471657de2b6216ade6c506d28fbc22ba8b8ed95c871ad9f3e3984e90d9723a7', ); - const ivpkM = Point.fromString( + const ivpkM = PublicKey.fromString( '0x111223493147f6785514b1c195bb37a2589f22a6596d30bb2bb145fdc9ca8f1e273bbffd678edce8fe30e0deafc4f66d58357c06fd4a820285294b9746c3be95', ); - const ovpkM = Point.fromString( + const ovpkM = PublicKey.fromString( '0x09115c96e962322ffed6522f57194627136b8d03ac7469109707f5e44190c4840c49773308a13d740a7f0d4f0e6163b02c5a408b6f965856b6a491002d073d5b', ); - const tpkM = Point.fromString( + const tpkM = PublicKey.fromString( '0x00d3d81beb009873eb7116327cf47c612d5758ef083d4fda78e9b63980b2a7622f567d22d2b02fe1f4ad42db9d58a36afd1983e7e2909d1cab61cafedad6193a', ); const publicKeys = new PublicKeys( diff --git a/yarn-project/stdlib/src/keys/derivation.ts b/yarn-project/stdlib/src/keys/derivation.ts index adacab96d3f7..9b4765a8aef3 100644 --- a/yarn-project/stdlib/src/keys/derivation.ts +++ b/yarn-project/stdlib/src/keys/derivation.ts @@ -7,7 +7,7 @@ import { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin'; import { AztecAddress } from '../aztec-address/index.js'; import type { KeyPrefix } from './key_types.js'; -import { hashPublicKey } from './public_key.js'; +import { PublicKey, hashPublicKey } from './public_key.js'; import { PublicKeys } from './public_keys.js'; import { getKeyGenerator } from './utils.js'; @@ -84,7 +84,7 @@ export async function computeAddressSecret(preaddress: Fr, ivsk: Fq) { return addressSecretCandidate; } -export function derivePublicKeyFromSecretKey(secretKey: Fq) { +export function derivePublicKeyFromSecretKey(secretKey: Fq): Promise { return Grumpkin.mul(Grumpkin.generator, secretKey); } diff --git a/yarn-project/stdlib/src/keys/public_key.ts b/yarn-project/stdlib/src/keys/public_key.ts index 9231e2c3d982..1bc5fbc2966d 100644 --- a/yarn-project/stdlib/src/keys/public_key.ts +++ b/yarn-project/stdlib/src/keys/public_key.ts @@ -1,16 +1,28 @@ import { DomainSeparator } from '@aztec/constants'; import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon'; import { Fr } from '@aztec/foundation/curves/bn254'; -import type { Point } from '@aztec/foundation/curves/grumpkin'; - -/** Represents a user public key. */ -export type PublicKey = Point; +import { Point } from '@aztec/foundation/curves/grumpkin'; /** - * Hashes a public key under the canonical single-public-key domain separator. + * Hashes a public key. * + * Mirrors Noir's `hash_public_key` in `noir-protocol-circuits/crates/types/src/public_keys.nr`: * `Poseidon2(DOM_SEP__SINGLE_PUBLIC_KEY_HASH, [pk.x, pk.y])`. + * + * This is distinct from Noir's generic `Hash` impl for `EmbeddedCurvePoint` (`noir_stdlib/src/embedded_curve_ops.nr`), + * which simply absorbs `x` then `y` into a `Hasher` state with no domain separator. That generic impl is unsuitable + * for hashing keys at the protocol boundary, where the domain separator is required to prevent collisions with hashes + * of other Grumpkin points (e.g. note commitments, nullifiers). */ export function hashPublicKey(pk: PublicKey): Promise { return poseidon2HashWithSeparator([pk.x, pk.y], DomainSeparator.SINGLE_PUBLIC_KEY_HASH); } + +/** + * Represents a user public key. + * + * Structurally identical to a Grumpkin `Point`; exposed as a distinct name so call sites read as "public key" where + * that's the domain meaning. + */ +export type PublicKey = Point; +export const PublicKey = Point; diff --git a/yarn-project/stdlib/src/keys/public_keys.test.ts b/yarn-project/stdlib/src/keys/public_keys.test.ts index d9a097783cab..635fb05df198 100644 --- a/yarn-project/stdlib/src/keys/public_keys.test.ts +++ b/yarn-project/stdlib/src/keys/public_keys.test.ts @@ -1,7 +1,7 @@ import { Fr } from '@aztec/foundation/curves/bn254'; -import { Point } from '@aztec/foundation/curves/grumpkin'; import { updateInlineTestData } from '@aztec/foundation/testing/files'; +import { PublicKey } from './public_key.js'; import { PublicKeys } from './public_keys.js'; describe('PublicKeys', () => { @@ -19,7 +19,7 @@ describe('PublicKeys', () => { }); it('computes public keys hash', async () => { - const keys = new PublicKeys(new Fr(11n), new Point(new Fr(3n), new Fr(4n), false), new Fr(22n), new Fr(33n)); + const keys = new PublicKeys(new Fr(11n), new PublicKey(new Fr(3n), new Fr(4n)), new Fr(22n), new Fr(33n)); const hash = await keys.hash(); expect(hash.toString()).toMatchInlineSnapshot( diff --git a/yarn-project/stdlib/src/keys/public_keys.ts b/yarn-project/stdlib/src/keys/public_keys.ts index d6a7c835ba2e..9a0e2beb9c59 100644 --- a/yarn-project/stdlib/src/keys/public_keys.ts +++ b/yarn-project/stdlib/src/keys/public_keys.ts @@ -8,7 +8,6 @@ import { } from '@aztec/constants'; import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon'; import { Fr } from '@aztec/foundation/curves/bn254'; -import { Point } from '@aztec/foundation/curves/grumpkin'; import { schemas } from '@aztec/foundation/schemas'; import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { bufferToHex, withoutHexPrefix } from '@aztec/foundation/string'; @@ -16,7 +15,7 @@ import type { FieldsOf } from '@aztec/foundation/types'; import { z } from 'zod'; -import { type PublicKey, hashPublicKey } from './public_key.js'; +import { PublicKey, hashPublicKey } from './public_key.js'; /** * A non-owner's view of an account's master public keys. @@ -61,7 +60,7 @@ export class PublicKeys { } return new PublicKeys( Fr.fromPlainObject(obj.npkMHash), - Point.fromPlainObject(obj.ivpkM), + PublicKey.fromPlainObject(obj.ivpkM), Fr.fromPlainObject(obj.ovpkMHash), Fr.fromPlainObject(obj.tpkMHash), ); @@ -73,8 +72,7 @@ export class PublicKeys { } // Mirror Noir's `PublicKeys::hash`: hash the four single-key digests under // DOM_SEP__PUBLIC_KEYS_HASH. `ivpk_m` must be reduced to its single-key digest first - // (Poseidon2 over [x, y]); passing the Point directly would inadvertently include - // `is_infinite` and produce a different value. + // (Poseidon2 over [x, y]); passing the Point directly would omit the DOM_SEP__SINGLE_PUBLIC_KEY_HASH const ivpkMHash = await hashPublicKey(this.ivpkM); return poseidon2HashWithSeparator( [this.npkMHash, ivpkMHash, this.ovpkMHash, this.tpkMHash], @@ -87,26 +85,26 @@ export class PublicKeys { } static default(): PublicKeys { - // Precomputed `hash_public_key(Point { DEFAULT_*_X, DEFAULT_*_Y, false })` for npk/ovpk/tpk. + // Precomputed `hash_public_key(Point { DEFAULT_*_X, DEFAULT_*_Y })` for npk/ovpk/tpk. // Sourced from constants.gen.ts (originally defined in // noir-protocol-circuits/crates/types/src/constants.nr); a self-test in public_keys.nr // (`default_hashes_match_default_points`) catches drift between the *_HASH constants and // the underlying X/Y points. return new PublicKeys( new Fr(DEFAULT_NPK_M_HASH), - new Point(new Fr(DEFAULT_IVPK_M_X), new Fr(DEFAULT_IVPK_M_Y), false), + new PublicKey(new Fr(DEFAULT_IVPK_M_X), new Fr(DEFAULT_IVPK_M_Y)), new Fr(DEFAULT_OVPK_M_HASH), new Fr(DEFAULT_TPK_M_HASH), ); } static async random(): Promise { - const npkM = await Point.random(); - const ovpkM = await Point.random(); - const tpkM = await Point.random(); + const npkM = await PublicKey.random(); + const ovpkM = await PublicKey.random(); + const tpkM = await PublicKey.random(); return new PublicKeys( await hashPublicKey(npkM), - await Point.random(), + await PublicKey.random(), await hashPublicKey(ovpkM), await hashPublicKey(tpkM), ); @@ -121,6 +119,12 @@ export class PublicKeys { ); } + /** + * Converts the PublicKeys instance into a Buffer. + * This method should be used when encoding the address for storage, transmission or serialization purposes. + * + * @returns A Buffer representation of the PublicKeys instance. + */ toBuffer(): Buffer { return serializeToBuffer([this.npkMHash, this.ivpkM, this.ovpkMHash, this.tpkMHash]); } @@ -128,7 +132,7 @@ export class PublicKeys { static fromBuffer(buffer: Buffer | BufferReader): PublicKeys { const reader = BufferReader.asReader(buffer); const npkMHash = Fr.fromBuffer(reader); - const ivpkM = reader.readObject(Point); + const ivpkM = reader.readObject(PublicKey); const ovpkMHash = Fr.fromBuffer(reader); const tpkMHash = Fr.fromBuffer(reader); return new PublicKeys(npkMHash, ivpkM, ovpkMHash, tpkMHash); @@ -148,27 +152,10 @@ export class PublicKeys { /** * Wire-format fields matching Noir's struct flattening of `PublicKeys`: - * `[npk_m_hash, ivpk_m.x, ivpk_m.y, ivpk_m.is_infinite, ovpk_m_hash, tpk_m_hash]` (6 fields). - * - * The `is_infinite` slot is `0` for `ivpkM` produced by `deriveKeys` (fixed-base scalar - * multiplication on Grumpkin cannot reach infinity for a non-zero scalar, and Poseidon-derived - * scalars are non-zero with cryptographically negligible exception). External flows that - * import pre-derived keys are responsible for maintaining this invariant (see the AZIP-8 - * migration note "Security note (PXE side)"). The slot stays on the wire because the Noir - * struct's `Point` still carries the flag. AZIP-8's "drop `is_infinite`" goal applies to the - * address-derivation hash (via `hash_public_key`, computed elsewhere), not to the - * contract-call args wire. - * TODO(F-553): drop the `is_infinite` slot once `Point` no longer carries the flag. + * `[npk_m_hash, ivpk_m.x, ivpk_m.y, ovpk_m_hash, tpk_m_hash]` (5 fields). */ toFields(): Fr[] { - return [ - this.npkMHash, - this.ivpkM.x, - this.ivpkM.y, - new Fr(this.ivpkM.isInfinite ? 1 : 0), - this.ovpkMHash, - this.tpkMHash, - ]; + return [this.npkMHash, this.ivpkM.x, this.ivpkM.y, this.ovpkMHash, this.tpkMHash]; } // Used in foundation/src/abi/encoder. Probably non-optimal but the encoder needs a refactor. @@ -178,13 +165,7 @@ export class PublicKeys { static fromFields(fields: Fr[] | FieldReader): PublicKeys { const reader = FieldReader.asReader(fields); - const npkMHash = reader.readField(); - const ivpkMX = reader.readField(); - const ivpkMY = reader.readField(); - const ivpkMIsInfinite = reader.readBoolean(); - const ovpkMHash = reader.readField(); - const tpkMHash = reader.readField(); - return new PublicKeys(npkMHash, new Point(ivpkMX, ivpkMY, ivpkMIsInfinite), ovpkMHash, tpkMHash); + return new PublicKeys(reader.readField(), reader.readObject(PublicKey), reader.readField(), reader.readField()); } toString() { diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index c05e258cee53..a20585ad65ce 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -123,7 +123,7 @@ import { PublicCallRequest, PublicCallRequestArrayLengths, } from '../kernel/public_call_request.js'; -import { PublicKeys, computeAddress, hashPublicKey } from '../keys/index.js'; +import { PublicKey, PublicKeys, computeAddress, hashPublicKey } from '../keys/index.js'; import { ExtendedDirectionalAppTaggingSecret } from '../logs/extended_directional_app_tagging_secret.js'; import { ContractClassLog, ContractClassLogFields } from '../logs/index.js'; import { PrivateLog } from '../logs/private_log.js'; @@ -577,7 +577,7 @@ export function makeVerificationKeyAsFields(size: number): VerificationKeyAsFiel * @returns A point. */ export function makePoint(seed = 1): Point { - return new Point(fr(seed), fr(seed + 1), false); + return new Point(fr(seed), fr(seed + 1)); } /** @@ -1442,7 +1442,7 @@ export function makeAvmContractInstanceHint(seed = 0): AvmContractInstanceHint { new Fr(seed + 0x7), new PublicKeys( new Fr(seed + 0x7), - new Point(new Fr(seed + 0x9), new Fr(seed + 0x10), false), + new PublicKey(new Fr(seed + 0x9), new Fr(seed + 0x10)), new Fr(seed + 0x11), new Fr(seed + 0x13), ),