diff --git a/Cargo.lock b/Cargo.lock index b585124a2c..3511284a46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -386,6 +386,16 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "byte-unit" +version = "4.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c" +dependencies = [ + "serde", + "utf8-width", +] + [[package]] name = "bytecount" version = "0.6.3" @@ -1771,6 +1781,7 @@ dependencies = [ "bech32", "bitcoin", "bs58", + "byte-unit", "bytes", "crossbeam-channel 0.5.8", "digest 0.10.6", @@ -4653,6 +4664,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8-width" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" + [[package]] name = "utf8parse" version = "0.2.1" diff --git a/crates/relayer-cli/src/chain_registry.rs b/crates/relayer-cli/src/chain_registry.rs index 79454cef65..d7f95546bd 100644 --- a/crates/relayer-cli/src/chain_registry.rs +++ b/crates/relayer-cli/src/chain_registry.rs @@ -134,6 +134,7 @@ where fee_granter: None, max_msg_num: MaxMsgNum::default(), max_tx_size: MaxTxSize::default(), + max_grpc_decoding_size: default::max_grpc_decoding_size(), clock_drift: default::clock_drift(), max_block_time: default::max_block_time(), trusting_period: None, diff --git a/crates/relayer/Cargo.toml b/crates/relayer/Cargo.toml index 184e9e0d8f..460d3ee65e 100644 --- a/crates/relayer/Cargo.toml +++ b/crates/relayer/Cargo.toml @@ -70,6 +70,11 @@ secp256k1 = { version = "0.27.0", features = ["rand-std"] } strum = { version = "0.24.1", features = ["derive"] } once_cell = "1.17.1" +[dependencies.byte-unit] +version = "4.0.19" +default-features = false +features = ["serde"] + [dependencies.num-bigint] version = "0.4" features = ["serde"] diff --git a/crates/relayer/src/chain/cosmos.rs b/crates/relayer/src/chain/cosmos.rs index dc8850a2dd..17f3b230a2 100644 --- a/crates/relayer/src/chain/cosmos.rs +++ b/crates/relayer/src/chain/cosmos.rs @@ -11,7 +11,8 @@ use num_bigint::BigInt; use std::{cmp::Ordering, thread}; use tokio::runtime::Runtime as TokioRuntime; -use tonic::{codegen::http::Uri, metadata::AsciiMetadataValue}; +use tonic::codegen::http::Uri; +use tonic::metadata::AsciiMetadataValue; use tracing::{error, instrument, trace, warn}; use ibc_proto::cosmos::{ @@ -328,13 +329,16 @@ impl CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new( ibc_proto::interchain_security::ccv::consumer::v1::QueryParamsRequest {}, ); let response = self .block_on(client.query_params(request)) - .map_err(Error::grpc_status)?; + .map_err(|e| Error::grpc_status(e, "query_ccv_consumer_chain_params".to_owned()))?; let params = response .into_inner() @@ -362,12 +366,15 @@ impl CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(ibc_proto::cosmos::staking::v1beta1::QueryParamsRequest {}); let response = self .block_on(client.params(request)) - .map_err(Error::grpc_status)?; + .map_err(|e| Error::grpc_status(e, "query_staking_params".to_owned()))?; let params = response .into_inner() @@ -414,6 +421,9 @@ impl CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(ibc_proto::cosmos::base::node::v1beta1::ConfigRequest {}); match self.block_on(client.config(request)) { @@ -426,7 +436,7 @@ impl CosmosSdkChain { if is_unimplemented_node_query(&e) { Ok(None) } else { - Err(Error::grpc_status(e)) + Err(Error::grpc_status(e, "query_config_params".to_owned())) } } } @@ -1120,10 +1130,13 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let response = self .block_on(client.client_states(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_clients".to_owned()))? .into_inner(); // Deserialize into domain type @@ -1308,12 +1321,15 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let response = match self.block_on(client.client_connections(request)) { Ok(res) => res.into_inner(), Err(e) if e.code() == tonic::Code::NotFound => return Ok(vec![]), - Err(e) => return Err(Error::grpc_status(e)), + Err(e) => return Err(Error::grpc_status(e, "query_client_connections".to_owned())), }; let ids = response @@ -1349,11 +1365,14 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let response = self .block_on(client.connections(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_connections".to_owned()))? .into_inner(); let connections = response @@ -1401,6 +1420,10 @@ impl ChainEndpoint for CosmosSdkChain { .await .map_err(Error::grpc_transport)?; + client = client.max_decoding_message_size( + chain.config().max_grpc_decoding_size.get_bytes() as usize, + ); + let mut request = connection::QueryConnectionRequest { connection_id: connection_id.to_string(), } @@ -1416,7 +1439,7 @@ impl ChainEndpoint for CosmosSdkChain { if e.code() == tonic::Code::NotFound { Error::connection_not_found(connection_id.clone()) } else { - Error::grpc_status(e) + Error::grpc_status(e, "query_connection".to_owned()) } })?; @@ -1479,11 +1502,14 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let response = self .block_on(client.connection_channels(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_connection_channels".to_owned()))? .into_inner(); let channels = response @@ -1524,11 +1550,14 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let response = self .block_on(client.channels(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_channels".to_owned()))? .into_inner(); let channels = response @@ -1599,11 +1628,14 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let response = self .block_on(client.channel_client_state(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_channel_client_state".to_owned()))? .into_inner(); let client_state: Option = response @@ -1659,11 +1691,14 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let response = self .block_on(client.packet_commitments(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_packet_commitments".to_owned()))? .into_inner(); let mut commitment_sequences: Vec = response @@ -1727,11 +1762,14 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let mut response = self .block_on(client.unreceived_packets(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_unreceived_packets".to_owned()))? .into_inner(); response.sequences.sort_unstable(); @@ -1788,11 +1826,14 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let response = self .block_on(client.packet_acknowledgements(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_packet_acknowledgements".to_owned()))? .into_inner(); let acks_sequences = response @@ -1830,11 +1871,14 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client + .max_decoding_message_size(self.config().max_grpc_decoding_size.get_bytes() as usize); + let request = tonic::Request::new(request.into()); let mut response = self .block_on(client.unreceived_acks(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_unreceived_acknowledgements".to_owned()))? .into_inner(); response.sequences.sort_unstable(); @@ -1886,11 +1930,15 @@ impl ChainEndpoint for CosmosSdkChain { ) .map_err(Error::grpc_transport)?; + client = client.max_decoding_message_size( + self.config().max_grpc_decoding_size.get_bytes() as usize, + ); + let request = tonic::Request::new(request.into()); let response = self .block_on(client.next_sequence_receive(request)) - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_next_sequence_receive".to_owned()))? .into_inner(); Ok((Sequence::from(response.next_sequence_receive), None)) diff --git a/crates/relayer/src/chain/cosmos/query/account.rs b/crates/relayer/src/chain/cosmos/query/account.rs index 15cdd8f467..0a424d1693 100644 --- a/crates/relayer/src/chain/cosmos/query/account.rs +++ b/crates/relayer/src/chain/cosmos/query/account.rs @@ -5,6 +5,7 @@ use prost::Message; use tracing::info; use crate::chain::cosmos::types::account::Account; +use crate::config::default::max_grpc_decoding_size; use crate::error::Error; /// Get a `&mut Account` from an `&mut Option` if it is `Some(Account)`. @@ -57,6 +58,8 @@ pub async fn query_account( .await .map_err(Error::grpc_transport)?; + client = client.max_decoding_message_size(max_grpc_decoding_size().get_bytes() as usize); + let request = tonic::Request::new(QueryAccountRequest { address: account_address.to_string(), }); @@ -64,7 +67,11 @@ pub async fn query_account( let response = client.account(request).await; // Querying for an account might fail, i.e. if the account doesn't actually exist - let resp_account = match response.map_err(Error::grpc_status)?.into_inner().account { + let resp_account = match response + .map_err(|e| Error::grpc_status(e, "query_account".to_owned()))? + .into_inner() + .account + { Some(account) => account, None => return Err(Error::empty_query_account(account_address.to_string())), }; diff --git a/crates/relayer/src/chain/cosmos/query/balance.rs b/crates/relayer/src/chain/cosmos/query/balance.rs index 81292e534a..114bd27e74 100644 --- a/crates/relayer/src/chain/cosmos/query/balance.rs +++ b/crates/relayer/src/chain/cosmos/query/balance.rs @@ -4,7 +4,9 @@ use ibc_proto::cosmos::bank::v1beta1::{ query_client::QueryClient, QueryAllBalancesRequest, QueryBalanceRequest, }; -use crate::{account::Balance, error::Error}; +use crate::account::Balance; +use crate::config::default::max_grpc_decoding_size; +use crate::error::Error; /// Uses the GRPC client to retrieve the account balance for a specific denom pub async fn query_balance( @@ -16,6 +18,8 @@ pub async fn query_balance( .await .map_err(Error::grpc_transport)?; + client = client.max_decoding_message_size(max_grpc_decoding_size().get_bytes() as usize); + let request = tonic::Request::new(QueryBalanceRequest { address: account_address.to_string(), denom: denom.to_string(), @@ -25,7 +29,7 @@ pub async fn query_balance( .balance(request) .await .map(|r| r.into_inner()) - .map_err(Error::grpc_status)?; + .map_err(|e| Error::grpc_status(e, "query_balance".to_owned()))?; // Querying for a balance might fail, i.e. if the account doesn't actually exist let balance = response @@ -47,6 +51,8 @@ pub async fn query_all_balances( .await .map_err(Error::grpc_transport)?; + client = client.max_decoding_message_size(max_grpc_decoding_size().get_bytes() as usize); + let request = tonic::Request::new(QueryAllBalancesRequest { address: account_address.to_string(), pagination: None, @@ -56,7 +62,7 @@ pub async fn query_all_balances( .all_balances(request) .await .map(|r| r.into_inner()) - .map_err(Error::grpc_status)?; + .map_err(|e| Error::grpc_status(e, "query_all_balances".to_owned()))?; let balances = response .balances diff --git a/crates/relayer/src/chain/cosmos/query/consensus_state.rs b/crates/relayer/src/chain/cosmos/query/consensus_state.rs index db897a5de9..dbbe57f00f 100644 --- a/crates/relayer/src/chain/cosmos/query/consensus_state.rs +++ b/crates/relayer/src/chain/cosmos/query/consensus_state.rs @@ -3,12 +3,11 @@ use tracing::{debug, warn}; use ibc_relayer_types::{core::ics24_host::identifier::ChainId, Height}; -use crate::{ - chain::requests::{QueryConsensusStateHeightsRequest, QueryConsensusStatesRequest}, - consensus_state::AnyConsensusStateWithHeight, - error::Error, - util::pretty::{PrettyConsensusStateWithHeight, PrettyHeight}, -}; +use crate::chain::requests::{QueryConsensusStateHeightsRequest, QueryConsensusStatesRequest}; +use crate::config::default::max_grpc_decoding_size; +use crate::consensus_state::AnyConsensusStateWithHeight; +use crate::error::Error; +use crate::util::pretty::{PrettyConsensusStateWithHeight, PrettyHeight}; pub async fn query_consensus_state_heights( chain_id: &ChainId, @@ -40,6 +39,8 @@ pub async fn query_consensus_state_heights( .await .map_err(Error::grpc_transport)?; + client = client.max_decoding_message_size(max_grpc_decoding_size().get_bytes() as usize); + let grpc_request = tonic::Request::new(request.clone().into()); let grpc_response = client.consensus_state_heights(grpc_request).await; @@ -61,7 +62,9 @@ pub async fn query_consensus_state_heights( } } - let response = grpc_response.map_err(Error::grpc_status)?.into_inner(); + let response = grpc_response + .map_err(|e| Error::grpc_status(e, "query_consensus_state_heights".to_owned()))? + .into_inner(); let mut heights: Vec<_> = response .consensus_state_heights @@ -102,10 +105,12 @@ pub async fn query_consensus_states( .await .map_err(Error::grpc_transport)?; + client = client.max_decoding_message_size(max_grpc_decoding_size().get_bytes() as usize); + let response = client .consensus_states(tonic::Request::new(request.into())) .await - .map_err(Error::grpc_status)? + .map_err(|e| Error::grpc_status(e, "query_consensus_states".to_owned()))? .into_inner(); let mut consensus_states: Vec<_> = response diff --git a/crates/relayer/src/chain/cosmos/query/denom_trace.rs b/crates/relayer/src/chain/cosmos/query/denom_trace.rs index db18584a74..a0ef33cff0 100644 --- a/crates/relayer/src/chain/cosmos/query/denom_trace.rs +++ b/crates/relayer/src/chain/cosmos/query/denom_trace.rs @@ -4,7 +4,9 @@ use ibc_proto::ibc::applications::transfer::v1::{ query_client::QueryClient, QueryDenomTraceRequest, }; -use crate::{denom::DenomTrace, error::Error}; +use crate::config::default::max_grpc_decoding_size; +use crate::denom::DenomTrace; +use crate::error::Error; // Uses the GRPC client to retrieve the denom trace for a specific hash pub async fn query_denom_trace(grpc_address: &Uri, hash: &str) -> Result { @@ -12,6 +14,8 @@ pub async fn query_denom_trace(grpc_address: &Uri, hash: &str) -> Result Result Result { @@ -18,11 +19,13 @@ pub async fn send_tx_simulate(grpc_address: &Uri, tx: Tx) -> Result bool { false } + + pub fn max_grpc_decoding_size() -> Byte { + Byte::from_bytes(33554432) + } } #[derive(Clone, Debug, Default, Deserialize, Serialize)] @@ -472,6 +477,8 @@ pub struct ChainConfig { pub max_msg_num: MaxMsgNum, #[serde(default)] pub max_tx_size: MaxTxSize, + #[serde(default = "default::max_grpc_decoding_size")] + pub max_grpc_decoding_size: Byte, /// A correction parameter that helps deal with clocks that are only approximately synchronized /// between the source and destination chains for a client. @@ -593,6 +600,18 @@ mod tests { dbg!(config); } + #[test] + fn parse_valid_decoding_size_config() { + let path = concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/config/fixtures/relayer_conf_example_decoding_size.toml" + ); + + let config = load(path).expect("could not parse config"); + + dbg!(config); + } + #[test] fn serialize_valid_config() { let path = concat!( diff --git a/crates/relayer/src/error.rs b/crates/relayer/src/error.rs index c64a56f4a1..6194220614 100644 --- a/crates/relayer/src/error.rs +++ b/crates/relayer/src/error.rs @@ -94,8 +94,8 @@ define_error! { |_| { "gRPC error" }, GrpcStatus - { status: GrpcStatus } - |e| { format!("gRPC call failed with status: {0}", e.status) }, + { status: GrpcStatus, query: String } + |e| { format!("gRPC call `{}` failed with status: {1}", e.query, e.status) }, GrpcTransport [ TraceError ] diff --git a/crates/relayer/tests/config/fixtures/relayer_conf_example_decoding_size.toml b/crates/relayer/tests/config/fixtures/relayer_conf_example_decoding_size.toml new file mode 100644 index 0000000000..e6c51b578a --- /dev/null +++ b/crates/relayer/tests/config/fixtures/relayer_conf_example_decoding_size.toml @@ -0,0 +1,56 @@ +[global] +log_level = 'error' + +[mode] + +[mode.clients] +enabled = true +refresh = true +misbehaviour = true + +[mode.connections] +enabled = false + +[mode.channels] +enabled = false + +[mode.packets] +enabled = true +clear_interval = 100 +clear_on_start = true +tx_confirmation = true + +[[chains]] +id = 'chain_A' +rpc_addr = 'http://127.0.0.1:26657' +grpc_addr = 'http://127.0.0.1:9090' +websocket_addr = 'ws://localhost:26657/websocket' +rpc_timeout = '10s' +account_prefix = 'cosmos' +key_name = 'testkey' +store_prefix = 'ibc' +max_gas = 200000 +gas_price = { price = 0.001, denom = 'stake' } +max_msg_num = 4 +max_tx_size = 1048576 +max_grpc_decoding_size = '4MiB' +clock_drift = '5s' +trusting_period = '14days' +trust_threshold = { numerator = '1', denominator = '3' } +address_type = { derivation = 'cosmos' } + +[[chains]] +id = 'chain_B' +rpc_addr = 'http://127.0.0.1:26557' +grpc_addr = 'http://127.0.0.1:9090' +websocket_addr = 'ws://localhost:26557/websocket' +rpc_timeout = '10s' +account_prefix = 'cosmos' +key_name = 'testkey' +store_prefix = 'ibc' +gas_price = { price = 0.001, denom = 'stake' } +max_grpc_decoding_size = '5.91 MB' +clock_drift = '5s' +trusting_period = '14days' +trust_threshold = { numerator = '1', denominator = '3' } +address_type = { derivation = 'ethermint', proto_type = { pk_type = '/injective.crypto.v1beta1.ethsecp256k1.PubKey' } } \ No newline at end of file diff --git a/tools/test-framework/src/chain/ext/proposal.rs b/tools/test-framework/src/chain/ext/proposal.rs index 34cf204261..46321a5447 100644 --- a/tools/test-framework/src/chain/ext/proposal.rs +++ b/tools/test-framework/src/chain/ext/proposal.rs @@ -1,5 +1,6 @@ use eyre::eyre; use http::Uri; +use ibc_relayer::config::default::max_grpc_decoding_size; use prost::Message; use ibc_proto::cosmos::gov::v1beta1::{query_client::QueryClient, QueryProposalRequest}; @@ -59,13 +60,15 @@ pub async fn query_upgrade_proposal_height( } }; + client = client.max_decoding_message_size(max_grpc_decoding_size().get_bytes() as usize); + let request = tonic::Request::new(QueryProposalRequest { proposal_id }); let response = client .proposal(request) .await .map(|r| r.into_inner()) - .map_err(RelayerError::grpc_status)?; + .map_err(|e| RelayerError::grpc_status(e, "query_upgrade_proposal_height".to_owned()))?; // Querying for a balance might fail, i.e. if the account doesn't actually exist let proposal = response diff --git a/tools/test-framework/src/types/single/node.rs b/tools/test-framework/src/types/single/node.rs index 65a366c5d1..882a31d619 100644 --- a/tools/test-framework/src/types/single/node.rs +++ b/tools/test-framework/src/types/single/node.rs @@ -153,6 +153,7 @@ impl FullNode { fee_granter: None, max_msg_num: Default::default(), max_tx_size: Default::default(), + max_grpc_decoding_size: config::default::max_grpc_decoding_size(), max_block_time: Duration::from_secs(30), clock_drift: Duration::from_secs(5), trusting_period: Some(Duration::from_secs(14 * 24 * 3600)),