From f464a52a923e0d396d72eee84f552ebfa59a9c2d Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Wed, 31 Jul 2024 18:08:17 +0800 Subject: [PATCH 1/8] refund after sign --- chain-signatures/contract/src/lib.rs | 43 +++++++-- chain-signatures/contract/tests/sign.rs | 120 ++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 8 deletions(-) diff --git a/chain-signatures/contract/src/lib.rs b/chain-signatures/contract/src/lib.rs index 01315435d..6e2e48824 100644 --- a/chain-signatures/contract/src/lib.rs +++ b/chain-signatures/contract/src/lib.rs @@ -133,11 +133,11 @@ impl VersionedMpcContract { )); } // Check deposit - let deposit = env::attached_deposit(); + let deposit = env::attached_deposit().as_yoctonear(); let required_deposit = self.signature_deposit(); - if deposit.as_yoctonear() < required_deposit { + if deposit < required_deposit { return Err(MpcContractError::SignError(SignError::InsufficientDeposit( - deposit.as_yoctonear(), + deposit, required_deposit, ))); } @@ -158,12 +158,22 @@ impl VersionedMpcContract { } let predecessor = env::predecessor_account_id(); let request = SignatureRequest::new(payload, &predecessor, &path); + log!("request: {request:?}"); if !self.request_already_exists(&request) { log!( "sign: predecessor={predecessor}, payload={payload:?}, path={path:?}, key_version={key_version}", ); env::log_str(&serde_json::to_string(&near_sdk::env::random_seed_array()).unwrap()); - Ok(Self::ext(env::current_account_id()).sign_helper(request)) + if deposit > required_deposit { + let refund = deposit - required_deposit; + log!("refund more than required deposit {refund} to {predecessor}"); + Promise::new(predecessor.clone()).transfer(NearToken::from_yoctonear(refund)); + } + Ok(Self::ext(env::current_account_id()).sign_helper( + request, + predecessor, + required_deposit, + )) } else { Err(MpcContractError::SignError(SignError::PayloadCollision)) } @@ -684,12 +694,16 @@ impl VersionedMpcContract { } #[private] - pub fn sign_helper(&mut self, request: SignatureRequest) { + pub fn sign_helper(&mut self, request: SignatureRequest, requester: AccountId, deposit: u128) { match self { Self::V0(mpc_contract) => { + // refund must happen in clear_state_on_finish, because regardless of this success or fail + // the promise created by clear_state_on_finish is executed, because of callback_unwrap and + // promise_then. but if return_signature_on_finish fail (returns error), the promise created + // by it won't execute. let yield_promise = env::promise_yield_create( "clear_state_on_finish", - &serde_json::to_vec(&(&request,)).unwrap(), + &serde_json::to_vec(&(&request, &requester, deposit)).unwrap(), CLEAR_STATE_ON_FINISH_CALL_GAS, GasWeight(0), DATA_ID_REGISTER, @@ -739,15 +753,28 @@ impl VersionedMpcContract { pub fn clear_state_on_finish( &mut self, request: SignatureRequest, + refund_to_on_fail: AccountId, + refund_amount_on_fail: u128, #[callback_result] signature: Result, ) -> Result, MpcContractError> { match self { Self::V0(mpc_contract) => { // Clean up the local state - mpc_contract.remove_request(request)?; + let result = mpc_contract.remove_request(request); + if result.is_err() { + log!("refund {refund_amount_on_fail} to {refund_to_on_fail} due to fail"); + Promise::new(refund_to_on_fail.clone()) + .transfer(NearToken::from_yoctonear(refund_amount_on_fail)); + result?; + } match signature { Ok(signature) => Ok(SignatureResult::Ok(signature)), - Err(_) => Ok(SignatureResult::Err(SignaturePromiseError::Failed)), + Err(_) => { + log!("refund {refund_amount_on_fail} to {refund_to_on_fail} due to fail"); + Promise::new(refund_to_on_fail) + .transfer(NearToken::from_yoctonear(refund_amount_on_fail)); + Ok(SignatureResult::Err(SignaturePromiseError::Failed)) + } } } } diff --git a/chain-signatures/contract/tests/sign.rs b/chain-signatures/contract/tests/sign.rs index 22271336d..74a069d00 100644 --- a/chain-signatures/contract/tests/sign.rs +++ b/chain-signatures/contract/tests/sign.rs @@ -5,6 +5,8 @@ use mpc_contract::errors; use mpc_contract::primitives::{CandidateInfo, SignRequest}; use near_workspaces::types::AccountId; +use crypto_shared::SignatureResponse; +use near_sdk::NearToken; use std::collections::HashMap; #[tokio::test] @@ -57,6 +59,124 @@ async fn test_contract_sign_request() -> anyhow::Result<()> { Ok(()) } +#[tokio::test] +async fn test_contract_sign_success_refund() -> anyhow::Result<()> { + let (worker, contract, _, sk) = init_env().await; + let alice = worker.dev_create_account().await?; + let balance = alice.view_account().await?.balance; + + let path = "test"; + + let msg = "hello world!"; + println!("submitting: {msg}"); + let (payload_hash, respond_req, respond_resp) = + create_response(alice.id(), msg, path, &sk).await; + let request = SignRequest { + payload: payload_hash, + path: path.into(), + key_version: 0, + }; + + let status = alice + .call(contract.id(), "sign") + .args_json(serde_json::json!({ + "request": request, + })) + .deposit(NearToken::from_near(1)) + .max_gas() + .transact_async() + .await?; + dbg!(&status); + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + + // Call `respond` as if we are the MPC network itself. + let respond = contract + .call("respond") + .args_json(serde_json::json!({ + "request": respond_req, + "response": respond_resp + })) + .max_gas() + .transact() + .await?; + dbg!(&respond); + + let execution = status.await?; + dbg!(&execution); + + let execution = execution.into_result()?; + + // Finally wait the result: + let returned_resp: SignatureResponse = execution.json()?; + assert_eq!( + returned_resp, respond_resp, + "Returned signature request does not match" + ); + + let new_balance = alice.view_account().await?.balance; + assert!( + balance.as_millinear() - new_balance.as_millinear() < 10, + "refund should happen" + ); + + Ok(()) +} + +#[tokio::test] +async fn test_contract_sign_fail_refund() -> anyhow::Result<()> { + let (worker, contract, _, sk) = init_env().await; + let alice = worker.dev_create_account().await?; + let balance = alice.view_account().await?.balance; + + let path = "test"; + + let msg = "hello world!"; + println!("submitting: {msg}"); + let (payload_hash, _, _) = create_response(alice.id(), msg, path, &sk).await; + let request = SignRequest { + payload: payload_hash, + path: path.into(), + key_version: 0, + }; + + let status = alice + .call(contract.id(), "sign") + .args_json(serde_json::json!({ + "request": request, + })) + .deposit(NearToken::from_near(1)) + .max_gas() + .transact_async() + .await?; + dbg!(&status); + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + + // we do not respond, sign will fail due to timeout + let execution = status.await; + dbg!(&execution); + let err = execution + .unwrap() + .into_result() + .expect_err("should have failed with timeout"); + assert!(err + .to_string() + .contains(&errors::MpcContractError::SignError(errors::SignError::Timeout).to_string())); + + let new_balance = alice.view_account().await?.balance; + println!( + "{} {} {}", + balance.as_millinear(), + new_balance.as_millinear(), + contract.view_account().await?.balance + ); + assert!( + balance.as_millinear() - new_balance.as_millinear() < 10, + "refund should happen" + ); + + Ok(()) +} + #[tokio::test] async fn test_contract_sign_request_deposits() -> anyhow::Result<()> { let (_, contract, _, sk) = init_env().await; From d82635ee0dc96aa85c1e6abc6c5f38e4d47193ea Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Wed, 31 Jul 2024 18:37:57 +0800 Subject: [PATCH 2/8] fmt --- chain-signatures/contract/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chain-signatures/contract/src/lib.rs b/chain-signatures/contract/src/lib.rs index c0f568a36..57ca0fea5 100644 --- a/chain-signatures/contract/src/lib.rs +++ b/chain-signatures/contract/src/lib.rs @@ -139,8 +139,7 @@ impl VersionedMpcContract { if deposit < required_deposit { return Err(InvalidParameters::InsufficientDeposit.message(format!( "Attached {}, Required {}", - deposit, - required_deposit, + deposit, required_deposit, ))); } // Make sure sign call will not run out of gas doing recursive calls because the payload will never be removed From 64c5ea5162741ba2cd86e3800ca7ab9d2aa66cdf Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Thu, 1 Aug 2024 11:49:28 +0800 Subject: [PATCH 3/8] address comment --- chain-signatures/contract/src/lib.rs | 67 ++++++++++++--------- chain-signatures/contract/src/primitives.rs | 16 ++++- chain-signatures/contract/tests/common.rs | 8 ++- 3 files changed, 61 insertions(+), 30 deletions(-) diff --git a/chain-signatures/contract/src/lib.rs b/chain-signatures/contract/src/lib.rs index 57ca0fea5..90e7f8e71 100644 --- a/chain-signatures/contract/src/lib.rs +++ b/chain-signatures/contract/src/lib.rs @@ -134,12 +134,13 @@ impl VersionedMpcContract { return Err(SignError::UnsupportedKeyVersion.into()); } // Check deposit - let deposit = env::attached_deposit().as_yoctonear(); + let deposit = env::attached_deposit(); let required_deposit = self.signature_deposit(); - if deposit < required_deposit { + if deposit.as_yoctonear() < required_deposit { return Err(InvalidParameters::InsufficientDeposit.message(format!( "Attached {}, Required {}", - deposit, required_deposit, + deposit.as_yoctonear(), + required_deposit, ))); } // Make sure sign call will not run out of gas doing recursive calls because the payload will never be removed @@ -159,23 +160,19 @@ impl VersionedMpcContract { } } let predecessor = env::predecessor_account_id(); - let request = SignatureRequest::new(payload, &predecessor, &path); - log!("request: {request:?}"); + let request = SignatureRequest::new( + payload, + &predecessor, + &path, + deposit, + NearToken::from_yoctonear(required_deposit), + ); if !self.request_already_exists(&request) { log!( "sign: predecessor={predecessor}, payload={payload:?}, path={path:?}, key_version={key_version}", ); env::log_str(&serde_json::to_string(&near_sdk::env::random_seed_array()).unwrap()); - if deposit > required_deposit { - let refund = deposit - required_deposit; - log!("refund more than required deposit {refund} to {predecessor}"); - Promise::new(predecessor.clone()).transfer(NearToken::from_yoctonear(refund)); - } - Ok(Self::ext(env::current_account_id()).sign_helper( - request, - predecessor, - required_deposit, - )) + Ok(Self::ext(env::current_account_id()).sign_helper(request)) } else { Err(SignError::PayloadCollision.into()) } @@ -669,7 +666,7 @@ impl VersionedMpcContract { } #[private] - pub fn sign_helper(&mut self, request: SignatureRequest, requester: AccountId, deposit: u128) { + pub fn sign_helper(&mut self, request: SignatureRequest) { match self { Self::V0(mpc_contract) => { // refund must happen in clear_state_on_finish, because regardless of this success or fail @@ -678,7 +675,7 @@ impl VersionedMpcContract { // by it won't execute. let yield_promise = env::promise_yield_create( "clear_state_on_finish", - &serde_json::to_vec(&(&request, &requester, deposit)).unwrap(), + &serde_json::to_vec(&(&request)).unwrap(), CLEAR_STATE_ON_FINISH_CALL_GAS, GasWeight(0), DATA_ID_REGISTER, @@ -726,31 +723,47 @@ impl VersionedMpcContract { } } + fn refund_on_fail(request: &SignatureRequest) { + let amount = request.deposit; + let to = request.requester.clone(); + log!("refund {amount} to {to} due to fail"); + Promise::new(to).transfer(amount); + } + + fn refund_on_success(request: &SignatureRequest) { + let deposit = request.deposit; + let required = request.required_deposit; + if let Some(diff) = deposit.checked_sub(required) { + if diff > NearToken::from_yoctonear(0) { + let to = request.requester.clone(); + log!("refund more than required deposit {diff} to {to}"); + Promise::new(to).transfer(diff); + } + } + } + #[private] #[handle_result] pub fn clear_state_on_finish( &mut self, request: SignatureRequest, - refund_to_on_fail: AccountId, - refund_amount_on_fail: u128, #[callback_result] signature: Result, ) -> Result, Error> { match self { Self::V0(mpc_contract) => { // Clean up the local state - let result = mpc_contract.remove_request(request); + let result = mpc_contract.remove_request(request.clone()); if result.is_err() { - log!("refund {refund_amount_on_fail} to {refund_to_on_fail} due to fail"); - Promise::new(refund_to_on_fail.clone()) - .transfer(NearToken::from_yoctonear(refund_amount_on_fail)); + Self::refund_on_fail(&request); result?; } match signature { - Ok(signature) => Ok(SignatureResult::Ok(signature)), + Ok(signature) => { + Self::refund_on_success(&request); + Ok(SignatureResult::Ok(signature)) + } Err(_) => { - log!("refund {refund_amount_on_fail} to {refund_to_on_fail} due to fail"); - Promise::new(refund_to_on_fail) - .transfer(NearToken::from_yoctonear(refund_amount_on_fail)); + Self::refund_on_fail(&request); Ok(SignatureResult::Err(SignaturePromiseError::Failed)) } } diff --git a/chain-signatures/contract/src/primitives.rs b/chain-signatures/contract/src/primitives.rs index e463208b6..9c8a2bc6e 100644 --- a/chain-signatures/contract/src/primitives.rs +++ b/chain-signatures/contract/src/primitives.rs @@ -2,7 +2,7 @@ use crypto_shared::{derive_epsilon, SerializableScalar}; use k256::Scalar; use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; use near_sdk::serde::{Deserialize, Serialize}; -use near_sdk::{AccountId, BorshStorageKey, CryptoHash, PublicKey}; +use near_sdk::{AccountId, BorshStorageKey, CryptoHash, NearToken, PublicKey}; use std::collections::{BTreeMap, HashMap, HashSet}; pub mod hpke { @@ -29,10 +29,19 @@ pub struct YieldIndex { pub struct SignatureRequest { pub epsilon: SerializableScalar, pub payload_hash: SerializableScalar, + pub requester: AccountId, + pub deposit: NearToken, + pub required_deposit: NearToken, } impl SignatureRequest { - pub fn new(payload_hash: Scalar, predecessor_id: &AccountId, path: &str) -> Self { + pub fn new( + payload_hash: Scalar, + predecessor_id: &AccountId, + path: &str, + deposit: NearToken, + required_deposit: NearToken, + ) -> Self { let epsilon = derive_epsilon(predecessor_id, path); let epsilon = SerializableScalar { scalar: epsilon }; let payload_hash = SerializableScalar { @@ -41,6 +50,9 @@ impl SignatureRequest { SignatureRequest { epsilon, payload_hash, + requester: predecessor_id.clone(), + deposit, + required_deposit, } } } diff --git a/chain-signatures/contract/tests/common.rs b/chain-signatures/contract/tests/common.rs index 6ff31b0a6..1dd214cf7 100644 --- a/chain-signatures/contract/tests/common.rs +++ b/chain-signatures/contract/tests/common.rs @@ -176,7 +176,13 @@ pub async fn create_response( let s = signature.s(); let (r_bytes, _s_bytes) = signature.split_bytes(); let payload_hash_s = Scalar::from_bytes(payload_hash).unwrap(); - let respond_req = SignatureRequest::new(payload_hash_s, predecessor_id, path); + let respond_req = SignatureRequest::new( + payload_hash_s, + predecessor_id, + path, + NearToken::from_near(0), + NearToken::from_near(0), + ); let big_r = AffinePoint::decompress(&r_bytes, k256::elliptic_curve::subtle::Choice::from(0)).unwrap(); let s: k256::Scalar = *s.as_ref(); From 4d0ca807d3d51496283c02360040f5188a4debd3 Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Thu, 1 Aug 2024 17:24:43 +0800 Subject: [PATCH 4/8] Failed to deserialize input from JSON --- chain-signatures/contract/src/lib.rs | 32 ++++++++++++--------- chain-signatures/contract/src/primitives.rs | 11 +++---- chain-signatures/contract/tests/common.rs | 2 -- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/chain-signatures/contract/src/lib.rs b/chain-signatures/contract/src/lib.rs index 1ff247a63..df4b5112c 100644 --- a/chain-signatures/contract/src/lib.rs +++ b/chain-signatures/contract/src/lib.rs @@ -22,7 +22,7 @@ use near_sdk::{ }; use primitives::{ CandidateInfo, Candidates, Participants, PkVotes, SignRequest, SignaturePromiseError, - SignatureRequest, SignatureResult, StorageKey, Votes, YieldIndex, + SignatureRequest, SignatureResult, StorageKey, Votes, YieldIndex, ContractSignatureRequest }; use std::collections::{BTreeMap, HashSet}; @@ -164,15 +164,19 @@ impl VersionedMpcContract { payload, &predecessor, &path, - deposit, - NearToken::from_yoctonear(required_deposit), ); if !self.request_already_exists(&request) { log!( "sign: predecessor={predecessor}, payload={payload:?}, path={path:?}, key_version={key_version}", ); env::log_str(&serde_json::to_string(&near_sdk::env::random_seed_array()).unwrap()); - Ok(Self::ext(env::current_account_id()).sign_helper(request)) + let contract_signature_request = ContractSignatureRequest { + request, + requester: predecessor, + deposit, + required_deposit: NearToken::from_yoctonear(required_deposit), + }; + Ok(Self::ext(env::current_account_id()).sign_helper(contract_signature_request)) } else { Err(SignError::PayloadCollision.into()) } @@ -683,7 +687,7 @@ impl VersionedMpcContract { } #[private] - pub fn sign_helper(&mut self, request: SignatureRequest) { + pub fn sign_helper(&mut self, contract_signature_request: ContractSignatureRequest) { match self { Self::V0(mpc_contract) => { // refund must happen in clear_state_on_finish, because regardless of this success or fail @@ -692,7 +696,7 @@ impl VersionedMpcContract { // by it won't execute. let yield_promise = env::promise_yield_create( "clear_state_on_finish", - &serde_json::to_vec(&(&request)).unwrap(), + &serde_json::to_vec(&(&contract_signature_request)).unwrap(), CLEAR_STATE_ON_FINISH_CALL_GAS, GasWeight(0), DATA_ID_REGISTER, @@ -704,7 +708,7 @@ impl VersionedMpcContract { .try_into() .expect("conversion to CryptoHash failed"); - mpc_contract.add_request(&request, data_id); + mpc_contract.add_request(&contract_signature_request.request, data_id); // NOTE: there's another promise after the clear_state_on_finish to avoid any errors // that would rollback the state. @@ -740,14 +744,14 @@ impl VersionedMpcContract { } } - fn refund_on_fail(request: &SignatureRequest) { + fn refund_on_fail(request: &ContractSignatureRequest) { let amount = request.deposit; let to = request.requester.clone(); log!("refund {amount} to {to} due to fail"); Promise::new(to).transfer(amount); } - fn refund_on_success(request: &SignatureRequest) { + fn refund_on_success(request: &ContractSignatureRequest) { let deposit = request.deposit; let required = request.required_deposit; if let Some(diff) = deposit.checked_sub(required) { @@ -763,24 +767,24 @@ impl VersionedMpcContract { #[handle_result] pub fn clear_state_on_finish( &mut self, - request: SignatureRequest, + contract_signature_request: ContractSignatureRequest, #[callback_result] signature: Result, ) -> Result, Error> { match self { Self::V0(mpc_contract) => { // Clean up the local state - let result = mpc_contract.remove_request(request.clone()); + let result = mpc_contract.remove_request(contract_signature_request.request.clone()); if result.is_err() { - Self::refund_on_fail(&request); + Self::refund_on_fail(&contract_signature_request); result?; } match signature { Ok(signature) => { - Self::refund_on_success(&request); + Self::refund_on_success(&contract_signature_request); Ok(SignatureResult::Ok(signature)) } Err(_) => { - Self::refund_on_fail(&request); + Self::refund_on_fail(&contract_signature_request); Ok(SignatureResult::Err(SignaturePromiseError::Failed)) } } diff --git a/chain-signatures/contract/src/primitives.rs b/chain-signatures/contract/src/primitives.rs index 9c8a2bc6e..e972a0544 100644 --- a/chain-signatures/contract/src/primitives.rs +++ b/chain-signatures/contract/src/primitives.rs @@ -29,6 +29,12 @@ pub struct YieldIndex { pub struct SignatureRequest { pub epsilon: SerializableScalar, pub payload_hash: SerializableScalar, +} + +#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug, Clone)] +#[borsh(crate = "near_sdk::borsh")] +pub struct ContractSignatureRequest { + pub request: SignatureRequest, pub requester: AccountId, pub deposit: NearToken, pub required_deposit: NearToken, @@ -39,8 +45,6 @@ impl SignatureRequest { payload_hash: Scalar, predecessor_id: &AccountId, path: &str, - deposit: NearToken, - required_deposit: NearToken, ) -> Self { let epsilon = derive_epsilon(predecessor_id, path); let epsilon = SerializableScalar { scalar: epsilon }; @@ -50,9 +54,6 @@ impl SignatureRequest { SignatureRequest { epsilon, payload_hash, - requester: predecessor_id.clone(), - deposit, - required_deposit, } } } diff --git a/chain-signatures/contract/tests/common.rs b/chain-signatures/contract/tests/common.rs index 1dd214cf7..1f75fe129 100644 --- a/chain-signatures/contract/tests/common.rs +++ b/chain-signatures/contract/tests/common.rs @@ -180,8 +180,6 @@ pub async fn create_response( payload_hash_s, predecessor_id, path, - NearToken::from_near(0), - NearToken::from_near(0), ); let big_r = AffinePoint::decompress(&r_bytes, k256::elliptic_curve::subtle::Choice::from(0)).unwrap(); From 35953cb46b43204f1ba85d9a44cfe7bb3e6c5bfb Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Thu, 1 Aug 2024 17:34:08 +0800 Subject: [PATCH 5/8] fixed --- chain-signatures/contract/src/lib.rs | 15 ++++++--------- chain-signatures/contract/src/primitives.rs | 6 +----- chain-signatures/contract/tests/common.rs | 6 +----- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/chain-signatures/contract/src/lib.rs b/chain-signatures/contract/src/lib.rs index df4b5112c..d8fe3d147 100644 --- a/chain-signatures/contract/src/lib.rs +++ b/chain-signatures/contract/src/lib.rs @@ -21,8 +21,8 @@ use near_sdk::{ PromiseError, PublicKey, }; use primitives::{ - CandidateInfo, Candidates, Participants, PkVotes, SignRequest, SignaturePromiseError, - SignatureRequest, SignatureResult, StorageKey, Votes, YieldIndex, ContractSignatureRequest + CandidateInfo, Candidates, ContractSignatureRequest, Participants, PkVotes, SignRequest, + SignaturePromiseError, SignatureRequest, SignatureResult, StorageKey, Votes, YieldIndex, }; use std::collections::{BTreeMap, HashSet}; @@ -160,11 +160,7 @@ impl VersionedMpcContract { } } let predecessor = env::predecessor_account_id(); - let request = SignatureRequest::new( - payload, - &predecessor, - &path, - ); + let request = SignatureRequest::new(payload, &predecessor, &path); if !self.request_already_exists(&request) { log!( "sign: predecessor={predecessor}, payload={payload:?}, path={path:?}, key_version={key_version}", @@ -696,7 +692,7 @@ impl VersionedMpcContract { // by it won't execute. let yield_promise = env::promise_yield_create( "clear_state_on_finish", - &serde_json::to_vec(&(&contract_signature_request)).unwrap(), + &serde_json::to_vec(&(&contract_signature_request,)).unwrap(), CLEAR_STATE_ON_FINISH_CALL_GAS, GasWeight(0), DATA_ID_REGISTER, @@ -773,7 +769,8 @@ impl VersionedMpcContract { match self { Self::V0(mpc_contract) => { // Clean up the local state - let result = mpc_contract.remove_request(contract_signature_request.request.clone()); + let result = + mpc_contract.remove_request(contract_signature_request.request.clone()); if result.is_err() { Self::refund_on_fail(&contract_signature_request); result?; diff --git a/chain-signatures/contract/src/primitives.rs b/chain-signatures/contract/src/primitives.rs index e972a0544..fc2639983 100644 --- a/chain-signatures/contract/src/primitives.rs +++ b/chain-signatures/contract/src/primitives.rs @@ -41,11 +41,7 @@ pub struct ContractSignatureRequest { } impl SignatureRequest { - pub fn new( - payload_hash: Scalar, - predecessor_id: &AccountId, - path: &str, - ) -> Self { + pub fn new(payload_hash: Scalar, predecessor_id: &AccountId, path: &str) -> Self { let epsilon = derive_epsilon(predecessor_id, path); let epsilon = SerializableScalar { scalar: epsilon }; let payload_hash = SerializableScalar { diff --git a/chain-signatures/contract/tests/common.rs b/chain-signatures/contract/tests/common.rs index 1f75fe129..6ff31b0a6 100644 --- a/chain-signatures/contract/tests/common.rs +++ b/chain-signatures/contract/tests/common.rs @@ -176,11 +176,7 @@ pub async fn create_response( let s = signature.s(); let (r_bytes, _s_bytes) = signature.split_bytes(); let payload_hash_s = Scalar::from_bytes(payload_hash).unwrap(); - let respond_req = SignatureRequest::new( - payload_hash_s, - predecessor_id, - path, - ); + let respond_req = SignatureRequest::new(payload_hash_s, 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(); From 5277d7309ace503f2cb55d96fbf601dda68b5edc Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Thu, 1 Aug 2024 17:49:28 +0800 Subject: [PATCH 6/8] a little more deposit to update --- chain-signatures/contract/tests/updates.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain-signatures/contract/tests/updates.rs b/chain-signatures/contract/tests/updates.rs index afb747f78..28839804b 100644 --- a/chain-signatures/contract/tests/updates.rs +++ b/chain-signatures/contract/tests/updates.rs @@ -34,7 +34,7 @@ pub fn invalid_contract() -> ProposeUpdateArgs { /// This is the current deposit required for a contract deploy. This is subject to change but make /// sure that it's not larger than 2mb. We can go up to 4mb technically but our contract should /// not be getting that big. -const CURRENT_CONTRACT_DEPLOY_DEPOSIT: NearToken = NearToken::from_millinear(8600); +const CURRENT_CONTRACT_DEPLOY_DEPOSIT: NearToken = NearToken::from_millinear(8700); #[tokio::test] async fn test_propose_contract_max_size_upload() { From cae626b5b16f356822c32aee59a6b7a90461563e Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Thu, 1 Aug 2024 18:26:28 +0800 Subject: [PATCH 7/8] move comment --- chain-signatures/contract/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chain-signatures/contract/src/lib.rs b/chain-signatures/contract/src/lib.rs index d8fe3d147..fae3e3b1e 100644 --- a/chain-signatures/contract/src/lib.rs +++ b/chain-signatures/contract/src/lib.rs @@ -686,10 +686,6 @@ impl VersionedMpcContract { pub fn sign_helper(&mut self, contract_signature_request: ContractSignatureRequest) { match self { Self::V0(mpc_contract) => { - // refund must happen in clear_state_on_finish, because regardless of this success or fail - // the promise created by clear_state_on_finish is executed, because of callback_unwrap and - // promise_then. but if return_signature_on_finish fail (returns error), the promise created - // by it won't execute. let yield_promise = env::promise_yield_create( "clear_state_on_finish", &serde_json::to_vec(&(&contract_signature_request,)).unwrap(), @@ -772,6 +768,10 @@ impl VersionedMpcContract { let result = mpc_contract.remove_request(contract_signature_request.request.clone()); if result.is_err() { + // refund must happen in clear_state_on_finish, because regardless of this success or fail + // the promise created by clear_state_on_finish is executed, because of callback_unwrap and + // promise_then. but if return_signature_on_finish fail (returns error), the promise created + // by it won't execute. Self::refund_on_fail(&contract_signature_request); result?; } From b917d4fa5bc5b040a5ea4d318f56a7db616f7502 Mon Sep 17 00:00:00 2001 From: Bo Yao Date: Fri, 2 Aug 2024 08:07:13 +0800 Subject: [PATCH 8/8] make sure mpc contract balance right --- chain-signatures/contract/tests/sign.rs | 26 +++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/chain-signatures/contract/tests/sign.rs b/chain-signatures/contract/tests/sign.rs index 6cbac2a06..d79567cdb 100644 --- a/chain-signatures/contract/tests/sign.rs +++ b/chain-signatures/contract/tests/sign.rs @@ -64,7 +64,7 @@ async fn test_contract_sign_success_refund() -> anyhow::Result<()> { let (worker, contract, _, sk) = init_env().await; let alice = worker.dev_create_account().await?; let balance = alice.view_account().await?.balance; - + let contract_balance = contract.view_account().await?.balance; let path = "test"; let msg = "hello world!"; @@ -114,10 +114,22 @@ async fn test_contract_sign_success_refund() -> anyhow::Result<()> { ); let new_balance = alice.view_account().await?.balance; + let new_contract_balance = contract.view_account().await?.balance; assert!( balance.as_millinear() - new_balance.as_millinear() < 10, "refund should happen" ); + println!( + "{} {} {} {}", + balance.as_millinear(), + new_balance.as_millinear(), + contract_balance.as_millinear(), + new_contract_balance.as_millinear(), + ); + assert!( + contract_balance.as_millinear() - new_contract_balance.as_millinear() < 20, + "respond should take less than 0.02 NEAR" + ); Ok(()) } @@ -127,7 +139,7 @@ async fn test_contract_sign_fail_refund() -> anyhow::Result<()> { let (worker, contract, _, sk) = init_env().await; let alice = worker.dev_create_account().await?; let balance = alice.view_account().await?.balance; - + let contract_balance = contract.view_account().await?.balance; let path = "test"; let msg = "hello world!"; @@ -163,16 +175,22 @@ async fn test_contract_sign_fail_refund() -> anyhow::Result<()> { .contains(&errors::SignError::Timeout.to_string())); let new_balance = alice.view_account().await?.balance; + let new_contract_balance = contract.view_account().await?.balance; println!( - "{} {} {}", + "{} {} {} {}", balance.as_millinear(), new_balance.as_millinear(), - contract.view_account().await?.balance + contract_balance.as_yoctonear(), + new_contract_balance.as_yoctonear(), ); assert!( balance.as_millinear() - new_balance.as_millinear() < 10, "refund should happen" ); + assert!( + contract_balance.as_millinear() - new_contract_balance.as_millinear() <= 1, + "refund transfer should take less than 0.001 NEAR" + ); Ok(()) }