Skip to content

Commit 4240025

Browse files
committed
fix: address comments
1 parent 2b81e4d commit 4240025

5 files changed

Lines changed: 76 additions & 32 deletions

File tree

crates/contract/src/api/node_migration.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ impl MpcContract {
169169
/// Returns the following errors:
170170
/// - [`InvalidState::ProtocolStateNotRunning`]: if protocol is not in [`Running`](ProtocolContractState::Running) state
171171
/// - [`InvalidState::NotParticipant`]: if caller is not a current participant
172+
/// - [`ConversionError::DataConversion`](crate::errors::ConversionError::DataConversion): if the provided keyset contains a malformed public key
172173
/// - [`NodeMigrationError::KeysetMismatch`](crate::errors::NodeMigrationError::KeysetMismatch): if provided keyset does not match the expected keyset
173174
/// - [`NodeMigrationError::MigrationNotFound`](crate::errors::NodeMigrationError::MigrationNotFound): if no migration record exists for the caller
174175
/// - [`NodeMigrationError::AccountPublicKeyMismatch`](crate::errors::NodeMigrationError::AccountPublicKeyMismatch): if caller’s public key does not match the expected destination node

crates/contract/src/dto_mapping.rs

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use near_sdk::env::sha256_array;
1818

1919
use crate::{
2020
config::Config,
21-
crypto_shared::types::PublicKeyExtended,
21+
crypto_shared::types::{PublicKeyExtended, serializable::SerializableEdwardsPoint},
2222
errors::{ConversionError, Error},
2323
primitives::{
2424
domain::{AddDomainsVotes, DomainRegistry},
@@ -682,35 +682,53 @@ impl IntoInterfaceType<dtos::DomainRegistry> for &DomainRegistry {
682682
impl TryIntoContractType<PublicKeyExtended> for dtos::PublicKeyExtended {
683683
type Error = Error;
684684
fn try_into_contract_type(self) -> Result<PublicKeyExtended, Self::Error> {
685-
let public_key =
686-
dtos::PublicKey::try_from(&self).map_err(|err| ConversionError::DataConversion {
687-
reason: format!("Failed to parse public key: {err}"),
688-
})?;
689-
let extended: PublicKeyExtended =
690-
public_key
691-
.try_into()
692-
.map_err(|err| ConversionError::DataConversion {
693-
reason: format!("Failed to extend public key: {err}"),
694-
})?;
685+
let parse_failed = |err| ConversionError::DataConversion {
686+
reason: format!("Failed to parse public key: {err}"),
687+
};
695688

696-
// The DTO carries the Edwards point alongside the compressed key; the contract type
697-
// derives it instead, so reject a pair that disagrees rather than silently dropping it.
698-
if let (
699-
dtos::PublicKeyExtended::Ed25519 { edwards_point, .. },
700-
PublicKeyExtended::Ed25519 {
701-
edwards_point: derived,
702-
..
703-
},
704-
) = (&self, &extended)
705-
&& derived.to_bytes() != *edwards_point
706-
{
707-
return Err(ConversionError::DataConversion {
708-
reason: "The Edwards point does not match the compressed public key.".to_string(),
689+
match self {
690+
dtos::PublicKeyExtended::Secp256k1 { near_public_key } => {
691+
Ok(PublicKeyExtended::Secp256k1 {
692+
near_public_key: near_public_key.parse().map_err(parse_failed)?,
693+
})
709694
}
710-
.into());
711-
}
695+
dtos::PublicKeyExtended::Ed25519 {
696+
near_public_key_compressed,
697+
edwards_point,
698+
} => {
699+
let near_public_key_compressed: dtos::Ed25519PublicKey =
700+
near_public_key_compressed.parse().map_err(parse_failed)?;
701+
let derived = SerializableEdwardsPoint::from_bytes(&near_public_key_compressed)
702+
.into_option()
703+
.ok_or_else(|| ConversionError::DataConversion {
704+
reason: "The compressed key is not a valid Edwards point.".to_string(),
705+
})?;
706+
// The DTO carries the Edwards point alongside the compressed key; the contract
707+
// type derives it, so a pair that disagrees is rejected rather than dropped.
708+
if derived.to_bytes() != edwards_point {
709+
return Err(ConversionError::DataConversion {
710+
reason: "The Edwards point does not match the compressed public key."
711+
.to_string(),
712+
}
713+
.into());
714+
}
712715

713-
Ok(extended)
716+
Ok(PublicKeyExtended::Ed25519 {
717+
near_public_key_compressed,
718+
edwards_point: derived,
719+
})
720+
}
721+
dtos::PublicKeyExtended::Bls12381 { public_key } => {
722+
let dtos::PublicKey::Bls12381(public_key) = public_key else {
723+
return Err(ConversionError::DataConversion {
724+
reason: "Expected a bls12381g2 public key.".to_string(),
725+
}
726+
.into());
727+
};
728+
729+
Ok(PublicKeyExtended::Bls12381 { public_key })
730+
}
731+
}
714732
}
715733
}
716734

@@ -1137,6 +1155,31 @@ mod tests {
11371155
);
11381156
}
11391157

1158+
/// The variant tag is not what decides the curve — the `<curve>:` prefix inside the key
1159+
/// string is. A pair that disagrees must be rejected, not silently reinterpreted.
1160+
#[rstest]
1161+
#[case::ed25519_tag_holding_a_secp256k1_key(dtos::PublicKeyExtended::Ed25519 {
1162+
near_public_key_compressed: String::from(&dtos::Secp256k1PublicKey([1u8; 64])),
1163+
edwards_point: [0u8; 32],
1164+
})]
1165+
#[case::bls12381_tag_holding_an_ed25519_key(dtos::PublicKeyExtended::Bls12381 {
1166+
public_key: dtos::PublicKey::Ed25519(bogus_ed25519_public_key()),
1167+
})]
1168+
fn public_key_extended__should_reject_a_variant_tag_that_disagrees_with_the_key(
1169+
#[case] dto: dtos::PublicKeyExtended,
1170+
) {
1171+
// When
1172+
let result: Result<PublicKeyExtended, Error> = dto.try_into_contract_type();
1173+
1174+
// Then
1175+
assert_matches!(
1176+
result,
1177+
Err(Error::ConversionError(
1178+
ConversionError::DataConversion { .. }
1179+
))
1180+
);
1181+
}
1182+
11401183
/// A threshold below the relative (>= 60%) requirement must be rejected at the
11411184
/// DTO boundary rather than deferred to a later validation step.
11421185
#[test]

