Skip to content

Commit c517e5c

Browse files
authored
feat(submitter): Report reverted payload with success criteria (hyperlane-xyz#6170)
### Description - Generate contract calls to check if a particular message was delivered - Populate payload success criteria with serialized contract calls - Check if success criteria is met at finality stage of Lander ### Backward compatibility Yes ### Testing None --------- Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com>
1 parent a539ecc commit c517e5c

21 files changed

Lines changed: 186 additions & 82 deletions

File tree

rust/main/agents/relayer/src/msg/op_queue.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,10 @@ pub mod test {
384384
todo!()
385385
}
386386

387+
fn success_criteria(&self) -> ChainResult<Option<Vec<u8>>> {
388+
todo!()
389+
}
390+
387391
fn on_reprepare(
388392
&mut self,
389393
_err_msg: Option<String>,

rust/main/agents/relayer/src/msg/op_submitter.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,14 +661,30 @@ async fn submit_via_lander(
661661
}
662662
};
663663

664+
let operation_success_criteria = match op.success_criteria() {
665+
Ok(s) => s,
666+
Err(e) => {
667+
let reason = ReprepareReason::ErrorCreatingPayloadSuccessCriteria;
668+
let msg = "Error creating payload success criteria";
669+
prepare_op(op, prepare_queue, e, msg, reason).await;
670+
return;
671+
}
672+
};
673+
664674
let message_id = op.id();
665675
let metadata = format!("{message_id:?}");
666676
let mailbox = op
667677
.try_get_mailbox()
668678
.expect("Operation should contain Mailbox address")
669679
.address();
670680
let payload_id = PayloadId::random();
671-
let payload = FullPayload::new(payload_id, metadata, operation_payload, mailbox);
681+
let payload = FullPayload::new(
682+
payload_id,
683+
metadata,
684+
operation_payload,
685+
operation_success_criteria,
686+
mailbox,
687+
);
672688

673689
if let Err(e) = entrypoint.send_payload(&payload).await {
674690
let reason = ReprepareReason::ErrorSubmitting;

rust/main/agents/relayer/src/msg/pending_message.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,12 @@ impl PendingOperation for PendingMessage {
552552
Ok(payload)
553553
}
554554

555+
fn success_criteria(&self) -> ChainResult<Option<Vec<u8>>> {
556+
let mailbox = &self.ctx.destination_mailbox;
557+
let message = &self.message;
558+
mailbox.delivered_calldata(message.id())
559+
}
560+
555561
fn on_reprepare(
556562
&mut self,
557563
err: Option<String>,

rust/main/chains/hyperlane-cosmos-native/src/mailbox.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,8 @@ impl Mailbox for CosmosNativeMailbox {
182182
) -> ChainResult<Vec<u8>> {
183183
todo!() // we dont need this for now
184184
}
185+
186+
fn delivered_calldata(&self, _message_id: H256) -> ChainResult<Option<Vec<u8>>> {
187+
todo!()
188+
}
185189
}

rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::num::NonZeroU64;
21
use std::str::FromStr;
32

43
use async_trait::async_trait;
@@ -18,7 +17,7 @@ use crate::payloads::mailbox::{
1817
};
1918
use crate::types::tx_response_to_outcome;
2019
use crate::utils::get_block_height_for_reorg_period;
21-
use crate::{payloads, ConnectionConf, CosmosAddress, CosmosProvider, Signer};
20+
use crate::{payloads, ConnectionConf, CosmosAddress, CosmosProvider};
2221

2322
#[derive(Clone, Debug)]
2423
/// A reference to a Mailbox contract on some Cosmos chain
@@ -206,6 +205,10 @@ impl Mailbox for CosmosMailbox {
206205
) -> ChainResult<Vec<u8>> {
207206
todo!() // not required
208207
}
208+
209+
fn delivered_calldata(&self, message_id: H256) -> ChainResult<Option<Vec<u8>>> {
210+
todo!()
211+
}
209212
}
210213

211214
impl CosmosMailbox {

rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,16 @@ use ethers_contract::builders::ContractCall;
1414
use ethers_contract::{Multicall, MulticallResult};
1515
use ethers_core::utils::WEI_IN_ETHER;
1616
use futures_util::future::join_all;
17-
use hyperlane_core::rpc_clients::call_and_retry_indefinitely;
18-
use hyperlane_core::{BatchResult, QueueOperation, ReorgPeriod, H512};
1917
use tokio::join;
2018
use tokio::sync::Mutex;
2119
use tracing::instrument;
2220

2321
use hyperlane_core::{
24-
utils::bytes_to_hex, BatchItem, ChainCommunicationError, ChainResult, ContractLocator,
25-
HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage,
26-
HyperlaneProtocolError, HyperlaneProvider, Indexed, Indexer, LogMeta, Mailbox,
27-
RawHyperlaneMessage, SequenceAwareIndexer, TxCostEstimate, TxOutcome, H160, H256, U256,
22+
rpc_clients::call_and_retry_indefinitely, utils::bytes_to_hex, BatchItem, BatchResult,
23+
ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain,
24+
HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProtocolError,
25+
HyperlaneProvider, Indexed, Indexer, LogMeta, Mailbox, QueueOperation, RawHyperlaneMessage,
26+
ReorgPeriod, SequenceAwareIndexer, TxCostEstimate, TxOutcome, H160, H256, H512, U256,
2827
};
2928

3029
use crate::error::HyperlaneEthereumError;
@@ -693,6 +692,13 @@ where
693692
let data = (contract_call.tx, contract_call.function);
694693
serde_json::to_vec(&data).map_err(Into::into)
695694
}
695+
696+
fn delivered_calldata(&self, message_id: H256) -> ChainResult<Option<Vec<u8>>> {
697+
let call = self.contract.delivered(message_id.into());
698+
699+
let data = (call.tx, call.function);
700+
serde_json::to_vec(&data).map(Some).map_err(Into::into)
701+
}
696702
}
697703

