Skip to content

Commit

Permalink
Added a new function that do a dry_run before sumbiting a call onto t…
Browse files Browse the repository at this point in the history
…he chain

Some RPC functions that can fail now uses the new dry_run function.
Removed the old dry_run function that is defunct.
  • Loading branch information
syan095 committed Oct 27, 2023
1 parent 9ef3413 commit 3af9f03
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 51 deletions.
52 changes: 16 additions & 36 deletions api/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,26 +139,12 @@ impl StateChainApi {
}
}

#[async_trait]
impl<
SignedExtrinsicClient: Send + Sync + 'static + SignedExtrinsicApi,
RawRpcClient: Send + Sync + 'static + RawRpcApi,
> OperatorApi for StateChainClient<SignedExtrinsicClient, BaseRpcClient<RawRpcClient>>
{
async fn dry_run(
&self,
_call: RuntimeCall,
_at: Option<state_chain_runtime::Hash>,
) -> Result<Bytes> {
// TODO: PRO-917 fix dry run
Ok(Bytes::from(vec![]))
}
}

#[async_trait]
impl GovernanceApi for StateChainClient {}
#[async_trait]
impl BrokerApi for StateChainClient {}
#[async_trait]
impl OperatorApi for StateChainClient {}

#[async_trait]
pub trait OperatorApi: SignedExtrinsicApi + RotateSessionKeysApi + AuctionPhaseApi {
Expand All @@ -169,9 +155,9 @@ pub trait OperatorApi: SignedExtrinsicApi + RotateSessionKeysApi + AuctionPhaseA
executor: Option<EthereumAddress>,
) -> Result<H256> {
let call = RuntimeCall::from(pallet_cf_funding::Call::redeem { amount, address, executor });
self.dry_run(call.clone(), None).await?;

let (tx_hash, ..) = self.submit_signed_extrinsic(call).await.until_in_block().await?;
let (tx_hash, ..) =
self.submit_signed_extrinsic_with_dry_run(call).await?.until_in_block().await?;

Ok(tx_hash)
}
Expand Down Expand Up @@ -209,11 +195,9 @@ pub trait OperatorApi: SignedExtrinsicApi + RotateSessionKeysApi + AuctionPhaseA
AccountRole::None => bail!("Cannot register account role None"),
};

let _ = self.dry_run(call.clone(), None).await?;

let (tx_hash, ..) = self
.submit_signed_extrinsic(call)
.await
.submit_signed_extrinsic_with_dry_run(call)
.await?
.until_in_block()
.await
.context("Could not register account role for account")?;
Expand Down Expand Up @@ -282,12 +266,6 @@ pub trait OperatorApi: SignedExtrinsicApi + RotateSessionKeysApi + AuctionPhaseA
println!("Vanity name set at tx {tx_hash:#x}.");
Ok(())
}

async fn dry_run(
&self,
call: RuntimeCall,
at: Option<state_chain_runtime::Hash>,
) -> Result<Bytes>;
}

