Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7d172e1
.
DSharifi Feb 20, 2026
02fcf18
revise on LLM reviews
DSharifi Feb 20, 2026
fdaa925
create separate module for with hex
DSharifi Feb 20, 2026
633b06e
move bounds to conts
DSharifi Feb 20, 2026
9e78903
Delete crates/near-mpc-sdk/src/sign.rs
DSharifi Feb 20, 2026
338609a
Update crates/contract/Cargo.toml
DSharifi Feb 20, 2026
3d63bd0
Merge remote-tracking branch 'origin/main' into dsharifi/add-bounded-vec
DSharifi Feb 20, 2026
d196924
Merge branch 'dsharifi/add-bounded-vec' of github.com:near/mpc into d…
DSharifi Feb 20, 2026
a412077
just bounded vec
DSharifi Feb 20, 2026
ff05535
Merge remote-tracking branch 'origin/main' into just-bounded-vec
DSharifi Feb 20, 2026
f3fff6f
cargo fmt
DSharifi Feb 20, 2026
4f8ef1f
fix doc tests
DSharifi Feb 20, 2026
a55c475
Merge remote-tracking branch 'origin/main' into just-bounded-vec
DSharifi Feb 20, 2026
c8dfdfe
rename into_vec
DSharifi Feb 20, 2026
fe3fb48
remove as_vec, we have as_slice
DSharifi Feb 20, 2026
ed0dd27
remove opt function, and impl mapping for all witnesses
DSharifi Feb 20, 2026
1738eca
created the sdk crate with builder
DSharifi Feb 20, 2026
74f89d0
added signing module
DSharifi Feb 20, 2026
211c10c
added sign for the method name
DSharifi Feb 20, 2026
3a90eae
make NotSet public
DSharifi Feb 20, 2026
c82139a
use sdk builder in sign_utils sandbox test
DSharifi Feb 20, 2026
33e5c4f
integrate the sdk into the contract sandbox tests
DSharifi Feb 20, 2026
a7f451e
sort/shear
DSharifi Feb 20, 2026
e0474c5
remove clone for type with copy
DSharifi Feb 20, 2026
7b44c5c
remove stale copy pasta comments
DSharifi Feb 20, 2026
f73856e
remove from implementation
DSharifi Feb 20, 2026
c9a4bfc
Merge remote-tracking branch 'origin/main' into dsharifi/make-sdk-crate
DSharifi Feb 23, 2026
d588938
move SignRequestArgs to interface crate
DSharifi Feb 23, 2026
ae84e11
remove serde unused dep
DSharifi Feb 23, 2026
01d9eed
move import to top
DSharifi Feb 23, 2026
1a891e6
rename to payload_v2
DSharifi Feb 23, 2026
cba6326
add comment to the bounds for payload sizes
DSharifi Feb 23, 2026
3015e3b
Merge remote-tracking branch 'origin/main' into dsharifi/make-sdk-crate
DSharifi Feb 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ members = [
"crates/foreign-chain-rpc-interfaces",
"crates/include-measurements",
"crates/mpc-attestation",
"crates/near-mpc-sdk",
"crates/node",
"crates/node-types",
"crates/primitives",
Expand Down Expand Up @@ -44,6 +45,7 @@ mpc-contract = { path = "crates/contract", features = ["dev-utils"] }
mpc-node = { path = "crates/node" }
mpc-primitives = { path = "crates/primitives", features = ["abi"] }
mpc-tls = { path = "crates/tls" }
near-mpc-sdk = { path = "crates/near-mpc-sdk" }
node-types = { path = "crates/node-types" }
tee-authority = { path = "crates/tee-authority" }
test-utils = { path = "crates/test-utils" }
Expand Down
6 changes: 4 additions & 2 deletions crates/contract-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ pub mod types {

pub use metrics::Metrics;
pub use primitives::{
AccountId, CkdAppId, DomainId, K256AffinePoint, K256Scalar, K256Signature,
SignatureResponse, Tweak,
AccountId, CkdAppId, DomainId, Ed25519Signature, K256AffinePoint, K256Scalar,
K256Signature, SignatureResponse, Tweak,
};
pub use sign::*;
pub use state::{
AddDomainsVotes, AttemptId, AuthenticatedAccountId, AuthenticatedParticipantId,
DomainConfig, DomainPurpose, DomainRegistry, EpochId, InitializingContractState, KeyEvent,
Expand All @@ -34,6 +35,7 @@ pub mod types {
mod metrics;
mod participants;
mod primitives;
mod sign;
mod state;
mod updates;
}
4 changes: 3 additions & 1 deletion crates/contract-interface/src/types/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ pub enum SignatureResponse {
}

#[serde_as]
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
#[derive(
Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize, derive_more::From,
)]
#[cfg_attr(
all(feature = "abi", not(target_arch = "wasm32")),
derive(schemars::JsonSchema)
Expand Down
31 changes: 31 additions & 0 deletions crates/contract-interface/src/types/sign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use bounded_collections::{BoundedVec, hex_serde};
use serde::{Deserialize, Serialize};

use crate::types::DomainId;

pub const ECDSA_PAYLOAD_SIZE_BYTES: usize = 32;

pub const EDDSA_PAYLOAD_SIZE_LOWER_BOUND_BYTES: usize = 32;
// Transaction signatures for Solana is over the whole transaction payload,
// not the transaction hash. The max size for a solana transaction is 1232 bytes,
// to fit in a single UDP packet, hence the 1232 byte upper bounds.
pub const EDDSA_PAYLOAD_SIZE_UPPER_BOUND_BYTES: usize = 1232;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct SignRequestArgs {
pub path: String,
pub payload_v2: Payload,
pub domain_id: DomainId,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Payload {
Ecdsa(
#[serde(with = "hex_serde")]
BoundedVec<u8, ECDSA_PAYLOAD_SIZE_BYTES, ECDSA_PAYLOAD_SIZE_BYTES>,
),
Eddsa(
#[serde(with = "hex_serde")]
BoundedVec<u8, EDDSA_PAYLOAD_SIZE_LOWER_BOUND_BYTES, EDDSA_PAYLOAD_SIZE_UPPER_BOUND_BYTES>,
),
}
1 change: 1 addition & 0 deletions crates/contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ futures = { workspace = true }
insta = { workspace = true }
mpc-contract = { workspace = true, features = ["test-utils"] }
near-abi = { workspace = true }
near-mpc-sdk = { workspace = true }
near-workspaces = { workspace = true }
rand = { workspace = true }
rand_core = { workspace = true }
Expand Down
8 changes: 6 additions & 2 deletions crates/contract/src/primitives/signature.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use crate::crypto_shared;
use crate::errors::{Error, InvalidParameters};
use crate::DomainId;
use contract_interface::types::{
ECDSA_PAYLOAD_SIZE_BYTES, EDDSA_PAYLOAD_SIZE_LOWER_BOUND_BYTES,
EDDSA_PAYLOAD_SIZE_UPPER_BOUND_BYTES,
};
use crypto_shared::derive_tweak;
use near_account_id::AccountId;
use near_sdk::{near, CryptoHash};
Expand Down Expand Up @@ -34,7 +38,7 @@ pub enum Payload {
definitions = "<[u8; 32] as ::borsh::BorshSchema>::add_definitions_recursively"
),))
)]
Bytes<32, 32>,
Bytes<ECDSA_PAYLOAD_SIZE_BYTES, ECDSA_PAYLOAD_SIZE_BYTES>,
),
Eddsa(
#[cfg_attr(
Expand All @@ -45,7 +49,7 @@ pub enum Payload {
definitions = "<Vec<u8> as ::borsh::BorshSchema>::add_definitions_recursively"
),))
)]
Bytes<32, 1232>,
Bytes<EDDSA_PAYLOAD_SIZE_LOWER_BOUND_BYTES, EDDSA_PAYLOAD_SIZE_UPPER_BOUND_BYTES>,
),
}

