From 69dd64cef9ad7e17021bdbbe44cddc447ee90f14 Mon Sep 17 00:00:00 2001 From: Bowen Wang Date: Wed, 13 Dec 2023 13:10:08 -0800 Subject: [PATCH 1/8] adding experiment with self calls --- contract/src/lib.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/contract/src/lib.rs b/contract/src/lib.rs index d1eb05fd0..9929c975c 100644 --- a/contract/src/lib.rs +++ b/contract/src/lib.rs @@ -1,6 +1,7 @@ use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use near_sdk::collections::LookupMap; use near_sdk::serde::{Deserialize, Serialize}; -use near_sdk::{env, near_bindgen, AccountId, PanicOnDefault, PublicKey}; +use near_sdk::{env, near_bindgen, AccountId, PanicOnDefault, Promise, PromiseOrValue, PublicKey}; use std::collections::{BTreeMap, HashSet}; type ParticipantId = u32; @@ -73,6 +74,7 @@ pub enum ProtocolContractState { #[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)] pub struct MpcContract { protocol_state: ProtocolContractState, + pending_requests: LookupMap, Option>, } #[near_bindgen] @@ -86,6 +88,7 @@ impl MpcContract { threshold, pk_votes: BTreeMap::new(), }), + pending_requests: LookupMap::new(b"m"), } } @@ -290,10 +293,32 @@ impl MpcContract { } #[allow(unused_variables)] - pub fn sign(&mut self, payload: [u8; 32]) -> [u8; 32] { - near_sdk::env::random_seed_array() + pub fn sign(&mut self, payload: [u8; 32]) -> Promise { + let hash = env::sha256(&payload); + self.pending_requests.insert(&hash, &None); + Self::ext(env::current_account_id()).sign_helper(hash) + } + + pub fn sign_helper(&mut self, payload_id: Vec) -> PromiseOrValue { + if let Some(signature) = self.pending_requests.get(&payload_id) { + match signature { + Some(signature) => { + self.pending_requests.remove(&payload_id); + PromiseOrValue::Value(signature) + } + None => { + env::log_str("not ready"); + let account_id = env::current_account_id(); + PromiseOrValue::Promise(Self::ext(account_id).sign_helper(payload_id)) + } + } + } else { + env::panic_str("unexpected request"); + } } #[allow(unused_variables)] - pub fn respond(&mut self, receipt_id: [u8; 32], big_r: String, s: String) {} + pub fn respond(&mut self, receipt_id: [u8; 32], big_r: String, s: String) { + self.pending_requests.insert(&receipt_id.to_vec(), &Some(s)); + } } From a409b2f807a1f7c07e17d534bdffe7ddaf4eb074 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Thu, 4 Jan 2024 18:02:02 +1100 Subject: [PATCH 2/8] make node code work with self calls --- Cargo.lock | 1 + contract/Cargo.toml | 1 + contract/src/lib.rs | 1 + node/src/indexer.rs | 1 + node/src/protocol/signature.rs | 9 +++++---- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab54f35d7..b4eda9501 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3833,6 +3833,7 @@ dependencies = [ "near-sdk", "schemars", "serde", + "serde_json", ] [[package]] diff --git a/contract/Cargo.toml b/contract/Cargo.toml index 23dbc0f36..5695b58a4 100644 --- a/contract/Cargo.toml +++ b/contract/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib", "lib"] [dependencies] near-sdk = "4.1.1" serde = { version = "1", features = ["derive"] } +serde_json = "1" schemars = "0.8" [profile.release] diff --git a/contract/src/lib.rs b/contract/src/lib.rs index e3a0e1dd4..81120c8a0 100644 --- a/contract/src/lib.rs +++ b/contract/src/lib.rs @@ -295,6 +295,7 @@ impl MpcContract { #[allow(unused_variables)] pub fn sign(&mut self, payload: [u8; 32], path: String) -> Promise { self.pending_requests.insert(&payload, &None); + env::log_str(&serde_json::to_string(&near_sdk::env::random_seed_array()).unwrap()); Self::ext(env::current_account_id()).sign_helper(payload) } diff --git a/node/src/indexer.rs b/node/src/indexer.rs index 3be1e5627..92f36e2a1 100644 --- a/node/src/indexer.rs +++ b/node/src/indexer.rs @@ -87,6 +87,7 @@ async fn handle_block( serde_json::from_slice::<'_, SignPayload>(function_call.args()) { let Ok(entropy) = serde_json::from_slice::<'_, [u8; 32]>(&result) else { + tracing::warn!("incorrect `sign` return type: {:?}", result); continue; }; let epsilon = diff --git a/node/src/protocol/signature.rs b/node/src/protocol/signature.rs index b6933e6e8..519291c0c 100644 --- a/node/src/protocol/signature.rs +++ b/node/src/protocol/signature.rs @@ -102,7 +102,7 @@ pub struct SignatureManager { /// Ongoing signature generation protocols. generators: HashMap, /// Generated signatures assigned to the current node that are yet to be published. - signatures: Vec<(CryptoHash, FullSignature)>, + signatures: Vec<(CryptoHash, [u8; 32], FullSignature)>, participants: Vec, me: Participant, @@ -300,7 +300,8 @@ impl SignatureManager { "completed signature generation" ); if generator.proposer == self.me { - self.signatures.push((*receipt_id, output)); + self.signatures + .push((*receipt_id, generator.msg_hash, output)); } // Do not retain the protocol return false; @@ -317,7 +318,7 @@ impl SignatureManager { signer: &T, mpc_contract_id: &AccountId, ) -> Result<(), near_fetch::Error> { - for (receipt_id, signature) in self.signatures.drain(..) { + for (receipt_id, payload, signature) in self.signatures.drain(..) { // TODO: Figure out how to properly serialize the signature // let r_s = signature.big_r.x().concat(signature.s.to_bytes()); // let tag = @@ -334,7 +335,7 @@ impl SignatureManager { FunctionCallAction { method_name: "respond".to_string(), args: serde_json::to_vec(&serde_json::json!({ - "receipt_id": receipt_id.as_bytes(), + "payload": payload, "big_r": signature.big_r, "s": signature.s })) From 056a3107698e1226710e3638dd88c89426edcce8 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Mon, 8 Jan 2024 18:39:30 +1100 Subject: [PATCH 3/8] index receipt txs properly --- integration-tests/tests/multichain/mod.rs | 7 +++++++ node/src/indexer.rs | 20 ++++++++++++++------ node/src/protocol/signature.rs | 4 ++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/integration-tests/tests/multichain/mod.rs b/integration-tests/tests/multichain/mod.rs index 12a098631..9012c06dd 100644 --- a/integration-tests/tests/multichain/mod.rs +++ b/integration-tests/tests/multichain/mod.rs @@ -66,6 +66,13 @@ async fn test_signature() -> anyhow::Result<()> { let state_0 = wait_for::running_mpc(&ctx, 0).await?; assert_eq!(state_0.participants.len(), 3); + for i in 0..ctx.nodes.len() { + wait_for::has_at_least_triples(&ctx, i, 2).await?; + } + for i in 0..ctx.nodes.len() { + wait_for::has_at_least_presignatures(&ctx, i, 2).await?; + } + let worker = &ctx.nodes.ctx().worker; let (account_id, secret_key) = worker.dev_generate().await; worker diff --git a/node/src/indexer.rs b/node/src/indexer.rs index 92f36e2a1..173a62cc3 100644 --- a/node/src/indexer.rs +++ b/node/src/indexer.rs @@ -78,7 +78,7 @@ async fn handle_block( for action in block.actions().cloned().collect::>() { if action.receiver_id() == ctx.mpc_contract_id { let receipt = block.receipt_by_id(&action.receipt_id()).unwrap(); - let ExecutionStatus::SuccessValue(result) = receipt.status() else { + let ExecutionStatus::SuccessReceiptId(receipt_id) = receipt.status() else { continue; }; if let Some(function_call) = action.as_function_call() { @@ -86,15 +86,23 @@ async fn handle_block( if let Ok(sign_payload) = serde_json::from_slice::<'_, SignPayload>(function_call.args()) { - let Ok(entropy) = serde_json::from_slice::<'_, [u8; 32]>(&result) else { - tracing::warn!("incorrect `sign` return type: {:?}", result); + if receipt.logs().is_empty() { + tracing::warn!("`sign` did not produce entropy"); + continue; + } + let Ok(entropy) = serde_json::from_str::<'_, [u8; 32]>(&receipt.logs()[0]) + else { + tracing::warn!( + "`sign` did not produce entropy correctly: {:?}", + receipt.logs()[0] + ); continue; }; let epsilon = kdf::derive_epsilon(&action.predecessor_id(), &sign_payload.path); - let delta = kdf::derive_delta(receipt.receipt_id(), entropy); + let delta = kdf::derive_delta(receipt_id, entropy); tracing::info!( - receipt_id = %receipt.receipt_id(), + receipt_id = %receipt_id, caller_id = receipt.predecessor_id().to_string(), payload = hex::encode(sign_payload.payload), entropy = hex::encode(entropy), @@ -102,7 +110,7 @@ async fn handle_block( ); let mut queue = ctx.queue.write().await; queue.add(SignRequest { - receipt_id: receipt.receipt_id(), + receipt_id, msg_hash: sign_payload.payload, epsilon, delta, diff --git a/node/src/protocol/signature.rs b/node/src/protocol/signature.rs index 519291c0c..a0707e671 100644 --- a/node/src/protocol/signature.rs +++ b/node/src/protocol/signature.rs @@ -326,8 +326,7 @@ impl SignatureManager { // let signature = r_s.append(tag); // let signature = Secp256K1Signature::try_from(signature.as_slice()).unwrap(); // let signature = Signature::SECP256K1(signature); - tracing::info!(%receipt_id, big_r = signature.big_r.to_base58(), s = ?signature.s, "publishing signature response"); - rpc_client + let response = rpc_client .send_tx( signer, mpc_contract_id, @@ -346,6 +345,7 @@ impl SignatureManager { )], ) .await?; + tracing::info!(%receipt_id, big_r = signature.big_r.to_base58(), s = ?signature.s, status = ?response.status, "published signature response"); } Ok(()) } From 9d7d5448b3b8a6ef442c71877106a36d4a2c96e2 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Tue, 9 Jan 2024 22:43:50 +1100 Subject: [PATCH 4/8] migrate to async_broadcast_tx --- contract/src/lib.rs | 6 +- integration-tests/tests/lib.rs | 7 +- integration-tests/tests/multichain/mod.rs | 93 +++++++++++++++-------- 3 files changed, 69 insertions(+), 37 deletions(-) diff --git a/contract/src/lib.rs b/contract/src/lib.rs index 81120c8a0..1de7f761c 100644 --- a/contract/src/lib.rs +++ b/contract/src/lib.rs @@ -74,7 +74,7 @@ pub enum ProtocolContractState { #[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)] pub struct MpcContract { protocol_state: ProtocolContractState, - pending_requests: LookupMap<[u8; 32], Option>, + pending_requests: LookupMap<[u8; 32], Option<(String, String)>>, } #[near_bindgen] @@ -299,7 +299,7 @@ impl MpcContract { Self::ext(env::current_account_id()).sign_helper(payload) } - pub fn sign_helper(&mut self, payload: [u8; 32]) -> PromiseOrValue { + pub fn sign_helper(&mut self, payload: [u8; 32]) -> PromiseOrValue<(String, String)> { if let Some(signature) = self.pending_requests.get(&payload) { match signature { Some(signature) => { @@ -319,6 +319,6 @@ impl MpcContract { #[allow(unused_variables)] pub fn respond(&mut self, payload: [u8; 32], big_r: String, s: String) { - self.pending_requests.insert(&payload, &Some(s)); + self.pending_requests.insert(&payload, &Some((big_r, s))); } } diff --git a/integration-tests/tests/lib.rs b/integration-tests/tests/lib.rs index 49290050b..1a739d0cf 100644 --- a/integration-tests/tests/lib.rs +++ b/integration-tests/tests/lib.rs @@ -13,6 +13,7 @@ use mpc_recovery::{ use mpc_recovery_integration_tests::env::containers::DockerClient; use mpc_recovery_integration_tests::indexer::FullSignature; use mpc_recovery_integration_tests::{env, indexer}; +use near_jsonrpc_client::JsonRpcClient; use near_primitives::hash::CryptoHash; use near_workspaces::{network::Sandbox, Worker}; use std::collections::HashMap; @@ -68,6 +69,7 @@ where pub struct MultichainTestContext<'a> { nodes: mpc_recovery_integration_tests::multichain::Nodes<'a>, rpc_client: near_fetch::Client, + jsonrpc_client: JsonRpcClient, http_client: reqwest::Client, responses: Arc>>, } @@ -97,10 +99,13 @@ where .unwrap(); }); - let rpc_client = near_fetch::Client::new(&nodes.ctx().lake_indexer.rpc_host_address); + let connector = JsonRpcClient::new_client(); + let jsonrpc_client = connector.connect(&nodes.ctx().lake_indexer.rpc_host_address); + let rpc_client = near_fetch::Client::from_client(jsonrpc_client.clone()); f(MultichainTestContext { nodes, rpc_client, + jsonrpc_client, http_client: reqwest::Client::default(), responses, }) diff --git a/integration-tests/tests/multichain/mod.rs b/integration-tests/tests/multichain/mod.rs index 9012c06dd..c0dc91ddf 100644 --- a/integration-tests/tests/multichain/mod.rs +++ b/integration-tests/tests/multichain/mod.rs @@ -1,12 +1,18 @@ use crate::{wait_for, with_multichain_nodes}; +use anyhow::Context; +use backon::{ExponentialBuilder, Retryable}; use k256::elliptic_curve::sec1::FromEncodedPoint; use k256::{AffinePoint, EncodedPoint, Scalar, Secp256k1}; use mpc_recovery_node::kdf; use mpc_recovery_node::util::ScalarExt; -use near_crypto::InMemorySigner; -use near_primitives::transaction::{Action, FunctionCallAction}; -use near_primitives::views::ExecutionStatusView; +use near_crypto::{InMemorySigner, Signer}; +use near_fetch::signer::ExposeAccountId; +use near_jsonrpc_client::methods::broadcast_tx_async::RpcBroadcastTxAsyncRequest; +use near_jsonrpc_client::methods::tx::{RpcTransactionStatusRequest, TransactionInfo}; +use near_primitives::transaction::{Action, FunctionCallAction, Transaction}; +use near_primitives::views::FinalExecutionStatus; use rand::Rng; +use std::time::Duration; use test_log::test; #[test(tokio::test)] @@ -80,45 +86,66 @@ async fn test_signature() -> anyhow::Result<()> { .await? .into_result()?; let payload: [u8; 32] = rand::thread_rng().gen(); - let outcome = ctx + let signer = InMemorySigner { + account_id: account_id.clone(), + public_key: secret_key.public_key().clone().into(), + secret_key: secret_key.to_string().parse()?, + }; + let (nonce, block_hash, _) = ctx .rpc_client - .send_tx( - &InMemorySigner { - account_id: account_id.clone(), - public_key: secret_key.public_key().clone().into(), - secret_key: secret_key.to_string().parse()?, - }, - ctx.nodes.ctx().mpc_contract.id(), - vec![Action::FunctionCall(FunctionCallAction { - method_name: "sign".to_string(), - args: serde_json::to_vec(&serde_json::json!({ - "payload": payload, - "path": "test", - }))?, - gas: 300_000_000_000_000, - deposit: 0, - })], - ) + .fetch_nonce(signer.account_id(), &signer.public_key()) .await?; - let ExecutionStatusView::SuccessReceiptId(receipt_id) = - outcome.transaction_outcome.outcome.status - else { - anyhow::bail!("missing receipt id"); - }; - - let signature = wait_for::has_response(&ctx, receipt_id).await?; - let signature_output = cait_sith::FullSignature:: { - big_r: signature.big_r, - s: signature.s, + let tx_hash = ctx + .jsonrpc_client + .call(&RpcBroadcastTxAsyncRequest { + signed_transaction: Transaction { + nonce, + block_hash, + signer_id: signer.account_id().clone(), + public_key: signer.public_key(), + receiver_id: ctx.nodes.ctx().mpc_contract.id().clone(), + actions: vec![Action::FunctionCall(FunctionCallAction { + method_name: "sign".to_string(), + args: serde_json::to_vec(&serde_json::json!({ + "payload": payload, + "path": "test", + }))?, + gas: 300_000_000_000_000, + deposit: 0, + })], + } + .sign(&signer), + }) + .await?; + tokio::time::sleep(Duration::from_secs(1)).await; + let is_tx_ready = || async { + let outcome_view = ctx + .jsonrpc_client + .call(RpcTransactionStatusRequest { + transaction_info: TransactionInfo::TransactionId { + hash: tx_hash, + account_id: ctx.nodes.ctx().mpc_contract.id().clone(), + }, + }) + .await?; + let FinalExecutionStatus::SuccessValue(payload) = outcome_view.status else { + anyhow::bail!("tx finished unsuccessfully: {:?}", outcome_view.status); + }; + let (big_r, s): (AffinePoint, Scalar) = serde_json::from_slice(&payload)?; + let signature = cait_sith::FullSignature:: { big_r: big_r, s: s }; + Ok(signature) }; - + let signature = is_tx_ready + .retry(&ExponentialBuilder::default().with_max_times(6)) + .await + .with_context(|| format!("failed to wait for signature response"))?; let mut bytes = vec![0x04]; bytes.extend_from_slice(&state_0.public_key.as_bytes()[1..]); let point = EncodedPoint::from_bytes(bytes).unwrap(); let public_key = AffinePoint::from_encoded_point(&point).unwrap(); let epsilon = kdf::derive_epsilon(&account_id, "test"); - assert!(signature_output.verify( + assert!(signature.verify( &kdf::derive_key(public_key, epsilon), &Scalar::from_bytes(&payload), )); From 2d5ecec3264ab2d99a1341b3e924a523926695f9 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Tue, 9 Jan 2024 22:48:28 +1100 Subject: [PATCH 5/8] delete dead code --- integration-tests/src/indexer.rs | 100 ------------------------------- integration-tests/src/lib.rs | 1 - integration-tests/tests/lib.rs | 50 +--------------- 3 files changed, 1 insertion(+), 150 deletions(-) delete mode 100644 integration-tests/src/indexer.rs diff --git a/integration-tests/src/indexer.rs b/integration-tests/src/indexer.rs deleted file mode 100644 index ad310c007..000000000 --- a/integration-tests/src/indexer.rs +++ /dev/null @@ -1,100 +0,0 @@ -use k256::{AffinePoint, Scalar}; -use near_lake_framework::{LakeBuilder, LakeContext}; -use near_lake_primitives::actions::ActionMetaDataExt; -use near_lake_primitives::{receipts::ExecutionStatus, AccountId}; -use near_primitives::hash::CryptoHash; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::sync::Arc; -use tokio::sync::RwLock; - -#[derive(Debug, Serialize, Deserialize)] -struct RespondPayload { - receipt_id: [u8; 32], - big_r: AffinePoint, - s: Scalar, -} - -pub struct FullSignature { - pub big_r: AffinePoint, - pub s: Scalar, -} - -#[derive(LakeContext)] -struct Context { - mpc_contract_id: AccountId, - responses: Arc>>, -} - -async fn handle_block( - mut block: near_lake_primitives::block::Block, - ctx: &Context, -) -> anyhow::Result<()> { - for action in block.actions().cloned().collect::>() { - if action.receiver_id() == ctx.mpc_contract_id { - let receipt = block.receipt_by_id(&action.receipt_id()).unwrap(); - if let Some(function_call) = action.as_function_call() { - if function_call.method_name() == "respond" { - let ExecutionStatus::SuccessValue(_) = receipt.status() else { - tracing::error!("indexed a failed `respond` function call"); - continue; - }; - if let Ok(respond_payload) = - serde_json::from_slice::<'_, RespondPayload>(function_call.args()) - { - let receipt_id = CryptoHash(respond_payload.receipt_id); - tracing::info!( - receipt_id = %receipt_id, - caller_id = receipt.predecessor_id().to_string(), - big_r = ?respond_payload.big_r, - s = ?respond_payload.s, - "indexed new `respond` function call" - ); - let mut responses = ctx.responses.write().await; - responses.insert( - receipt_id, - FullSignature { - big_r: respond_payload.big_r, - s: respond_payload.s, - }, - ); - drop(responses); - } - } - } - } - } - Ok(()) -} - -pub fn run( - s3_bucket: &str, - s3_region: &str, - start_block_height: u64, - s3_url: &str, - mpc_contract_id: AccountId, - responses: Arc>>, -) -> anyhow::Result<()> { - let mut lake_builder = LakeBuilder::default() - .s3_bucket_name(s3_bucket) - .s3_region_name(s3_region) - .start_block_height(start_block_height); - let lake = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() - .block_on(async { - let aws_config = aws_config::from_env().load().await; - let s3_config = aws_sdk_s3::config::Builder::from(&aws_config) - .endpoint_url(s3_url) - .build(); - lake_builder = lake_builder.s3_config(s3_config); - lake_builder.build() - })?; - let context = Context { - mpc_contract_id, - responses, - }; - lake.run_with_context(handle_block, &context)?; - Ok(()) -} diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index ee0bc5f73..dcd12bc4f 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -12,7 +12,6 @@ use crate::env::containers::{self, LocalStack}; use testcontainers::{Container, GenericImage}; pub mod env; -pub mod indexer; pub mod mpc; pub mod multichain; pub mod sandbox; diff --git a/integration-tests/tests/lib.rs b/integration-tests/tests/lib.rs index 1a739d0cf..bfd02a5b4 100644 --- a/integration-tests/tests/lib.rs +++ b/integration-tests/tests/lib.rs @@ -10,16 +10,10 @@ use mpc_recovery::{ ClaimOidcResponse, MpcPkResponse, NewAccountResponse, SignResponse, UserCredentialsResponse, }, }; +use mpc_recovery_integration_tests::env; use mpc_recovery_integration_tests::env::containers::DockerClient; -use mpc_recovery_integration_tests::indexer::FullSignature; -use mpc_recovery_integration_tests::{env, indexer}; use near_jsonrpc_client::JsonRpcClient; -use near_primitives::hash::CryptoHash; use near_workspaces::{network::Sandbox, Worker}; -use std::collections::HashMap; -use std::sync::Arc; -use std::thread; -use tokio::sync::RwLock; pub struct TestContext { env: String, @@ -71,7 +65,6 @@ pub struct MultichainTestContext<'a> { rpc_client: near_fetch::Client, jsonrpc_client: JsonRpcClient, http_client: reqwest::Client, - responses: Arc>>, } async fn with_multichain_nodes(nodes: usize, f: F) -> anyhow::Result<()> @@ -81,24 +74,6 @@ where let docker_client = DockerClient::default(); let nodes = mpc_recovery_integration_tests::multichain::run(nodes, &docker_client).await?; - let s3_bucket = nodes.ctx().localstack.s3_bucket.clone(); - let s3_region = nodes.ctx().localstack.s3_region.clone(); - let s3_url = nodes.ctx().localstack.s3_host_address.clone(); - let mpc_contract_id = nodes.ctx().mpc_contract.id().clone(); - let responses = Arc::new(RwLock::new(HashMap::new())); - let responses_clone = responses.clone(); - thread::spawn(move || { - indexer::run( - &s3_bucket, - &s3_region, - 0, - &s3_url, - mpc_contract_id, - responses_clone, - ) - .unwrap(); - }); - let connector = JsonRpcClient::new_client(); let jsonrpc_client = connector.connect(&nodes.ctx().lake_indexer.rpc_host_address); let rpc_client = near_fetch::Client::from_client(jsonrpc_client.clone()); @@ -107,7 +82,6 @@ where rpc_client, jsonrpc_client, http_client: reqwest::Client::default(), - responses, }) .await?; @@ -222,9 +196,7 @@ mod wait_for { use backon::Retryable; use mpc_contract::ProtocolContractState; use mpc_contract::RunningContractState; - use mpc_recovery_integration_tests::indexer::FullSignature; use mpc_recovery_node::web::StateView; - use near_primitives::hash::CryptoHash; pub async fn running_mpc<'a>( ctx: &MultichainTestContext<'a>, @@ -309,26 +281,6 @@ mod wait_for { .await .with_context(|| format!("mpc node '{id}' failed to generate '{expected_presignature_count}' presignatures before deadline")) } - - pub async fn has_response<'a>( - ctx: &MultichainTestContext<'a>, - receipt_id: CryptoHash, - ) -> anyhow::Result { - let is_enough_presignatures = || async { - let mut responses = ctx.responses.write().await; - if let Some(signature) = responses.remove(&receipt_id) { - return Ok(signature); - } - drop(responses); - anyhow::bail!("mpc has not responded yet") - }; - is_enough_presignatures - .retry(&ExponentialBuilder::default().with_max_times(8)) - .await - .with_context(|| { - format!("mpc failed to respond to receipt id '{receipt_id}' before deadline") - }) - } } trait MpcCheck { From 1fbf8104d1f095065a87eaa6c9f6e75656838a89 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Wed, 10 Jan 2024 22:45:04 +1100 Subject: [PATCH 6/8] appease clippy --- integration-tests/tests/multichain/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/tests/multichain/mod.rs b/integration-tests/tests/multichain/mod.rs index c0dc91ddf..c5fca8b66 100644 --- a/integration-tests/tests/multichain/mod.rs +++ b/integration-tests/tests/multichain/mod.rs @@ -132,13 +132,13 @@ async fn test_signature() -> anyhow::Result<()> { anyhow::bail!("tx finished unsuccessfully: {:?}", outcome_view.status); }; let (big_r, s): (AffinePoint, Scalar) = serde_json::from_slice(&payload)?; - let signature = cait_sith::FullSignature:: { big_r: big_r, s: s }; + let signature = cait_sith::FullSignature:: { big_r, s }; Ok(signature) }; let signature = is_tx_ready .retry(&ExponentialBuilder::default().with_max_times(6)) .await - .with_context(|| format!("failed to wait for signature response"))?; + .with_context(|| "failed to wait for signature response")?; let mut bytes = vec![0x04]; bytes.extend_from_slice(&state_0.public_key.as_bytes()[1..]); let point = EncodedPoint::from_bytes(bytes).unwrap(); From f89871ee33047b9e92d5dc2a68e275f7f1bbf741 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Thu, 11 Jan 2024 13:29:07 +1100 Subject: [PATCH 7/8] apply suggestions --- contract/src/lib.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/contract/src/lib.rs b/contract/src/lib.rs index 62143db5a..b6ae0c863 100644 --- a/contract/src/lib.rs +++ b/contract/src/lib.rs @@ -296,10 +296,15 @@ impl MpcContract { pub fn sign(&mut self, payload: [u8; 32], path: String) -> Promise { self.pending_requests.insert(&payload, &None); env::log_str(&serde_json::to_string(&near_sdk::env::random_seed_array()).unwrap()); - Self::ext(env::current_account_id()).sign_helper(payload) + Self::ext(env::current_account_id()).sign_helper(payload, 0) } - pub fn sign_helper(&mut self, payload: [u8; 32]) -> PromiseOrValue<(String, String)> { + #[private] + pub fn sign_helper( + &mut self, + payload: [u8; 32], + depth: usize, + ) -> PromiseOrValue<(String, String)> { if let Some(signature) = self.pending_requests.get(&payload) { match signature { Some(signature) => { @@ -307,9 +312,9 @@ impl MpcContract { PromiseOrValue::Value(signature) } None => { - env::log_str("not ready"); + env::log_str(&format!("not ready yet (depth={})", depth)); let account_id = env::current_account_id(); - PromiseOrValue::Promise(Self::ext(account_id).sign_helper(payload)) + PromiseOrValue::Promise(Self::ext(account_id).sign_helper(payload, depth + 1)) } } } else { From 4ab556981830d1633d2f00927cd0e7933722641a Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Mon, 22 Jan 2024 19:57:05 +0200 Subject: [PATCH 8/8] prevent submission of the same paylaod --- contract/src/lib.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/contract/src/lib.rs b/contract/src/lib.rs index 125987004..049f31c5b 100644 --- a/contract/src/lib.rs +++ b/contract/src/lib.rs @@ -261,9 +261,14 @@ impl MpcContract { #[allow(unused_variables)] pub fn sign(&mut self, payload: [u8; 32], path: String) -> Promise { - self.pending_requests.insert(&payload, &None); - env::log_str(&serde_json::to_string(&near_sdk::env::random_seed_array()).unwrap()); - Self::ext(env::current_account_id()).sign_helper(payload, 0) + match self.pending_requests.get(&payload) { + None => { + self.pending_requests.insert(&payload, &None); + env::log_str(&serde_json::to_string(&near_sdk::env::random_seed_array()).unwrap()); + Self::ext(env::current_account_id()).sign_helper(payload, 0) + } + Some(_) => env::panic_str("Signature for this payload already requested"), + } } #[private]