Skip to content

Commit d55b5ca

Browse files
authored
chore(contract): move away from near-sdk PublicKey type (#4256)
1 parent fa6a2c3 commit d55b5ca

14 files changed

Lines changed: 319 additions & 185 deletions

File tree

crates/contract/src/api/ckd.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,8 @@ impl MpcContract {
8585

8686
self.assert_caller_is_attested_participant_and_protocol_active();
8787

88-
let PublicKeyExtended::Bls12381 {
89-
public_key: dtos::PublicKey::Bls12381(public_key),
90-
} = self.public_key_extended(request.domain_id)?
88+
let PublicKeyExtended::Bls12381 { public_key } =
89+
self.public_key_extended(request.domain_id)?
9190
else {
9291
env::panic_str("Domain is not compatible with CKD (expected Bls12381 curve)");
9392
};

crates/contract/src/api/foreign_chain.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,13 @@ impl MpcContract {
112112
dtos::SignatureResponse::Secp256k1(signature_response),
113113
PublicKeyExtended::Secp256k1 { near_public_key },
114114
) => {
115-
let secp_pk = dtos::Secp256k1PublicKey::try_from(&near_public_key)
116-
.expect("Secp256k1 variant always has a secp256k1 key");
117-
118115
let payload_hash: [u8; 32] = response.payload_hash.0;
119116

120117
// Check the signature is correct against the root public key
121118
near_mpc_signature_verifier::verify_ecdsa_signature(
122119
signature_response,
123120
&payload_hash,
124-
&secp_pk,
121+
&near_public_key,
125122
)
126123
.is_ok()
127124
}

crates/contract/src/api/keys.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ impl MpcContract {
4242

4343
let derived_public_key: dtos::PublicKey = match public_key {
4444
PublicKeyExtended::Secp256k1 { near_public_key } => {
45-
let secp_pk = dtos::Secp256k1PublicKey::try_from(&near_public_key)
46-
.expect("Secp256k1 variant always has a secp256k1 key");
47-
let affine = *k256::PublicKey::try_from(&secp_pk)
45+
let affine = *k256::PublicKey::try_from(&near_public_key)
4846
.expect("stored key is always valid")
4947
.as_affine();
5048
let derived_public_key =
@@ -56,7 +54,7 @@ impl MpcContract {
5654
derive_public_key_edwards_point_ed25519(&edwards_point, &tweak);
5755
dtos::Ed25519PublicKey::from(derived_public_key_edwards_point.compress()).into()
5856
}
59-
PublicKeyExtended::Bls12381 { public_key } => public_key,
57+
PublicKeyExtended::Bls12381 { public_key } => public_key.into(),
6058
};
6159

6260
Ok(derived_public_key)

crates/contract/src/api/sign.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,7 @@ impl MpcContract {
112112
PublicKeyExtended::Secp256k1 { near_public_key },
113113
) => {
114114
// generate the expected public key
115-
let secp_pk = dtos::Secp256k1PublicKey::try_from(&near_public_key)
116-
.expect("Secp256k1 variant always has a secp256k1 key");
117-
let affine = *k256::PublicKey::try_from(&secp_pk)
115+
let affine = *k256::PublicKey::try_from(&near_public_key)
118116
.expect("stored key is always valid")
119117
.as_affine();
120118
let expected_public_key =

crates/contract/src/crypto_shared/types.rs

Lines changed: 32 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use serde::{Deserialize, Serialize};
1111
use serde_with::serde_as;
1212
use serializable::SerializableEdwardsPoint;
1313

14-
use crate::errors;
1514
use near_mpc_contract_interface::types as dtos;
1615

1716
#[cfg_attr(
@@ -22,136 +21,71 @@ use near_mpc_contract_interface::types as dtos;
2221
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
2322
pub enum PublicKeyExtended {
2423
Secp256k1 {
25-
near_public_key: near_sdk::PublicKey,
24+
near_public_key: dtos::Secp256k1PublicKey,
2625
},
2726
// Invariant: `edwards_point` is always the decompressed representation of `near_public_key_compressed`.
2827
Ed25519 {
2928
/// Serialized compressed Edwards-y point.
30-
near_public_key_compressed: near_sdk::PublicKey,
29+
near_public_key_compressed: dtos::Ed25519PublicKey,
3130
/// Decompressed Edwards point used for curve arithmetic operations.
3231
edwards_point: SerializableEdwardsPoint,
3332
},
3433
Bls12381 {
35-
public_key: dtos::PublicKey,
34+
public_key: dtos::Bls12381G2PublicKey,
3635
},
3736
}
3837

3938
#[derive(Clone, Debug)]
4039
pub enum PublicKeyExtendedConversionError {
41-
PublicKeyLengthMalformed,
4240
FailedDecompressingToEdwardsPoint,
43-
UnsupportedCurve,
4441
}
4542

4643
impl Display for PublicKeyExtendedConversionError {
4744
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4845
let message = match self {
49-
Self::PublicKeyLengthMalformed => "Provided public key has malformed length.",
5046
Self::FailedDecompressingToEdwardsPoint => {
5147
"The provided compressed key can not be decompressed to an edwards point."
5248
}
53-
Self::UnsupportedCurve => "The provided curve is not supported.",
5449
};
5550

5651
f.write_str(message)
5752
}
5853
}
5954

60-
impl TryFrom<PublicKeyExtended> for near_sdk::PublicKey {
61-
type Error = errors::Error;
62-
fn try_from(public_key_extended: PublicKeyExtended) -> Result<Self, Self::Error> {
63-
match public_key_extended {
64-
PublicKeyExtended::Secp256k1 { near_public_key } => Ok(near_public_key),
65-
PublicKeyExtended::Ed25519 {
66-
near_public_key_compressed,
67-
..
68-
} => Ok(near_public_key_compressed),
69-
PublicKeyExtended::Bls12381 { public_key: _ } => {
70-
Err(errors::ConversionError::DataConversion {
71-
reason: "Cannot convert Bls12381 key to near_sdk::PublicKey".into(),
72-
})?
73-
}
74-
}
75-
}
76-
}
77-
7855
impl From<PublicKeyExtended> for dtos::PublicKey {
7956
fn from(public_key_extended: PublicKeyExtended) -> Self {
8057
match public_key_extended {
8158
PublicKeyExtended::Secp256k1 { near_public_key } => {
82-
dtos::PublicKey::try_from(&near_public_key)
83-
.expect("Secp256k1 variant always has a secp256k1 key")
59+
dtos::PublicKey::Secp256k1(near_public_key)
8460
}
8561
PublicKeyExtended::Ed25519 {
8662
near_public_key_compressed,
8763
..
88-
} => dtos::PublicKey::try_from(&near_public_key_compressed)
89-
.expect("Ed25519 variant always has an ed25519 key"),
90-
PublicKeyExtended::Bls12381 { public_key } => public_key,
64+
} => dtos::PublicKey::Ed25519(near_public_key_compressed),
65+
PublicKeyExtended::Bls12381 { public_key } => dtos::PublicKey::Bls12381(public_key),
9166
}
9267
}
9368
}
9469

95-
impl TryFrom<near_sdk::PublicKey> for PublicKeyExtended {
96-
type Error = PublicKeyExtendedConversionError;
97-
fn try_from(near_public_key: near_sdk::PublicKey) -> Result<Self, Self::Error> {
98-
let extended_key = match near_public_key.curve_type() {
99-
near_sdk::CurveType::ED25519 => {
100-
let public_key_bytes: &[u8; 32] = near_public_key
101-
.as_bytes()
102-
.get(1..)
103-
.map(TryInto::try_into)
104-
.ok_or(PublicKeyExtendedConversionError::PublicKeyLengthMalformed)?
105-
.map_err(|_| PublicKeyExtendedConversionError::PublicKeyLengthMalformed)?;
106-
107-
let edwards_point = SerializableEdwardsPoint::from_bytes(public_key_bytes)
108-
.into_option()
109-
.ok_or(PublicKeyExtendedConversionError::FailedDecompressingToEdwardsPoint)?;
110-
111-
Self::Ed25519 {
112-
near_public_key_compressed: near_public_key,
113-
edwards_point,
114-
}
115-
}
116-
near_sdk::CurveType::SECP256K1 => Self::Secp256k1 { near_public_key },
117-
near_sdk::CurveType::MLDSA65 => {
118-
return Err(PublicKeyExtendedConversionError::UnsupportedCurve);
119-
}
120-
};
121-
122-
Ok(extended_key)
123-
}
124-
}
125-
12670
impl TryFrom<dtos::PublicKey> for PublicKeyExtended {
12771
type Error = PublicKeyExtendedConversionError;
12872
fn try_from(public_key: dtos::PublicKey) -> Result<Self, Self::Error> {
12973
let extended_key = match public_key {
130-
dtos::PublicKey::Ed25519(inner_public_key) => {
131-
let near_public_key: near_sdk::PublicKey = inner_public_key.into();
132-
let public_key_bytes: &[u8; 32] = near_public_key
133-
.as_bytes()
134-
.get(1..)
135-
.map(TryInto::try_into)
136-
.ok_or(PublicKeyExtendedConversionError::PublicKeyLengthMalformed)?
137-
.map_err(|_| PublicKeyExtendedConversionError::PublicKeyLengthMalformed)?;
138-
139-
let edwards_point = SerializableEdwardsPoint::from_bytes(public_key_bytes)
140-
.into_option()
141-
.ok_or(PublicKeyExtendedConversionError::FailedDecompressingToEdwardsPoint)?;
74+
dtos::PublicKey::Ed25519(near_public_key_compressed) => {
75+
let edwards_point =
76+
SerializableEdwardsPoint::from_bytes(&near_public_key_compressed)
77+
.into_option()
78+
.ok_or(
79+
PublicKeyExtendedConversionError::FailedDecompressingToEdwardsPoint,
80+
)?;
14281

14382
Self::Ed25519 {
144-
near_public_key_compressed: near_public_key,
83+
near_public_key_compressed,
14584
edwards_point,
14685
}
14786
}
148-
dtos::PublicKey::Secp256k1(inner_public_key) => {
149-
let near_public_key: near_sdk::PublicKey = inner_public_key.into();
150-
Self::Secp256k1 { near_public_key }
151-
}
152-
dtos::PublicKey::Bls12381(inner_public_key) => Self::Bls12381 {
153-
public_key: dtos::PublicKey::from(inner_public_key),
154-
},
87+
dtos::PublicKey::Secp256k1(near_public_key) => Self::Secp256k1 { near_public_key },
88+
dtos::PublicKey::Bls12381(public_key) => Self::Bls12381 { public_key },
15589
};
15690

15791
Ok(extended_key)
@@ -361,12 +295,19 @@ mod tests {
361295

362296
/// Tests the serialization and deserialization of [`PublicKeyExtended`] works.
363297
#[rstest]
364-
#[case(
298+
#[case::secp256k1(
365299
"secp256k1:4Ls3DBDeFDaf5zs2hxTBnJpKnfsnjNahpKU9HwQvij8fTXoCP9y5JQqQpe273WgrKhVVj1EH73t5mMJKDFMsxoEd"
300+
.parse::<dtos::PublicKey>()
301+
.unwrap()
302+
)]
303+
#[case::ed25519(
304+
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
305+
.parse::<dtos::PublicKey>()
306+
.unwrap()
366307
)]
367-
#[case("ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp")]
368-
fn test_serialization_of_public_key_extended(#[case] near_public_key: near_sdk::PublicKey) {
369-
let public_key_extended = PublicKeyExtended::try_from(near_public_key).unwrap();
308+
#[case::bls12381(dtos::PublicKey::Bls12381(dtos::Bls12381G2PublicKey([7u8; 96])))]
309+
fn test_serialization_of_public_key_extended(#[case] public_key: dtos::PublicKey) {
310+
let public_key_extended = PublicKeyExtended::try_from(public_key).unwrap();
370311
let mut buffer: Vec<u8> = vec![];
371312
BorshSerialize::serialize(&public_key_extended, &mut buffer).unwrap();
372313

@@ -378,22 +319,17 @@ mod tests {
378319
}
379320

380321
#[test]
381-
fn public_key_extended_try_from_near_public_key__should_reject_mldsa65() {
382-
// Given
383-
const MLDSA65_PUBLIC_KEY_SIZE: usize = 1952;
384-
let near_public_key = near_sdk::PublicKey::from_parts(
385-
near_sdk::CurveType::MLDSA65,
386-
vec![0u8; MLDSA65_PUBLIC_KEY_SIZE],
387-
)
388-
.unwrap();
322+
fn public_key_extended_try_from_public_key__should_reject_a_non_curve_ed25519_key() {
323+
// Given a 32-byte value whose y-coordinate has no corresponding x on the curve.
324+
let public_key = dtos::PublicKey::Ed25519(dtos::Ed25519PublicKey([2u8; 32]));
389325

390326
// When
391-
let result = PublicKeyExtended::try_from(near_public_key);
327+
let result = PublicKeyExtended::try_from(public_key);
392328

393329
// Then
394330
assert_matches!(
395331
result,
396-
Err(PublicKeyExtendedConversionError::UnsupportedCurve)
332+
Err(PublicKeyExtendedConversionError::FailedDecompressingToEdwardsPoint)
397333
);
398334
}
399335
}

crates/contract/src/dto_mapping.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ impl IntoInterfaceType<dtos::PublicKeyExtended> for &PublicKeyExtended {
683683
edwards_point: edwards_point.to_bytes(),
684684
},
685685
PublicKeyExtended::Bls12381 { public_key } => dtos::PublicKeyExtended::Bls12381 {
686-
public_key: public_key.clone(),
686+
public_key: dtos::PublicKey::Bls12381(public_key.clone()),
687687
},
688688
}
689689
}

crates/contract/src/primitives/key_state.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ pub use mpc_primitives::{AttemptId, EpochId, KeyEventId};
1313
//
1414
// This is the contract-internal storage type, distinct from the DTO
1515
// [`near_mpc_contract_interface::types::KeyForDomain`] used over the wire.
16-
// They are kept separate because the contract stores public keys as
17-
// [`PublicKeyExtended`] (`near_sdk::PublicKey` plus a decompressed Edwards
18-
// point), while the DTO uses the JSON-friendly string/byte form.
16+
// They are kept separate because the contract stores keys as typed DTO key
17+
// structs plus a decompressed [`SerializableEdwardsPoint`] for curve
18+
// arithmetic, while the DTO uses the JSON-friendly string/byte form.
1919
#[near(serializers=[borsh, json])]
2020
#[derive(Debug, PartialEq, Eq, Clone)]
2121
pub struct KeyForDomain {

crates/contract/src/primitives/test_utils.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,9 @@ fn gen_random_edwards_point() -> (SerializableEdwardsPoint, CompressedEdwardsY)
7575

7676
pub fn bogus_ed25519_public_key_extended() -> PublicKeyExtended {
7777
let (edwards_point, compressed_edwards_point) = gen_random_edwards_point();
78-
let near_public_key_compressed = near_sdk::PublicKey::from_parts(
79-
near_sdk::CurveType::ED25519,
80-
compressed_edwards_point.as_bytes().into(),
81-
)
82-
.unwrap();
8378

8479
PublicKeyExtended::Ed25519 {
85-
near_public_key_compressed,
80+
near_public_key_compressed: Ed25519PublicKey::from(compressed_edwards_point),
8681
edwards_point,
8782
}
8883
}

crates/contract/src/snapshots/mpc_contract__tests__mpc_contract_borsh_schema_has_not_changed.snap

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -731,26 +731,6 @@ BorshSchemaContainer {
731731
],
732732
),
733733
},
734-
"MpcPublicKey": Enum {
735-
tag_width: 1,
736-
variants: [
737-
(
738-
0,
739-
"Secp256k1",
740-
"Secp256k1PublicKey",
741-
),
742-
(
743-
1,
744-
"Ed25519",
745-
"Ed25519PublicKey",
746-
),
747-
(
748-
2,
749-
"Bls12381",
750-
"Bls12381G2PublicKey",
751-
),
752-
],
753-
},
754734
"MrtdHash": Struct {
755735
fields: UnnamedFields(
756736
[
@@ -985,16 +965,6 @@ BorshSchemaContainer {
985965
"Protocol__Frost": Struct {
986966
fields: Empty,
987967
},
988-
"PublicKey": Struct {
989-
fields: NamedFields(
990-
[
991-
(
992-
"data",
993-
"Vec<u8>",
994-
),
995-
],
996-
),
997-
},
998968
"PublicKeyExtended": Enum {
999969
tag_width: 1,
1000970
variants: [
@@ -1020,7 +990,7 @@ BorshSchemaContainer {
1020990
[
1021991
(
1022992
"public_key",
1023-
"MpcPublicKey",
993+
"Bls12381G2PublicKey",
1024994
),
1025995
],
1026996
),
@@ -1030,7 +1000,7 @@ BorshSchemaContainer {
10301000
[
10311001
(
10321002
"near_public_key_compressed",
1033-
"PublicKey",
1003+
"Ed25519PublicKey",
10341004
),
10351005
(
10361006
"edwards_point",
@@ -1044,7 +1014,7 @@ BorshSchemaContainer {
10441014
[
10451015
(
10461016
"near_public_key",
1047-
"PublicKey",
1017+
"Secp256k1PublicKey",
10481018
),
10491019
],
10501020
),

0 commit comments

Comments
 (0)