Expand Down
6 changes: 3 additions & 3 deletions crates/contract/tests/sandbox/upgrade_to_current_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use contract_interface::method_names;
use contract_interface::types::{self as dtos, ProtocolContractState};
use mpc_contract::{
crypto_shared::CKDResponse,
crypto_shared::SignatureResponse,
primitives::{
domain::{DomainConfig, DomainPurpose, SignatureScheme},
key_state::{EpochId, Keyset},
Expand All @@ -24,6 +23,7 @@ use mpc_contract::{
},
};
use near_account_id::AccountId;
use near_mpc_sdk::sign::SignatureRequestResponse;
use near_workspaces::{network::Sandbox, Account, Contract, Worker};
use rand_core::OsRng;
use rstest::rstest;
Expand Down Expand Up @@ -287,7 +287,7 @@ async fn upgrade_preserves_state_and_requests(
.unwrap();

let execution = pending.transaction.await.unwrap().into_result().unwrap();
let returned: SignatureResponse = execution.json().unwrap();
let returned: SignatureRequestResponse = execution.json().unwrap();

assert_eq!(
returned, pending.response.response,
Expand Down Expand Up @@ -434,7 +434,7 @@ async fn upgrade_allows_new_request_types(
.unwrap();

let execution = pending.transaction.await.unwrap().into_result().unwrap();
let returned: SignatureResponse = execution.json().unwrap();
let returned: SignatureRequestResponse = execution.json().unwrap();

assert_eq!(
returned, pending.response.response,
Expand Down
64 changes: 36 additions & 28 deletions crates/contract/tests/sandbox/utils/sign_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,26 @@ use contract_interface::method_names::{
use contract_interface::types::{self as dtos};
use digest::{Digest, FixedOutput};
use ecdsa::signature::Verifier as _;
use k256::elliptic_curve::sec1::ToEncodedPoint as _;
use k256::{
elliptic_curve::{point::DecompressPoint as _, Field as _, Group as _},
AffinePoint, FieldBytes, Secp256k1,
};
use mpc_contract::{
crypto_shared::{
derive_key_secp256k1, derive_tweak, ed25519_types, k256_types,
k256_types::SerializableAffinePoint, kdf::check_ec_signature, kdf::derive_app_id,
CKDResponse, SerializableScalar, SignatureResponse,
derive_key_secp256k1, derive_tweak, kdf::check_ec_signature, kdf::derive_app_id,
CKDResponse,
},
errors,
primitives::{
ckd::{CKDRequest, CKDRequestArgs},
domain::DomainId,
signature::{Bytes, Payload, SignRequestArgs, SignatureRequest, YieldIndex},
signature::{Bytes, Payload, SignatureRequest, YieldIndex},
},
};
use near_account_id::AccountId;
use near_mpc_sdk::sign::{Ed25519Signature, K256AffinePoint, K256Scalar, K256Signature};
use near_mpc_sdk::sign::{SignRequestArgs, SignRequestBuilder, SignatureRequestResponse};
use near_workspaces::{
network::Sandbox, operations::TransactionStatus, types::NearToken, Account, Contract, Worker,
};
Expand Down Expand Up @@ -156,7 +158,7 @@ impl SignRequestTest {
let execution = status.await?;
dbg!(&execution);
let execution = execution.into_result()?;
let returned_resp: SignatureResponse = execution.json()?;
let returned_resp: SignatureRequestResponse = execution.json()?;
assert_eq!(
returned_resp, self.response.response,
"Returned signature request does not match"
Expand All @@ -176,7 +178,7 @@ impl SignRequestTest {
#[derive(Debug, Serialize)]
pub struct SignResponseArgs {
pub request: SignatureRequest,
pub response: SignatureResponse,
pub response: SignatureRequestResponse,
}

impl SignResponseArgs {
Expand Down Expand Up @@ -458,7 +460,7 @@ fn create_response_secp256k1(
msg: &str,
path: &str,
signing_key: &ts_ecdsa::KeygenOutput,
) -> (Payload, SignatureRequest, SignatureResponse) {
) -> (Payload, SignatureRequest, SignatureRequestResponse) {
let (digest, payload) = process_message(msg);
let pk = signing_key.public_key;
let tweak = derive_tweak(predecessor_id, path);
Expand All @@ -478,7 +480,6 @@ fn create_response_secp256k1(
let respond_req = SignatureRequest::new(domain_id, payload.clone(), predecessor_id, path);
let big_r =
AffinePoint::decompress(&r_bytes, k256::elliptic_curve::subtle::Choice::from(0)).unwrap();
let s: k256::Scalar = *s.as_ref();

let recovery_id = if check_ec_signature(&derived_pk, &big_r, &s, payload.as_ecdsa().unwrap(), 0)
.is_ok()
Expand All @@ -490,11 +491,15 @@ fn create_response_secp256k1(
panic!("unable to use recovery id of 0 or 1");
};

let respond_resp = SignatureResponse::Secp256k1(k256_types::Signature {
big_r: SerializableAffinePoint {
affine_point: big_r,
let encoded_point = big_r.to_encoded_point(true);

let respond_resp = SignatureRequestResponse::Secp256k1(K256Signature {
big_r: K256AffinePoint {
affine_point: encoded_point.as_bytes().try_into().unwrap(),
},
s: K256Scalar {
scalar: s.to_bytes().into(),
},
s: SerializableScalar { scalar: s },
recovery_id,
});

Expand All @@ -507,7 +512,7 @@ fn create_response_ed25519(
msg: &str,
path: &str,
signing_key: &eddsa::KeygenOutput,
) -> (Payload, SignatureRequest, SignatureResponse) {
) -> (Payload, SignatureRequest, SignatureRequestResponse) {
let tweak = derive_tweak(predecessor_id, path);
let derived_signing_key = derive_secret_key_ed25519(signing_key, &tweak);

Expand All @@ -521,7 +526,7 @@ fn create_response_ed25519(
frost_ed25519::SigningKey::from_scalar(derived_signing_key.private_share.to_scalar())
.unwrap();

let signature = derived_signing_key
let signature: [u8; 64] = derived_signing_key
.sign(OsRng, &payload)
.serialize()
.unwrap()
Expand All @@ -533,8 +538,8 @@ fn create_response_ed25519(

let respond_req = SignatureRequest::new(domain_id, payload.clone(), predecessor_id, path);

let signature_response = SignatureResponse::Ed25519 {
signature: ed25519_types::Signature::new(signature),
let signature_response = SignatureRequestResponse::Ed25519 {
signature: Ed25519Signature::from(signature),
};

(payload, respond_req, signature_response)
Expand Down Expand Up @@ -570,12 +575,13 @@ fn gen_ed25519_sign_test(
let path: String = rng.gen::<usize>().to_string();
let (payload, request, response) =
create_response_ed25519(domain_id, predecessor_id, &msg, &path, sk);
let args = SignRequestArgs {
payload_v2: Some(payload.clone()),
path,
domain_id: Some(domain_id),
..Default::default()
};
let args = SignRequestBuilder::new()
.with_path(path)
.with_payload(near_mpc_sdk::sign::Payload::Eddsa(
payload.as_eddsa().unwrap().to_vec().try_into().unwrap(),
))
.with_domain_id(domain_id.0)
.build();
SignRequestTest {
response: SignResponseArgs { request, response },
args,
Expand All @@ -592,12 +598,14 @@ pub fn gen_secp_256k1_sign_test(
let path: String = rng.gen::<usize>().to_string();
let (payload, request, response) =
create_response_secp256k1(domain_id, predecessor_id, &msg, &path, sk);
let args = SignRequestArgs {
payload_v2: Some(payload.clone()),
path,
domain_id: Some(domain_id),
..Default::default()
};

let payload_bytes: [u8; 32] = *payload.as_ecdsa().unwrap();

let args = SignRequestBuilder::new()
.with_path(path)
.with_payload(near_mpc_sdk::sign::Payload::Ecdsa(payload_bytes.into()))
.with_domain_id(domain_id.0)
.build();
SignRequestTest {
response: SignResponseArgs { request, response },
args,
Expand Down
9 changes: 7 additions & 2 deletions crates/devnet/src/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::collections::BTreeMap;

use contract_interface::method_names;
use contract_interface::{
method_names,
types::{EDDSA_PAYLOAD_SIZE_LOWER_BOUND_BYTES, EDDSA_PAYLOAD_SIZE_UPPER_BOUND_BYTES},
};
use mpc_contract::primitives::{
ckd::CKDRequestArgs,
domain::{DomainConfig, SignatureScheme},
Expand Down Expand Up @@ -168,7 +171,9 @@ fn make_payload(scheme: SignatureScheme) -> Payload {
Payload::Ecdsa(Bytes::new(rand::random::<[u8; 32]>().to_vec()).unwrap())
}
SignatureScheme::Ed25519 => {
let len = rand::random_range(32..=1232);
let len = rand::random_range(
EDDSA_PAYLOAD_SIZE_LOWER_BOUND_BYTES..=EDDSA_PAYLOAD_SIZE_UPPER_BOUND_BYTES,
);
let mut payload = vec![0; len];
rand::rng().fill_bytes(&mut payload);
Payload::Eddsa(Bytes::new(payload).unwrap())
Expand Down
14 changes: 14 additions & 0 deletions crates/near-mpc-sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "near-mpc-sdk"
version.workspace = true
edition.workspace = true
license.workspace = true

[dependencies]
bounded-collections = { workspace = true }
contract-interface = { workspace = true }

[dev-dependencies]

[lints]
workspace = true
4 changes: 4 additions & 0 deletions crates/near-mpc-sdk/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub use bounded_collections;
pub use contract_interface;

pub mod sign;
Loading