From 8693e7d9e1014803efda60162bbcd60826d52739 Mon Sep 17 00:00:00 2001 From: benluelo Date: Tue, 16 Jul 2024 06:27:06 -0400 Subject: [PATCH] feat(voyager): dump failing messages when submitting a batch --- evm/README.md | 2 +- lib/chain-utils/src/cosmos_sdk.rs | 147 ++++++++++-------- lib/relay-message/src/chain/cosmos_sdk.rs | 60 +++++-- lib/unionlabs/src/cosmos/base.rs | 1 + lib/unionlabs/src/cosmos/base/abci.rs | 1 + .../src/cosmos/base/abci/gas_info.rs | 25 +++ voyager-config.json | 2 +- 7 files changed, 160 insertions(+), 78 deletions(-) create mode 100644 lib/unionlabs/src/cosmos/base/abci.rs create mode 100644 lib/unionlabs/src/cosmos/base/abci/gas_info.rs diff --git a/evm/README.md b/evm/README.md index 3a473cbf0d..164ab0fad8 100644 --- a/evm/README.md +++ b/evm/README.md @@ -45,7 +45,7 @@ This links are working if you run a local devnet on a x86 machine only (Blocksco - CometblsClient: [0xc4f27a952faba4174ce0ee6d9d0c6f4c41524d49](http://localhost/address/0xc4f27a952faba4174ce0ee6d9d0c6f4c41524d49) - UCS01: [0xa9d03ba6e27b43c69a64c87f845485b73a8e5d46](http://localhost/address/0xa9d03ba6e27b43c69a64c87f845485b73a8e5d46) - UCS02: [0x524d4d28fc90dc5a257162abe37081f52681c7d6](http://localhost/address/0x524d4d28fc90dc5a257162abe37081f52681c7d6) -- Multicall: [0x72459D25D5e30ec16b9Ac91cfB7e5a7969347E58](http://localhost/address/0x72459D25D5e30ec16b9Ac91cfB7e5a7969347E58?tab=contract) +- Multicall: [0x9fd9D9528c8373D990a1380B9414bDE179007A35](http://localhost/address/0x9fd9D9528c8373D990a1380B9414bDE179007A35?tab=contract) ## Testnet 8 diff --git a/lib/chain-utils/src/cosmos_sdk.rs b/lib/chain-utils/src/cosmos_sdk.rs index 35a0947dcb..0b1ac39fb0 100644 --- a/lib/chain-utils/src/cosmos_sdk.rs +++ b/lib/chain-utils/src/cosmos_sdk.rs @@ -8,7 +8,7 @@ use tracing::{debug, error, info, warn}; use unionlabs::{ cosmos::{ auth::base_account::BaseAccount, - base::coin::Coin, + base::{abci::gas_info::GasInfo, coin::Coin}, crypto::{secp256k1, AnyPubKey}, tx::{ auth_info::AuthInfo, fee::Fee, mode_info::ModeInfo, sign_doc::SignDoc, @@ -192,74 +192,12 @@ pub trait CosmosSdkChainExt: CosmosSdkChainRpcs { messages: impl IntoIterator + Clone, memo: String, ) -> Result<(H256, u64), BroadcastTxCommitError> { - use protos::cosmos::tx; - let account = self.account_info(&signer.to_string()).await; - let mut client = tx::v1beta1::service_client::ServiceClient::connect(self.grpc_url()) + let (tx_body, mut auth_info, simulation_gas_info) = self + .simulate_tx(signer, messages, memo) .await - .unwrap(); - - let tx_body = TxBody { - messages: messages.clone().into_iter().collect(), - // TODO(benluelo): What do we want to use as our memo? - memo, - timeout_height: 0, - extension_options: vec![], - non_critical_extension_options: vec![], - }; - - let mut auth_info = AuthInfo { - signer_infos: [SignerInfo { - public_key: Some(AnyPubKey::Secp256k1(secp256k1::PubKey { - key: signer.public_key(), - })), - mode_info: ModeInfo::Single { - mode: SignMode::Direct, - }, - sequence: account.sequence, - }] - .to_vec(), - fee: self.gas_config().mk_fee(self.gas_config().max_gas).clone(), - }; - - let simulation_signature = signer - .try_sign( - &SignDoc { - body_bytes: tx_body.clone().encode_as::(), - auth_info_bytes: auth_info.clone().encode_as::(), - chain_id: self.tm_chain_id().to_string(), - account_number: account.account_number, - } - .encode_as::(), - ) - .expect("signing failed") - .to_vec(); - - let simulation_gas_info = { - let result = client - .simulate(tx::v1beta1::SimulateRequest { - tx_bytes: Tx { - body: tx_body.clone(), - auth_info: auth_info.clone(), - signatures: [simulation_signature.clone()].to_vec(), - } - .encode_as::(), - ..Default::default() - }) - .await; - - match result { - Ok(ok) => ok - .into_inner() - .gas_info - .expect("gas info is present on successful simulation result"), - Err(err) => { - error!(error = %err.message(), "tx simulation failed"); - return Err(BroadcastTxCommitError::SimulateTx(err.message().to_owned())); - } - } - }; + .map_err(BroadcastTxCommitError::SimulateTx)?; info!( gas_used = %simulation_gas_info.gas_used, @@ -397,6 +335,83 @@ pub trait CosmosSdkChainExt: CosmosSdkChainRpcs { } } + async fn simulate_tx( + &self, + signer: &CosmosSigner, + messages: impl IntoIterator + Clone, + memo: String, + ) -> Result<(TxBody, AuthInfo, GasInfo), String> { + use protos::cosmos::tx; + + let account = self.account_info(&signer.to_string()).await; + + let mut client = tx::v1beta1::service_client::ServiceClient::connect(self.grpc_url()) + .await + .unwrap(); + + let tx_body = TxBody { + messages: messages.clone().into_iter().collect(), + memo, + timeout_height: 0, + extension_options: vec![], + non_critical_extension_options: vec![], + }; + + let auth_info = AuthInfo { + signer_infos: [SignerInfo { + public_key: Some(AnyPubKey::Secp256k1(secp256k1::PubKey { + key: signer.public_key(), + })), + mode_info: ModeInfo::Single { + mode: SignMode::Direct, + }, + sequence: account.sequence, + }] + .to_vec(), + fee: self.gas_config().mk_fee(self.gas_config().max_gas).clone(), + }; + + let simulation_signature = signer + .try_sign( + &SignDoc { + body_bytes: tx_body.clone().encode_as::(), + auth_info_bytes: auth_info.clone().encode_as::(), + chain_id: self.tm_chain_id().to_string(), + account_number: account.account_number, + } + .encode_as::(), + ) + .expect("signing failed") + .to_vec(); + + let result = client + .simulate(tx::v1beta1::SimulateRequest { + tx_bytes: Tx { + body: tx_body.clone(), + auth_info: auth_info.clone(), + signatures: [simulation_signature.clone()].to_vec(), + } + .encode_as::(), + ..Default::default() + }) + .await; + + match result { + Ok(ok) => Ok(( + tx_body, + auth_info, + ok.into_inner() + .gas_info + .expect("gas info is present on successful simulation result") + .into(), + )), + Err(err) => { + info!(error = %err.message(), "tx simulation failed"); + Err(err.message().to_owned()) + } + } + } + async fn account_info(&self, account: &str) -> BaseAccount { debug!(%account, "fetching account"); let Any(account) = diff --git a/lib/relay-message/src/chain/cosmos_sdk.rs b/lib/relay-message/src/chain/cosmos_sdk.rs index 038c00f1d8..f1802a85e8 100644 --- a/lib/relay-message/src/chain/cosmos_sdk.rs +++ b/lib/relay-message/src/chain/cosmos_sdk.rs @@ -5,8 +5,9 @@ use chain_utils::{ keyring::ChainKeyring, }; use frame_support_procedural::{CloneNoBound, PartialEqNoBound}; +use futures::{stream, FutureExt, StreamExt}; use queue_msg::{data, effect, fetch, noop, seq, wait, Op}; -use tracing::{debug, info}; +use tracing::{debug, error, info}; use unionlabs::{ encoding::{Decode, DecodeAs, DecodeErrorOf, Encode, Proto}, google::protobuf::any::{mk_any, IntoAny}, @@ -70,20 +71,58 @@ where .with(|signer| { let msg = msg.clone(); + // TODO: Figure out a way to thread this value through + let memo = format!("Voyager {}", env!("CARGO_PKG_VERSION")); + async move { - let msgs = process_msgs(msg, signer, mk_create_client_states, mk_client_message); + let mut msgs = + process_msgs(msg, signer, mk_create_client_states, mk_client_message); + + let simulation_results = stream::iter(msgs.clone().into_iter().enumerate()) + .then(|(idx, msg)| { + let type_url = msg.type_url.clone(); + hc.simulate_tx(signer, [msg], memo.clone()) + .map(move |res| (idx, type_url, res)) + }) + .collect::)>>() + .await; + + // iterate backwards such that when we remove items from msgs, we don't shift the relative indices + for (idx, type_url, simulation_result) in simulation_results.into_iter().rev() { + match simulation_result { + Ok((_, _, gas_info)) => { + debug!( + msg = %type_url, + %idx, + gas_wanted = %gas_info.gas_wanted, + gas_used = %gas_info.gas_used, + "individual message simulation successful" + ) + } + Err(error) => { + error!( + %error, + msg = %type_url, + %idx, + "individual message simulation failed" + ); + + msgs.remove(idx); + } + } + } + + if msgs.is_empty() { + info!( + "no messages remaining to submit after filtering out failed transactions" + ); + return Ok(()); + } let batch_size = msgs.len(); let msg_names = msgs.iter().map(|x| x.type_url.clone()).collect::>(); - let (tx_hash, gas_used) = hc - .broadcast_tx_commit( - signer, - msgs, - // TODO: Figure out a way to thread this value through - format!("Voyager {}", env!("CARGO_PKG_VERSION")), - ) - .await?; + let (tx_hash, gas_used) = hc.broadcast_tx_commit(signer, msgs, memo).await?; info!( %tx_hash, @@ -91,6 +130,7 @@ where batch.size = %batch_size, "submitted cosmos transaction" ); + for msg in msg_names { info!(%tx_hash, %msg, "cosmos tx"); } diff --git a/lib/unionlabs/src/cosmos/base.rs b/lib/unionlabs/src/cosmos/base.rs index 2ef53913b6..b7eb304c78 100644 --- a/lib/unionlabs/src/cosmos/base.rs +++ b/lib/unionlabs/src/cosmos/base.rs @@ -1,2 +1,3 @@ +pub mod abci; pub mod coin; pub mod query; diff --git a/lib/unionlabs/src/cosmos/base/abci.rs b/lib/unionlabs/src/cosmos/base/abci.rs new file mode 100644 index 0000000000..01293126a2 --- /dev/null +++ b/lib/unionlabs/src/cosmos/base/abci.rs @@ -0,0 +1 @@ +pub mod gas_info; diff --git a/lib/unionlabs/src/cosmos/base/abci/gas_info.rs b/lib/unionlabs/src/cosmos/base/abci/gas_info.rs new file mode 100644 index 0000000000..8c97ed1d1e --- /dev/null +++ b/lib/unionlabs/src/cosmos/base/abci/gas_info.rs @@ -0,0 +1,25 @@ +use macros::model; + +#[model(proto(raw(protos::cosmos::base::abci::v1beta1::GasInfo), from, into))] +pub struct GasInfo { + pub gas_wanted: u64, + pub gas_used: u64, +} + +impl From for GasInfo { + fn from(value: protos::cosmos::base::abci::v1beta1::GasInfo) -> Self { + Self { + gas_wanted: value.gas_wanted, + gas_used: value.gas_used, + } + } +} + +impl From for protos::cosmos::base::abci::v1beta1::GasInfo { + fn from(value: GasInfo) -> Self { + Self { + gas_wanted: value.gas_wanted, + gas_used: value.gas_used, + } + } +} diff --git a/voyager-config.json b/voyager-config.json index b549d057e6..99da355843 100644 --- a/voyager-config.json +++ b/voyager-config.json @@ -6,7 +6,7 @@ "preset_base": "minimal", "ibc_commitment_slot": "0", "ibc_handler_address": "0xed2af2ad7fe0d92011b26a2e5d1b4dc7d12a47c5", - "multicall_address": "0x72459D25D5e30ec16b9Ac91cfB7e5a7969347E58", + "multicall_address": "0x9fd9D9528c8373D990a1380B9414bDE179007A35", "keyring": { "name": "ethereum-devnet", "keys": [