Skip to content

Commit dccc432

Browse files
Push cancelled messages to retry queue (#388)
Co-authored-by: Seun Lanlege <[email protected]>
1 parent a5aab62 commit dccc432

File tree

17 files changed

+268
-66
lines changed

17 files changed

+268
-66
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/pages/developers/network/relayer.mdx

+5
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,11 @@ consensus_state_id = "PARA"
364364
# This exists for testing and development,
365365
# misuse can cause the rpc to be overloaded with queries
366366
# initial_height = 1000
367+
# (Optional)
368+
# This provides the relayer with the precision for the fee token on this substrate chain
369+
# Needed for ensuring correct delivery fee estimation
370+
# It defaults to 6 which is the decimal for USDC and USDT on Polkadot parachains
371+
# fee_token_decimals = 6
367372
```
368373

369374
It is optional to provide the configuration option for any of the connected chains, The only consequence is your relayer will not deliver requests from the ommitted chain as it has no way of querying the associated fees for requests originating from this chain.

modules/pallets/ismp/rpc/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ pub struct Proof {
123123
pub fn runtime_error_into_rpc_error(e: impl std::fmt::Display) -> ErrorObjectOwned {
124124
ErrorObject::owned(
125125
9876, // no real reason for this value
126-
"Something wrong",
127-
Some(format!("{}", e)),
126+
format!("{}", e),
127+
None::<String>,
128128
)
129129
}
130130

tesseract/evm/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use sp_core::{bytes::from_hex, keccak_256, Pair, H160};
2121
use std::{sync::Arc, time::Duration};
2222
use tesseract_primitives::{
2323
queue::{start_pipeline, PipelineQueue},
24-
IsmpProvider, StateMachineUpdated, StreamError, TxReceipt,
24+
IsmpProvider, StateMachineUpdated, StreamError, TxResult,
2525
};
2626
use tx::handle_message_submission;
2727

@@ -144,7 +144,7 @@ pub struct EvmClient {
144144
>,
145145
>,
146146
/// Tx submission pipeline
147-
queue: Option<Arc<PipelineQueue<Vec<Message>, anyhow::Result<Vec<TxReceipt>>>>>,
147+
queue: Option<Arc<PipelineQueue<Vec<Message>, anyhow::Result<TxResult>>>>,
148148
}
149149

150150
impl EvmClient {

tesseract/evm/src/provider.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
abi::{beefy::BeefyConsensusState, EvmHost},
2+
abi::{beefy::BeefyConsensusState, erc_20::Erc20, EvmHost},
33
gas_oracle::is_orbit_chain,
44
state_comitment_key, EvmClient,
55
};
@@ -43,7 +43,7 @@ use sp_core::{H160, H256};
4343
use std::{collections::BTreeMap, sync::Arc, time::Duration};
4444
use tesseract_primitives::{
4545
wait_for_challenge_period, BoxStream, EstimateGasReturnParams, IsmpProvider, Query, Signature,
46-
StateMachineUpdated, StateProofQueryType, TxReceipt,
46+
StateMachineUpdated, StateProofQueryType, TxResult,
4747
};
4848

4949
#[async_trait::async_trait]
@@ -721,7 +721,7 @@ impl IsmpProvider for EvmClient {
721721
&self,
722722
messages: Vec<Message>,
723723
_coprocessor: StateMachine,
724-
) -> Result<Vec<TxReceipt>, Error> {
724+
) -> Result<TxResult, Error> {
725725
let queue = self
726726
.queue
727727
.as_ref()
@@ -850,6 +850,19 @@ impl IsmpProvider for EvmClient {
850850
fn max_concurrent_queries(&self) -> usize {
851851
self.config.tracing_batch_size.unwrap_or(10)
852852
}
853+
854+
async fn fee_token_decimals(&self) -> Result<u8, anyhow::Error> {
855+
let fee_token = match self.query_host_params(self.state_machine).await? {
856+
HostParam::EvmHostParam(params) => params.fee_token,
857+
_ => Err(anyhow!("Unexpected host params"))?,
858+
};
859+
860+
let contract = Erc20::new(fee_token, self.client.clone());
861+
862+
let decimals = contract.decimals().call().await?;
863+
864+
Ok(decimals)
865+
}
853866
}
854867

855868
pub enum CheckTraceForEventParams {

tesseract/evm/src/tx.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use pallet_ismp::offchain::{LeafIndexAndPos, Proof as MmrProof};
3434
use polkadot_sdk::sp_mmr_primitives::utils::NodesUtils;
3535
use primitive_types::{H256, U256};
3636
use std::{collections::BTreeSet, sync::Arc, time::Duration};
37-
use tesseract_primitives::{Hasher, Query, TxReceipt};
37+
use tesseract_primitives::{Hasher, Query, TxReceipt, TxResult};
3838

3939
use crate::gas_oracle::get_current_gas_cost_in_usd;
4040

@@ -49,9 +49,10 @@ type SolidityFunctionCall<T> = FunctionCall<
4949
pub async fn submit_messages(
5050
client: &EvmClient,
5151
messages: Vec<Message>,
52-
) -> anyhow::Result<BTreeSet<H256>> {
52+
) -> anyhow::Result<(BTreeSet<H256>, Vec<Message>)> {
5353
let calls = generate_contract_calls(client, messages.clone(), false).await?;
5454
let mut events = BTreeSet::new();
55+
let mut cancelled: Vec<Message> = vec![];
5556
for (index, call) in calls.into_iter().enumerate() {
5657
let gas_price = call.tx.gas_price();
5758
match call.clone().send().await {
@@ -71,6 +72,11 @@ pub async fn submit_messages(
7172
retry,
7273
)
7374
.await?;
75+
if matches!(messages[index], Message::Request(_) | Message::Response(_)) &&
76+
evs.is_empty()
77+
{
78+
cancelled.push(messages[index].clone())
79+
}
7480
events.extend(evs);
7581
},
7682
Err(err) => {
@@ -102,7 +108,7 @@ pub async fn submit_messages(
102108
log::trace!("Got {} receipts from executing on {:?}", events.len(), client.state_machine);
103109
}
104110

105-
Ok(events)
111+
Ok((events, cancelled))
106112
}
107113

108114
#[async_recursion::async_recursion]
@@ -192,7 +198,8 @@ where
192198
}
193199
}
194200

195-
Err(anyhow!("Transaction to {:?} was cancelled!", state_machine_clone))?
201+
log::error!("Transaction to {:?} was cancelled!", state_machine_clone);
202+
Ok(Default::default())
196203
}
197204
};
198205

@@ -427,8 +434,8 @@ pub fn get_chain_gas_limit(state_machine: StateMachine) -> u64 {
427434
pub async fn handle_message_submission(
428435
client: &EvmClient,
429436
messages: Vec<Message>,
430-
) -> Result<Vec<TxReceipt>, anyhow::Error> {
431-
let receipts = submit_messages(client, messages.clone()).await?;
437+
) -> Result<TxResult, anyhow::Error> {
438+
let (receipts, cancelled) = submit_messages(client, messages.clone()).await?;
432439
let height = client.client.get_block_number().await?.low_u64();
433440
let mut results = vec![];
434441
for msg in messages {
@@ -477,5 +484,5 @@ pub async fn handle_message_submission(
477484
}
478485
}
479486

480-
Ok(results)
487+
Ok(TxResult { receipts: results, unsuccessful: cancelled })
481488
}

tesseract/fees/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ impl TransactionPayment {
369369
/// Delete any unprofitable message
370370
pub async fn delete_unprofitable_messages(
371371
&self,
372-
unprofitable: Vec<i32>,
372+
unprofitable: impl IntoIterator<Item = i32>,
373373
) -> Result<(), anyhow::Error> {
374374
let actions = unprofitable
375375
.into_iter()

tesseract/integration-test/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ async fn create_clients(
134134
initial_height: None,
135135
max_concurent_queries: None,
136136
poll_interval: None,
137+
fee_token_decimals: None,
137138
};
138139

139140
let chain_b_config = SubstrateConfig {
@@ -148,6 +149,7 @@ async fn create_clients(
148149
initial_height: None,
149150
max_concurent_queries: None,
150151
poll_interval: None,
152+
fee_token_decimals: None,
151153
};
152154

153155
// setup state machines

tesseract/messaging/src/events.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -489,12 +489,16 @@ pub async fn return_successful_queries(
489489
return Ok((None, None))
490490
};
491491

492-
let mut fee_metadata: Cost = match msg {
493-
Message::Request(_) => og_source.query_request_fee_metadata(query.commitment).await?.into(),
494-
Message::Response(_) => og_source.query_response_fee_metadata(query.commitment).await?.into(),
492+
let fee_metadata = match msg {
493+
Message::Request(_) => og_source.query_request_fee_metadata(query.commitment).await?,
494+
Message::Response(_) => og_source.query_response_fee_metadata(query.commitment).await?,
495495
_ => Err(anyhow!("Unexpected message: {msg:?}"))?
496496
};
497497

498+
// normalize fee_metadata to 18 decimals since gas cost is calculated in 18 decimals
499+
let fee_token_decimal = og_source.fee_token_decimals().await?;
500+
let mut fee_metadata: Cost = (fee_metadata * U256::from(10u128.pow(18u32.saturating_sub(fee_token_decimal.into()) as u32))).into();
501+
498502
let profit = (U256::from(minimum_profit_percentage) *
499503
total_gas_to_be_expended_in_usd.0) /
500504
U256::from(100);

tesseract/messaging/src/lib.rs

+31-5
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use ismp::{consensus::StateMachineHeight, events::Event, host::StateMachine, rou
3535

3636
use tesseract_primitives::{
3737
config::RelayerConfig, observe_challenge_period, wait_for_state_machine_update,
38-
HandleGetResponse, HyperbridgeClaim, IsmpProvider, StateMachineUpdated, TxReceipt,
38+
HandleGetResponse, HyperbridgeClaim, IsmpProvider, StateMachineUpdated, TxReceipt, TxResult,
3939
};
4040
use transaction_fees::TransactionPayment;
4141

@@ -159,9 +159,13 @@ where
159159
hyperbridge,
160160
client_map,
161161
tx_payment,
162-
config,
162+
config.clone(),
163163
coprocessor,
164-
sender,
164+
if !config.disable_fee_accumulation.unwrap_or_default() {
165+
Some(sender)
166+
} else {
167+
None
168+
},
165169
)
166170
.await;
167171
tracing::error!("{name} terminated with result {res:?}");
@@ -353,7 +357,7 @@ async fn handle_update(
353357

354358
let res = chain_a.submit(messages.clone(), coprocessor).await;
355359
match res {
356-
Ok(receipts) => {
360+
Ok(TxResult { receipts, unsuccessful }) => {
357361
if let Some(sender) = fee_acc_sender {
358362
// We should not store messages when they are delivered to hyperbridge
359363
if chain_a.state_machine_id().state_id != coprocessor {
@@ -383,6 +387,25 @@ async fn handle_update(
383387
}
384388
}
385389
}
390+
391+
if !unsuccessful.is_empty() &&
392+
config.unprofitable_retry_frequency.is_some() &&
393+
chain_a.state_machine_id().state_id != coprocessor
394+
{
395+
tracing::error!(target: "tesseract", "Some transactions were cancelled and will be retried");
396+
tracing::trace!(target: "tesseract", "Persisting {} cancelled transactions going to {} to the db", unsuccessful.len(), chain_a.name());
397+
if let Err(err) = tx_payment
398+
.store_unprofitable_messages(
399+
unsuccessful,
400+
chain_a.state_machine_id().state_id,
401+
)
402+
.await
403+
{
404+
tracing::error!(
405+
"Encountered an error while cancelled messages inside the database {err:?}"
406+
)
407+
}
408+
}
386409
},
387410
Err(err) => {
388411
tracing::error!("Failed to submit transaction to {}: {err:?}", chain_a.name())
@@ -391,7 +414,10 @@ async fn handle_update(
391414
}
392415

393416
// Store currently unprofitable in messages in db
394-
if !unprofitable.is_empty() && config.unprofitable_retry_frequency.is_some() {
417+
if !unprofitable.is_empty() &&
418+
config.unprofitable_retry_frequency.is_some() &&
419+
chain_a.state_machine_id().state_id != coprocessor
420+
{
395421
tracing::trace!(target: "tesseract", "Persisting {} unprofitable messages going to {} to the db", unprofitable.len(), chain_a.name());
396422
if let Err(err) = tx_payment
397423
.store_unprofitable_messages(unprofitable, chain_a.state_machine_id().state_id)

0 commit comments

Comments
 (0)