#[async_trait]
Expand Down Expand Up @@ -327,14 +305,16 @@ pub trait BrokerApi: SignedExtrinsicApi {
channel_metadata: Option<CcmChannelMetadata>,
) -> Result<SwapDepositAddress> {
let (_tx_hash, events, header, ..) = self
.submit_signed_extrinsic(pallet_cf_swapping::Call::request_swap_deposit_address {
source_asset,
destination_asset,
destination_address,
broker_commission_bps,
channel_metadata,
})
.await
.submit_signed_extrinsic_with_dry_run(
pallet_cf_swapping::Call::request_swap_deposit_address {
source_asset,
destination_asset,
destination_address,
broker_commission_bps,
channel_metadata,
},
)
.await?
.until_in_block()
.await?;

Expand Down
24 changes: 12 additions & 12 deletions api/lib/src/lp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ impl LpApi for StateChainClient {}
pub trait LpApi: SignedExtrinsicApi {
async fn register_liquidity_refund_address(&self, address: EncodedAddress) -> Result<H256> {
let (tx_hash, ..) = self
.submit_signed_extrinsic(RuntimeCall::from(
.submit_signed_extrinsic_with_dry_run(RuntimeCall::from(
pallet_cf_lp::Call::register_liquidity_refund_address { address },
))
.await
.await?
.until_in_block()
.await
.context("Registration for Liquidity Refund Address failed.")?;
Expand Down Expand Up @@ -138,12 +138,12 @@ pub trait LpApi: SignedExtrinsicApi {
destination_address: EncodedAddress,
) -> Result<EgressId> {
let (_tx_hash, events, ..) = self
.submit_signed_extrinsic(pallet_cf_lp::Call::withdraw_asset {
.submit_signed_extrinsic_with_dry_run(pallet_cf_lp::Call::withdraw_asset {
amount,
asset,
destination_address,
})
.await
.await?
.until_in_block()
.await?;

Expand All @@ -169,15 +169,15 @@ pub trait LpApi: SignedExtrinsicApi {
) -> Result<Vec<RangeOrderReturn>> {
// Submit the mint order
let (_tx_hash, events, ..) = self
.submit_signed_extrinsic(pallet_cf_pools::Call::update_range_order {
.submit_signed_extrinsic_with_dry_run(pallet_cf_pools::Call::update_range_order {
base_asset,
pair_asset,
id,
option_tick_range,
increase_or_decrease,
size,
})
.await
.await?
.until_in_block()
.await?;

Expand All @@ -194,14 +194,14 @@ pub trait LpApi: SignedExtrinsicApi {
) -> Result<Vec<RangeOrderReturn>> {
// Submit the mint order
let (_tx_hash, events, ..) = self
.submit_signed_extrinsic(pallet_cf_pools::Call::set_range_order {
.submit_signed_extrinsic_with_dry_run(pallet_cf_pools::Call::set_range_order {
base_asset,
pair_asset,
id,
option_tick_range,
size,
})
.await
.await?
.until_in_block()
.await?;

Expand All @@ -219,15 +219,15 @@ pub trait LpApi: SignedExtrinsicApi {
) -> Result<Vec<LimitOrderReturn>> {
// Submit the mint order
let (_tx_hash, events, ..) = self
.submit_signed_extrinsic(pallet_cf_pools::Call::update_limit_order {
.submit_signed_extrinsic_with_dry_run(pallet_cf_pools::Call::update_limit_order {
sell_asset,
buy_asset,
id,
option_tick,
increase_or_decrease,
amount,
})
.await
.await?
.until_in_block()
.await?;

Expand All @@ -244,14 +244,14 @@ pub trait LpApi: SignedExtrinsicApi {
) -> Result<Vec<LimitOrderReturn>> {
// Submit the burn order
let (_tx_hash, events, ..) = self
.submit_signed_extrinsic(pallet_cf_pools::Call::set_limit_order {
.submit_signed_extrinsic_with_dry_run(pallet_cf_pools::Call::set_limit_order {
sell_asset,
buy_asset,
id,
option_tick,
sell_amount,
})
.await
.await?
.until_in_block()
.await?;

Expand Down
42 changes: 42 additions & 0 deletions engine/src/state_chain_observer/client/extrinsic_api/signed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ pub trait SignedExtrinsicApi {
+ Sync
+ 'static;

async fn submit_signed_extrinsic_with_dry_run<Call>(
&self,
call: Call,
) -> Result<(H256, (Self::UntilInBlockFuture, Self::UntilFinalizedFuture))>
where
Call: Into<state_chain_runtime::RuntimeCall>
+ Clone
+ std::fmt::Debug
+ Send
+ Sync
+ 'static;

async fn finalize_signed_extrinsic<Call>(
&self,
call: Call,
Expand All @@ -115,6 +127,7 @@ pub struct SignedExtrinsicClient {
oneshot::Sender<submission_watcher::FinalizationResult>,
submission_watcher::RequestStrategy,
)>,
dry_run_sender: mpsc::Sender<(state_chain_runtime::RuntimeCall, oneshot::Sender<Result<()>>)>,
_task_handle: ScopedJoinHandle<()>,
}

Expand All @@ -136,6 +149,7 @@ impl SignedExtrinsicClient {
const REQUEST_BUFFER: usize = 16;

let (request_sender, mut request_receiver) = mpsc::channel(REQUEST_BUFFER);
let (dry_run_sender, mut dry_run_receiver) = mpsc::channel(REQUEST_BUFFER);

let account_nonce = {
loop {
Expand Down Expand Up @@ -177,6 +191,7 @@ impl SignedExtrinsicClient {
Ok(Self {
account_id: signer.account_id.clone(),
request_sender,
dry_run_sender,
_task_handle: scope.spawn_with_handle({
let mut state_chain_stream = state_chain_stream.clone();

Expand All @@ -199,6 +214,9 @@ impl SignedExtrinsicClient {
if let Some((call, until_in_block_sender, until_finalized_sender, strategy)) = request_receiver.recv() => {
submission_watcher.new_request(&mut requests, call, until_in_block_sender, until_finalized_sender, strategy).await?;
} else break Ok(()),
if let Some((call, result_sender)) = dry_run_receiver.recv() => {
let _ = result_sender.send(submission_watcher.dry_run_extrinsic(call).await);
} else break Ok(()),
let submission_details = submission_watcher.watch_for_submission_in_block() => {
submission_watcher.on_submission_in_block(&mut requests, submission_details).await?;
},
Expand Down Expand Up @@ -258,6 +276,30 @@ impl SignedExtrinsicApi for SignedExtrinsicClient {
)
}

/// Dry run the call, and only submit the extrinsic onto the chain
/// if dry-run returns Ok(())
async fn submit_signed_extrinsic_with_dry_run<Call>(
&self,
call: Call,
) -> Result<(H256, (Self::UntilInBlockFuture, Self::UntilFinalizedFuture))>
where
Call: Into<state_chain_runtime::RuntimeCall>
+ Clone
+ std::fmt::Debug
+ Send
+ Sync
+ 'static,
{
let _ = send_request(&self.dry_run_sender, |result_sender| {
(call.clone().into(), result_sender)
})
.await
.await
.expect(OR_CANCEL)?;

Ok(self.submit_signed_extrinsic(call.into()).await)
}

async fn finalize_signed_extrinsic<Call>(
&self,
call: Call,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use std::{

use anyhow::{anyhow, Result};
use cf_primitives::SemVer;
use codec::{Decode, Encode};
use frame_support::{dispatch::DispatchInfo, pallet_prelude::InvalidTransaction};
use itertools::Itertools;
use sc_transaction_pool_api::TransactionStatus;
use sp_core::H256;
use sp_runtime::{traits::Hash, MultiAddress};
use sp_runtime::{traits::Hash, ApplyExtrinsicResult, MultiAddress};
use state_chain_runtime::{BlockNumber, Nonce, UncheckedExtrinsic};
use thiserror::Error;
use tokio::sync::oneshot;
Expand Down Expand Up @@ -43,6 +44,16 @@ pub enum ExtrinsicError<OtherError> {
Dispatch(DispatchError),
}

#[derive(Error, Debug)]
pub enum DryRunError {
#[error("The dry_run RPC call failed.")]
DryRunRpcCallError,
#[error("The reply from the dry_run RPC cannot be decoded correctly.")]
CannotDecodeReply,
#[error("The dry_run returned Invalid Transaction error.")]
TransactionInvalid,
}

pub type ExtrinsicResult<OtherError> = Result<
(H256, Vec<state_chain_runtime::RuntimeEvent>, state_chain_runtime::Header, DispatchInfo),
ExtrinsicError<OtherError>,
Expand Down Expand Up @@ -166,6 +177,24 @@ impl<'a, 'env, BaseRpcClient: base_rpc_api::BaseRpcApi + Send + Sync + 'static>
)
}

fn build_and_sign_extrinsic(
&self,
call: state_chain_runtime::RuntimeCall,
nonce: Nonce,
) -> state_chain_runtime::UncheckedExtrinsic {
self.signer
.new_signed_extrinsic(
call.clone(),
&self.runtime_version,
self.genesis_hash,
self.finalized_block_hash,
self.finalized_block_number,
self.extrinsic_lifetime,
nonce,
)
.0
}

async fn submit_extrinsic_at_nonce(
&mut self,
request: &mut Request,
Expand All @@ -184,9 +213,8 @@ impl<'a, 'env, BaseRpcClient: base_rpc_api::BaseRpcApi + Send + Sync + 'static>
assert!(lifetime.contains(&(self.finalized_block_number + 1)));

let tx_hash: H256 = {
use sp_core::{blake2_256, Encode};
let encoded = signed_extrinsic.encode();
blake2_256(&encoded).into()
sp_core::blake2_256(&encoded).into()
};

match self.base_rpc_client.submit_and_watch_extrinsic(signed_extrinsic).await {
Expand Down Expand Up @@ -271,6 +299,29 @@ impl<'a, 'env, BaseRpcClient: base_rpc_api::BaseRpcApi + Send + Sync + 'static>
})
}

pub async fn dry_run_extrinsic(
&mut self,
call: state_chain_runtime::RuntimeCall,
) -> anyhow::Result<()> {
let nonce = self.base_rpc_client.next_account_nonce(self.signer.account_id.clone()).await?;
let uxt = self.build_and_sign_extrinsic(call.clone(), nonce);
let dry_run_result = self
.base_rpc_client
.dry_run(Encode::encode(&uxt).into(), None)
.await
.map_err(|_| ExtrinsicError::Other(DryRunError::DryRunRpcCallError))?;
let res: ApplyExtrinsicResult = Decode::decode(&mut &*dry_run_result)
.map_err(|_| ExtrinsicError::Other(DryRunError::CannotDecodeReply))?;
info!(target: "state_chain_client", "Dry run completed. Result: {:?}", res.clone());
res.map_err(|_| ExtrinsicError::Other(DryRunError::TransactionInvalid))?
.map_err(|e| {
anyhow!(
"DispatchError during dry run: {:?}",
self.error_decoder.decode_dispatch_error(e)
)
})
}

pub async fn new_request(
&mut self,
requests: &mut BTreeMap<RequestID, Request>,
Expand Down
Loading

0 comments on commit 3af9f03

Please sign in to comment.