diff --git a/chain-signatures/Cargo.lock b/chain-signatures/Cargo.lock index 112306b3d..84482337b 100644 --- a/chain-signatures/Cargo.lock +++ b/chain-signatures/Cargo.lock @@ -4141,9 +4141,9 @@ dependencies = [ [[package]] name = "near-fetch" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d883b3727a4cfa0d528f6bd19584b9339de21c57f5eb53def7e952fa9e0c58e8" +checksum = "a82f56751beeb2537a222f1df033c8063ed4f41cab7cf1ba89723601e5a307e9" dependencies = [ "base64 0.22.1", "near-account-id", diff --git a/chain-signatures/node/Cargo.toml b/chain-signatures/node/Cargo.toml index a82ffa3eb..4503d85f1 100644 --- a/chain-signatures/node/Cargo.toml +++ b/chain-signatures/node/Cargo.toml @@ -46,7 +46,7 @@ url = { version = "2.4.0", features = ["serde"] } near-account-id = "1.0.0" near-crypto = "0.23.0" -near-fetch = "0.5.0" +near-fetch = "0.5.1" near-lake-framework = { git = "https://github.com/near/near-lake-framework-rs", branch = "node/1.40-and-async-run" } near-lake-primitives = { git = "https://github.com/near/near-lake-framework-rs", branch = "node/1.40-and-async-run" } near-primitives = "0.23.0" diff --git a/chain-signatures/node/src/protocol/cryptography.rs b/chain-signatures/node/src/protocol/cryptography.rs index a3f1cb968..294a8242c 100644 --- a/chain-signatures/node/src/protocol/cryptography.rs +++ b/chain-signatures/node/src/protocol/cryptography.rs @@ -427,7 +427,7 @@ impl CryptographicProtocol for RunningState { // block height is up to date, such that they too can process signature requests. If they cannot // then they are considered unstable and should not be a part of signature generation this round. let stable = ctx.mesh().stable_participants().await; - tracing::info!(?stable, "stable participants"); + tracing::trace!(?stable, "stable participants"); let mut sign_queue = self.sign_queue.write().await; crate::metrics::SIGN_QUEUE_SIZE diff --git a/integration-tests/chain-signatures/Cargo.lock b/integration-tests/chain-signatures/Cargo.lock index ed5444513..9fbdc8a08 100644 --- a/integration-tests/chain-signatures/Cargo.lock +++ b/integration-tests/chain-signatures/Cargo.lock @@ -4614,9 +4614,9 @@ dependencies = [ [[package]] name = "near-fetch" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d883b3727a4cfa0d528f6bd19584b9339de21c57f5eb53def7e952fa9e0c58e8" +checksum = "a82f56751beeb2537a222f1df033c8063ed4f41cab7cf1ba89723601e5a307e9" dependencies = [ "base64 0.22.1", "near-account-id", diff --git a/integration-tests/chain-signatures/Cargo.toml b/integration-tests/chain-signatures/Cargo.toml index 2f638cbc0..058ce29cc 100644 --- a/integration-tests/chain-signatures/Cargo.toml +++ b/integration-tests/chain-signatures/Cargo.toml @@ -35,7 +35,7 @@ k256 = { version = "0.13.1", features = ["sha256", "ecdsa", "serde"] } # near dependencies near-account-id = "1" near-crypto = "0.23.0" -near-fetch = "0.5.0" +near-fetch = "0.5.1" near-jsonrpc-client = "0.10.1" near-primitives = "0.23.0" near-lake-framework = { git = "https://github.com/near/near-lake-framework-rs", branch = "node/1.40" } diff --git a/integration-tests/chain-signatures/tests/actions/mod.rs b/integration-tests/chain-signatures/tests/actions/mod.rs index e0bc46814..f7c243e8e 100644 --- a/integration-tests/chain-signatures/tests/actions/mod.rs +++ b/integration-tests/chain-signatures/tests/actions/mod.rs @@ -19,9 +19,8 @@ use mpc_contract::primitives::SignatureRequest; use mpc_contract::RunningContractState; use mpc_node::kdf::into_eth_sig; use near_crypto::InMemorySigner; -use near_jsonrpc_client::methods::broadcast_tx_async::RpcBroadcastTxAsyncRequest; -use near_lake_primitives::CryptoHash; -use near_primitives::transaction::{Action, FunctionCallAction, Transaction}; +use near_fetch::ops::AsyncTransactionStatus; +use near_workspaces::types::NearToken; use near_workspaces::Account; use rand::Rng; use secp256k1::XOnlyPublicKey; @@ -40,7 +39,7 @@ use serde_json::json; pub async fn request_sign( ctx: &MultichainTestContext<'_>, -) -> anyhow::Result<([u8; 32], [u8; 32], Account, CryptoHash)> { +) -> anyhow::Result<([u8; 32], [u8; 32], Account, AsyncTransactionStatus)> { let worker = &ctx.nodes.ctx().worker; let account = worker.dev_create_account().await?; let payload: [u8; 32] = rand::thread_rng().gen(); @@ -51,39 +50,24 @@ pub async fn request_sign( public_key: account.secret_key().public_key().to_string().parse()?, secret_key: account.secret_key().to_string().parse()?, }; - let (nonce, block_hash, _) = ctx - .rpc_client - .fetch_nonce(&signer.account_id, &signer.public_key) - .await?; let request = SignRequest { payload: payload_hashed, path: "test".to_string(), key_version: 0, }; - 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.clone(), - receiver_id: ctx.nodes.ctx().mpc_contract.id().clone(), - actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { - method_name: "sign".to_string(), - args: serde_json::to_vec(&serde_json::json!({ - "request": request, - }))?, - gas: 300_000_000_000_000, - deposit: 1, - }))], - } - .sign(&signer), - }) + let status = ctx + .rpc_client + .call(&signer, ctx.nodes.ctx().mpc_contract.id(), "sign") + .args_json(serde_json::json!({ + "request": request, + })) + .max_gas() + .deposit(NearToken::from_yoctonear(1)) + .transact_async() .await?; tokio::time::sleep(Duration::from_secs(1)).await; - Ok((payload, payload_hashed, account, tx_hash)) + Ok((payload, payload_hashed, account, status)) } pub async fn assert_signature( @@ -104,16 +88,15 @@ pub async fn single_signature_rogue_responder( ctx: &MultichainTestContext<'_>, state: &RunningContractState, ) -> anyhow::Result<()> { - let (_, payload_hash, account, tx_hash) = request_sign(ctx).await?; + let (_, payload_hash, account, status) = request_sign(ctx).await?; // We have to use seperate transactions because one could fail. // This leads to a potential race condition where this transaction could get sent after the signature completes, but I think that's unlikely - let rogue_hash = rogue_respond(ctx, payload_hash, account.id(), "test").await?; - - let err = wait_for::rogue_message_responded(ctx, rogue_hash).await?; + let rogue_status = rogue_respond(ctx, payload_hash, account.id(), "test").await?; + let err = wait_for::rogue_message_responded(rogue_status).await?; assert!(err.contains(&errors::RespondError::InvalidSignature.to_string())); - let signature = wait_for::signature_responded(ctx, tx_hash).await?; + let signature = wait_for::signature_responded(status).await?; let mut mpc_pk_bytes = vec![0x04]; mpc_pk_bytes.extend_from_slice(&state.public_key.as_bytes()[1..]); @@ -135,8 +118,8 @@ pub async fn single_signature_production( ctx: &MultichainTestContext<'_>, state: &RunningContractState, ) -> anyhow::Result<()> { - let (_, payload_hash, account, tx_hash) = request_sign(ctx).await?; - let signature = wait_for::signature_responded(ctx, tx_hash).await?; + let (_, payload_hash, account, status) = request_sign(ctx).await?; + let signature = wait_for::signature_responded(status).await?; let mut mpc_pk_bytes = vec![0x04]; mpc_pk_bytes.extend_from_slice(&state.public_key.as_bytes()[1..]); @@ -150,7 +133,7 @@ pub async fn rogue_respond( payload_hash: [u8; 32], predecessor: &near_workspaces::AccountId, path: &str, -) -> anyhow::Result { +) -> anyhow::Result { let worker = &ctx.nodes.ctx().worker; let account = worker.dev_create_account().await?; @@ -159,10 +142,6 @@ pub async fn rogue_respond( public_key: account.secret_key().public_key().clone().into(), secret_key: account.secret_key().to_string().parse()?, }; - let (nonce, block_hash, _) = ctx - .rpc_client - .fetch_nonce(&signer.account_id, &signer.public_key) - .await?; let epsilon = derive_epsilon(predecessor, path); let request = SignatureRequest { @@ -185,32 +164,19 @@ pub async fn rogue_respond( recovery_id: 0, }; - let json = &serde_json::json!({ - "request": request, - "response": response, - }); - let hash = ctx - .jsonrpc_client - .call(&RpcBroadcastTxAsyncRequest { - signed_transaction: Transaction { - nonce, - block_hash, - signer_id: signer.account_id.clone(), - public_key: signer.public_key.clone(), - receiver_id: ctx.nodes.ctx().mpc_contract.id().clone(), - actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { - method_name: "respond".to_string(), - args: serde_json::to_vec(json)?, - gas: 300_000_000_000_000, - deposit: 0, - }))], - } - .sign(&signer), - }) + let status = ctx + .rpc_client + .call(&signer, ctx.nodes.ctx().mpc_contract.id(), "respond") + .args_json(serde_json::json!({ + "request": request, + "response": response, + })) + .max_gas() + .transact_async() .await?; tokio::time::sleep(Duration::from_secs(1)).await; - Ok(hash) + Ok(status) } pub async fn request_sign_non_random( @@ -218,7 +184,7 @@ pub async fn request_sign_non_random( account: Account, payload: [u8; 32], payload_hashed: [u8; 32], -) -> Result<([u8; 32], [u8; 32], Account, CryptoHash), WaitForError> { +) -> Result<([u8; 32], [u8; 32], Account, AsyncTransactionStatus), WaitForError> { let signer = InMemorySigner { account_id: account.id().clone(), public_key: account @@ -233,11 +199,6 @@ pub async fn request_sign_non_random( .parse() .map_err(|_| WaitForError::Parsing)?, }; - let (nonce, block_hash, _) = ctx - .rpc_client - .fetch_nonce(&signer.account_id, &signer.public_key) - .await - .map_err(|error| WaitForError::Fetch(format!("{error:?}")))?; let request = SignRequest { payload: payload_hashed, @@ -245,39 +206,27 @@ pub async fn request_sign_non_random( key_version: 0, }; - 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.clone(), - receiver_id: ctx.nodes.ctx().mpc_contract.id().clone(), - actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { - method_name: "sign".to_string(), - args: serde_json::to_vec(&serde_json::json!({ - "request": request, - })) - .map_err(|error| WaitForError::SerdeJson(format!("{error:?}")))?, - gas: 300_000_000_000_000, - deposit: 1, - }))], - } - .sign(&signer), - }) + let status = ctx + .rpc_client + .call(&signer, ctx.nodes.ctx().mpc_contract.id(), "sign") + .args_json(serde_json::json!({ + "request": request, + })) + .max_gas() + .deposit(NearToken::from_yoctonear(1)) + .transact_async() .await .map_err(|error| WaitForError::JsonRpc(format!("{error:?}")))?; tokio::time::sleep(Duration::from_secs(1)).await; - Ok((payload, payload_hashed, account, tx_hash)) + Ok((payload, payload_hashed, account, status)) } pub async fn single_payload_signature_production( ctx: &MultichainTestContext<'_>, state: &RunningContractState, ) -> anyhow::Result<()> { - let (payload, payload_hash, account, tx_hash) = request_sign(ctx).await?; - let first_tx_result = wait_for::signature_responded(ctx, tx_hash).await; + let (payload, payload_hash, account, status) = request_sign(ctx).await?; + let first_tx_result = wait_for::signature_responded(status).await; let signature = match first_tx_result { Ok(sig) => sig, Err(error) => { diff --git a/integration-tests/chain-signatures/tests/actions/wait_for.rs b/integration-tests/chain-signatures/tests/actions/wait_for.rs index a879a3487..7835886ed 100644 --- a/integration-tests/chain-signatures/tests/actions/wait_for.rs +++ b/integration-tests/chain-signatures/tests/actions/wait_for.rs @@ -1,3 +1,4 @@ +use std::task::Poll; use std::time::Duration; use crate::actions; @@ -12,9 +13,7 @@ use k256::Secp256k1; use mpc_contract::ProtocolContractState; use mpc_contract::RunningContractState; use mpc_node::web::StateView; -use near_jsonrpc_client::methods::tx::RpcTransactionStatusRequest; -use near_jsonrpc_client::methods::tx::TransactionInfo; -use near_lake_primitives::CryptoHash; +use near_fetch::ops::AsyncTransactionStatus; use near_primitives::errors::ActionErrorKind; use near_primitives::views::FinalExecutionStatus; use near_workspaces::Account; @@ -250,8 +249,6 @@ pub enum WaitForError { SerdeJson(String), #[error("Parsing error")] Parsing, - #[error("Near fetch: {0}")] - Fetch(String), } /// Used locally for testing to circumvent retrying on all errors. This will avoid retrying @@ -262,39 +259,24 @@ enum Outcome { } pub async fn signature_responded( - ctx: &MultichainTestContext<'_>, - tx_hash: CryptoHash, + status: AsyncTransactionStatus, ) -> Result, WaitForError> { let is_tx_ready = || async { - let outcome_view = match ctx - .jsonrpc_client - .call(RpcTransactionStatusRequest { - transaction_info: TransactionInfo::TransactionId { - tx_hash, - sender_account_id: ctx.nodes.ctx().mpc_contract.id().clone(), - }, - wait_until: near_primitives::views::TxExecutionStatus::Final, - }) + let Poll::Ready(outcome) = status + .status() .await - { - Err(error) => return Err(WaitForError::JsonRpc(format!("{error:?}"))), - Ok(outcome_view) => outcome_view, - }; - - let Some(outcome) = outcome_view.final_execution_outcome else { + .map_err(|err| WaitForError::JsonRpc(format!("{err:?}")))? + else { return Err(WaitForError::Signature(SignatureError::NotYetAvailable)); }; - let outcome = outcome.into_outcome(); - - let FinalExecutionStatus::SuccessValue(payload) = outcome.status else { - return Ok(Outcome::Failed(format!("{:?}", outcome.status))); - }; + if outcome.is_failure() { + return Ok(Outcome::Failed(format!("{:?}", outcome.status()))); + } - let result: SignatureResponse = match serde_json::from_slice(&payload) { - Err(error) => return Err(WaitForError::SerdeJson(format!("{error:?}"))), - Ok(response) => response, - }; + let result: SignatureResponse = outcome + .json() + .map_err(|err| WaitForError::SerdeJson(format!("{err:?}")))?; Ok(Outcome::Signature(cait_sith::FullSignature:: { big_r: result.big_r.affine_point, s: result.s.scalar, @@ -318,9 +300,9 @@ pub async fn signature_payload_responded( payload_hashed: [u8; 32], ) -> Result, WaitForError> { let is_signature_ready = || async { - let (_, _, _, tx_hash) = + let (_, _, _, status) = actions::request_sign_non_random(ctx, account.clone(), payload, payload_hashed).await?; - let result = signature_responded(ctx, tx_hash).await; + let result = signature_responded(status).await; if let Err(err) = &result { println!("failed to produce signature: {err:?}"); } @@ -332,48 +314,53 @@ pub async fn signature_payload_responded( } // Check that the rogue message failed -pub async fn rogue_message_responded( - ctx: &MultichainTestContext<'_>, - tx_hash: CryptoHash, -) -> anyhow::Result { +pub async fn rogue_message_responded(status: AsyncTransactionStatus) -> anyhow::Result { let is_tx_ready = || async { - let outcome_view = ctx - .jsonrpc_client - .call(RpcTransactionStatusRequest { - transaction_info: TransactionInfo::TransactionId { - tx_hash, - sender_account_id: ctx.nodes.ctx().mpc_contract.id().clone(), - }, - wait_until: near_primitives::views::TxExecutionStatus::Final, - }) - .await?; - - let Some(outcome) = outcome_view.final_execution_outcome else { - anyhow::bail!("final execution outcome not available"); + let Poll::Ready(outcome) = status + .status() + .await + .map_err(|err| WaitForError::JsonRpc(format!("{err:?}")))? + else { + return Err(WaitForError::Signature(SignatureError::NotYetAvailable)); }; - let outcome = outcome.into_outcome(); - let FinalExecutionStatus::Failure(ref failure) = outcome.status else { - anyhow::bail!("tx finished successfully: {:?}", outcome.status); + let FinalExecutionStatus::Failure(failure) = outcome.status() else { + return Err(WaitForError::JsonRpc(format!( + "rogue: unexpected success {:?}", + outcome.status() + ))); }; use near_primitives::errors::TxExecutionError; let TxExecutionError::ActionError(action_err) = failure else { - anyhow::bail!("invalid transaction: {:?}", outcome.status); + return Err(WaitForError::JsonRpc(format!( + "rogue: invalid transaction {:?}", + outcome.status(), + ))); }; let ActionErrorKind::FunctionCallError(ref err) = action_err.kind else { - anyhow::bail!("Not a function call error {:?}", outcome.status); + return Err(WaitForError::JsonRpc(format!( + "rogue: not a function call error {:?}", + outcome.status(), + ))); }; use near_primitives::errors::FunctionCallError; let FunctionCallError::ExecutionError(err_msg) = err else { - anyhow::bail!("Wrong error type: {:?}", err); + return Err(WaitForError::JsonRpc(format!( + "rogue: wrong execution error {:?}", + outcome.status(), + ))); }; Ok(err_msg.clone()) }; + let strategy = ConstantBuilder::default() + .with_delay(Duration::from_secs(20)) + .with_max_times(5); + let signature = is_tx_ready - .retry(&ExponentialBuilder::default().with_max_times(6)) + .retry(&strategy) .await .with_context(|| "failed to wait for rogue message response")?; diff --git a/integration-tests/chain-signatures/tests/cases/mod.rs b/integration-tests/chain-signatures/tests/cases/mod.rs index fa1fbae6c..5e84bc038 100644 --- a/integration-tests/chain-signatures/tests/cases/mod.rs +++ b/integration-tests/chain-signatures/tests/cases/mod.rs @@ -149,8 +149,8 @@ async fn test_key_derivation() -> anyhow::Result<()> { for _ in 0..3 { let mpc_pk: k256::AffinePoint = state_0.public_key.clone().into_affine_point(); - let (_, payload_hashed, account, tx_hash) = actions::request_sign(&ctx).await?; - let sig = wait_for::signature_responded(&ctx, tx_hash).await?; + let (_, payload_hashed, account, status) = actions::request_sign(&ctx).await?; + let sig = wait_for::signature_responded(status).await?; let hd_path = "test"; let derivation_epsilon = derive_epsilon(account.id(), hd_path); diff --git a/integration-tests/chain-signatures/tests/lib.rs b/integration-tests/chain-signatures/tests/lib.rs index f37ccaaf9..ef3c3643f 100644 --- a/integration-tests/chain-signatures/tests/lib.rs +++ b/integration-tests/chain-signatures/tests/lib.rs @@ -10,7 +10,6 @@ use integration_tests_chain_signatures::containers::DockerClient; use integration_tests_chain_signatures::utils::{vote_join, vote_leave}; use integration_tests_chain_signatures::{run, utils, MultichainConfig, Nodes}; -use near_jsonrpc_client::JsonRpcClient; use near_workspaces::types::{NearToken, SecretKey}; use near_workspaces::{Account, AccountId}; @@ -24,7 +23,6 @@ const CURRENT_CONTRACT_FILE_PATH: &str = pub struct MultichainTestContext<'a> { nodes: Nodes<'a>, rpc_client: near_fetch::Client, - jsonrpc_client: JsonRpcClient, http_client: reqwest::Client, cfg: MultichainConfig, } @@ -209,13 +207,12 @@ where let sk_local_path = nodes.ctx().storage_options.sk_share_local_path.clone(); - let connector = JsonRpcClient::new_client(); + let connector = near_jsonrpc_client::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()); + let rpc_client = near_fetch::Client::from_client(jsonrpc_client); let result = f(MultichainTestContext { nodes, rpc_client, - jsonrpc_client, http_client: reqwest::Client::default(), cfg, })