crates/contract/tests/snapshots/abi__abi_has_not_changed.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ expression: abi
549549
},
550550
{
551551
"name": "conclude_node_migration",
552-
"doc": " Finalizes a node migration for the calling account.\n\n This method can only be called while the protocol is in a [`Running`](ProtocolContractState::Running) state\n and by an existing participant. On success, the participant’s information is\n updated to the new destination node.\n\n # Errors\n Returns the following errors:\n - [`InvalidState::ProtocolStateNotRunning`]: if protocol is not in [`Running`](ProtocolContractState::Running) state\n - [`InvalidState::NotParticipant`]: if caller is not a current participant\n - [`NodeMigrationError::KeysetMismatch`](crate::errors::NodeMigrationError::KeysetMismatch): if provided keyset does not match the expected keyset\n - [`NodeMigrationError::MigrationNotFound`](crate::errors::NodeMigrationError::MigrationNotFound): if no migration record exists for the caller\n - [`NodeMigrationError::AccountPublicKeyMismatch`](crate::errors::NodeMigrationError::AccountPublicKeyMismatch): if caller’s public key does not match the expected destination node\n - [`InvalidParameters::InvalidTeeRemoteAttestation`]: if destination node’s TEE quote is invalid",
552+
"doc": " Finalizes a node migration for the calling account.\n\n This method can only be called while the protocol is in a [`Running`](ProtocolContractState::Running) state\n and by an existing participant. On success, the participant’s information is\n updated to the new destination node.\n\n # Errors\n Returns the following errors:\n - [`InvalidState::ProtocolStateNotRunning`]: if protocol is not in [`Running`](ProtocolContractState::Running) state\n - [`InvalidState::NotParticipant`]: if caller is not a current participant\n - [`ConversionError::DataConversion`](crate::errors::ConversionError::DataConversion): if the provided keyset contains a malformed public key\n - [`NodeMigrationError::KeysetMismatch`](crate::errors::NodeMigrationError::KeysetMismatch): if provided keyset does not match the expected keyset\n - [`NodeMigrationError::MigrationNotFound`](crate::errors::NodeMigrationError::MigrationNotFound): if no migration record exists for the caller\n - [`NodeMigrationError::AccountPublicKeyMismatch`](crate::errors::NodeMigrationError::AccountPublicKeyMismatch): if caller’s public key does not match the expected destination node\n - [`InvalidParameters::InvalidTeeRemoteAttestation`]: if destination node’s TEE quote is invalid",
553553
"kind": "call",
554554
"params": {
555555
"serialization_type": "json",

crates/devnet/src/mpc.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -495,11 +495,11 @@ impl MpcProposeUpdateContractCmd {
495495
.await
496496
.into_return_value()
497497
.expect("Failed to propose update");
498-
let update_id: u64 = serde_json::from_slice(&result).expect(&format!(
498+
let update_id: UpdateId = serde_json::from_slice(&result).expect(&format!(
499499
"Failed to deserialize result: {}",
500500
String::from_utf8_lossy(&result)
501501
));
502-
println!("Proposed update with ID {}", update_id);
502+
println!("Proposed update with ID {}", update_id.0);
503503
println!("Run the following command to vote for the update:");
504504
let self_exe = std::env::current_exe()
505505
.expect("Failed to get current executable path")
@@ -508,7 +508,7 @@ impl MpcProposeUpdateContractCmd {
508508
.to_string();
509509
println!(
510510
"{} mpc {} vote-update --update-id={}",
511-
self_exe, name, update_id
511+
self_exe, name, update_id.0
512512
);
513513
}
514514
}

crates/near-mpc-contract-interface/src/types/updates.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type Sha256Digest = [u8; 32];
2525
)]
2626
#[cfg_attr(
2727
all(feature = "abi", not(target_arch = "wasm32")),
28-
derive(schemars::JsonSchema, borsh::BorshSchema)
28+
derive(schemars::JsonSchema)
2929
)]
3030
pub struct UpdateId(pub u64);
3131

0 commit comments

Comments
 (0)