698704
pub struct EthereumMailboxAbi;

rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,17 @@ pub struct EthereumProvider<M> {
3535

3636
impl<M> EthereumProvider<M> {
3737
/// Create a ContractCall object for a given transaction and function.
38-
pub fn build_contract_call(
38+
pub fn build_contract_call<D>(
3939
&self,
4040
tx: TypedTransaction,
4141
function: Function,
42-
) -> ContractCall<M, ()> {
42+
) -> ContractCall<M, D> {
4343
ContractCall {
4444
tx,
4545
function,
4646
block: None,
4747
client: self.provider.clone(),
48-
datatype: PhantomData::<()>,
48+
datatype: PhantomData::<D>,
4949
}
5050
}
5151
}
@@ -93,6 +93,9 @@ pub trait EvmProviderForSubmitter: Send + Sync {
9393

9494
/// Send transaction into blockchain
9595
async fn send(&self, tx: &TypedTransaction, function: &Function) -> ChainResult<H256>;
96+
97+
/// Read-only call into blockchain which returns a boolean
98+
async fn check(&self, tx: &TypedTransaction, function: &Function) -> ChainResult<bool>;
9699
}
97100

98101
#[async_trait]
@@ -133,20 +136,30 @@ where
133136
tx: &TypedTransaction,
134137
function: &Function,
135138
) -> Result<U256, ChainCommunicationError> {
136-
let contract_call = self.build_contract_call(tx.clone(), function.clone());
139+
let contract_call = self.build_contract_call::<()>(tx.clone(), function.clone());
137140
let gas_limit = contract_call.estimate_gas().await?.into();
138141
Ok(gas_limit)
139142
}
140143

141144
async fn send(&self, tx: &TypedTransaction, function: &Function) -> ChainResult<H256> {
142-
let contract_call = self.build_contract_call(tx.clone(), function.clone());
145+
let contract_call = self.build_contract_call::<()>(tx.clone(), function.clone());
143146
let pending = contract_call
144147
.send()
145148
.await
146149
.map_err(|e| ChainCommunicationError::CustomError(e.to_string()))?;
147150

148151
Ok(pending.tx_hash().into())
149152
}
153+
154+
async fn check(&self, tx: &TypedTransaction, function: &Function) -> ChainResult<bool> {
155+
let contract_call = self.build_contract_call::<bool>(tx.clone(), function.clone());
156+
let success = contract_call
157+
.call()
158+
.await
159+
.map_err(|e| ChainCommunicationError::CustomError(e.to_string()))?;
160+
161+
Ok(success)
162+
}
150163
}
151164

152165
#[async_trait]

rust/main/chains/hyperlane-fuel/src/mailbox.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ impl Mailbox for FuelMailbox {
220220
// Seems like this is not needed for Fuel, as it's only used in mocks
221221
todo!()
222222
}
223+
224+
fn delivered_calldata(&self, message_id: H256) -> ChainResult<Option<Vec<u8>>> {
225+
todo!()
226+
}
223227
}
224228

225229
/// Struct that retrieves event data for a Fuel Mailbox contract

rust/main/chains/hyperlane-sealevel/src/mailbox.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,4 +552,8 @@ impl Mailbox for SealevelMailbox {
552552
let process_instruction = self.get_process_instruction(message, metadata).await?;
553553
serde_json::to_vec(&process_instruction).map_err(Into::into)
554554
}
555+
556+
fn delivered_calldata(&self, _message_id: H256) -> ChainResult<Option<Vec<u8>>> {
557+
Ok(None)
558+
}
555559
}

rust/main/hyperlane-core/src/traits/mailbox.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ pub trait Mailbox: HyperlaneContract + Send + Sync + Debug {
7171
message: &HyperlaneMessage,
7272
metadata: &[u8],
7373
) -> ChainResult<Vec<u8>>;
74+
75+
/// Get the calldata for a call which allows to check if a particular messages was delivered
76+
fn delivered_calldata(&self, message_id: H256) -> ChainResult<Option<Vec<u8>>>;
7477
}
7578

7679
/// The result of processing a batch of messages

0 commit comments

Comments
 (0)