diff --git a/modules/hyperclient/hyperclient.d.ts b/modules/hyperclient/hyperclient.d.ts index ae92810fc..51596b77b 100644 --- a/modules/hyperclient/hyperclient.d.ts +++ b/modules/hyperclient/hyperclient.d.ts @@ -19,18 +19,20 @@ */ export function start(): void; -interface IConfig { +export type HexString = `0x{string}` | `0x${string}`; + +export interface IConfig { // confuration object for the source chain - source: IChainConfig; + source: IEvmConfig | ISubstrateConfig; // confuration object for the destination chain - dest: IChainConfig; + dest: IEvmConfig | ISubstrateConfig; // confuration object for hyperbridge hyperbridge: IHyperbridgeConfig; // Indexer url indexer: string; } -interface IChainConfig { +export interface IEvmConfig { // rpc url of the chain rpc_url: string; // state machine identifier as a string @@ -41,12 +43,21 @@ interface IChainConfig { consensus_state_id: string; } -interface IHyperbridgeConfig { +export interface ISubstrateConfig { + // rpc url of the chain + rpc_url: string; + // consensus state identifier of this chain on hyperbridge + consensus_state_id: string; + // consensus state identifier of this chain on hyperbridge + hash_algo: "Keccak" | "Blake2"; +} + +export interface IHyperbridgeConfig { // websocket rpc endpoint for hyperbridge rpc_url: string; } -interface IPostRequest { +export interface IPostRequest { // The source state machine of this request. source: string; // The destination state machine of this request. @@ -61,11 +72,9 @@ interface IPostRequest { body: string; // Timestamp which this request expires in seconds. timeoutTimestamp: bigint; - // Height at which this request was emitted on the source - txHeight: bigint; } -interface IGetRequest { +export interface IGetRequest { // The source state machine of this request. source: string; // The destination state machine of this request. @@ -87,14 +96,12 @@ interface IGetRequest { /// `` /// For fetching keys from EVM contracts each key should be 52 bytes /// This should be a concatenation of contract address and slot hash - keys: `0x{string}`[]; + keys: HexString[]; // Timestamp which this request expires in seconds. timeoutTimestamp: bigint; - // Height at which this request was emitted on the source - txHeight: bigint; } -interface IPostResponse { +export interface IPostResponse { // The request that triggered this response. post: IPostRequest; // The response message. @@ -103,157 +110,174 @@ interface IPostResponse { timeoutTimestamp: bigint; } -type MessageStatus = - | Pending - | SourceFinalized - | HyperbridgeDelivered - | HyperbridgeFinalized - | DestinationDelivered - | Timeout; - // This transaction is still pending on the source chain -interface Pending { +export interface Pending { kind: "Pending"; } // This event is emitted on hyperbridge -interface SourceFinalized { +export interface SourceFinalized { kind: "SourceFinalized"; } // This event is emitted on hyperbridge -interface HyperbridgeDelivered { - kind: "HyperbridgeDelivered"; +export interface HyperbridgeVerified { + kind: "HyperbridgeVerified"; } // This event is emitted on the destination chain -interface HyperbridgeFinalized { +export interface HyperbridgeFinalized { kind: "HyperbridgeFinalized"; } // This event is emitted on the destination chain -interface DestinationDelivered { +export interface DestinationDelivered { kind: "DestinationDelivered"; } // The request has now timed out -interface Timeout { +export interface Timeout { kind: "Timeout"; } // The request has now timed out -interface DestinationFinalized { +export interface DestinationFinalized { kind: "DestinationFinalized"; } // The request has now timed out -interface HyperbridgeTimedout { +export interface HyperbridgeTimedout { kind: "HyperbridgeTimedout"; } // The request has now timed out -interface HyperbridgeTimedout { +export interface HyperbridgeTimedout { kind: "HyperbridgeTimedout"; } +// The request timeout has been finalized by the destination +export interface DestinationFinalizedState { + // the height of the destination chain at which the time out was finalized + DestinationFinalized: bigint +} + +// Hyperbridge has finalized some state +export interface HyperbridgeFinalizedState { + // The height of the state commitment that was finalized + HyperbridgeFinalized: bigint +} + +// The source chain has finalized some state commitment +export interface SourceFinalizedState { + // The height of the source chain which was finalized + SourceFinalized: bigint +} + +// The message has been verified & aggregated to Hyperbridge +export interface HyperbridgeVerifiedState { + // Height at which the message was aggregated to Hyperbridge + HyperbridgeVerified: bigint +} + +// Initial state for a pending cross-chain message +export interface MessageDispatched { + // The height at which the message was dispatched from the source chain + Dispatched: bigint +} + +// The possible initial states of a timeout (Post request or response) stream +export type TimeoutStreamState = "Pending" | DestinationFinalizedState | HyperbridgeVerifiedState | HyperbridgeFinalizedState; + +// The possible initial states of a message status (Post request or response) stream +export type MessageStatusStreamState = MessageDispatched | SourceFinalizedState | HyperbridgeVerifiedState | HyperbridgeFinalizedState; + // The possible states of an inflight request -type MessageStatusWithMeta = +export type MessageStatusWithMeta = | SourceFinalizedWithMetadata - | HyperbridgeDeliveredWithMetadata + | HyperbridgeVerifiedWithMetadata | HyperbridgeFinalizedWithMetadata | DestinationDeliveredWithMetadata | Timeout | ErrorWithMetadata; // The possible states of a timed-out request -type TimeoutStatusWithMeta = +export type TimeoutStatusWithMeta = | DestinationFinalizedWithMetadata - | HyperbridgeTimedoutWithMetadata + | HyperbridgeVerifiedWithMetadata | HyperbridgeFinalizedWithMetadata | TimeoutMessage | ErrorWithMetadata; // This event is emitted on hyperbridge -interface SourceFinalizedWithMetadata { +export interface SourceFinalizedWithMetadata { kind: "SourceFinalized"; // Block height of the source chain that was finalized. finalized_height: bigint; // The hash of the block where the event was emitted - block_hash: `0x{string}`; + block_hash: HexString; // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; + transaction_hash: HexString; // The block number where the event was emitted block_number: bigint; } // This event is emitted on hyperbridge -interface HyperbridgeDeliveredWithMetadata { - kind: "HyperbridgeDelivered"; +export interface HyperbridgeVerifiedWithMetadata { + kind: "HyperbridgeVerified"; // The hash of the block where the event was emitted - block_hash: `0x{string}`; + block_hash: HexString; // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; + transaction_hash: HexString; // The block number where the event was emitted block_number: bigint; } // This event is emitted on the destination chain -interface HyperbridgeFinalizedWithMetadata { +export interface HyperbridgeFinalizedWithMetadata { kind: "HyperbridgeFinalized"; // Block height of hyperbridge chain that was finalized. finalized_height: bigint; // The hash of the block where the event was emitted - block_hash: `0x{string}`; + block_hash: HexString; // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; + transaction_hash: HexString; // The block number where the event was emitted block_number: bigint; // The transaction calldata which can be used for self-relay - calldata: `0x{string}`; -} - -// This event is emitted on hyperbridge -interface HyperbridgeTimedoutWithMetadata { - kind: "HyperbridgeTimedout"; - // The hash of the block where the event was emitted - block_hash: `0x{string}`; - // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; - // The block number where the event was emitted - block_number: bigint; + calldata: HexString; } // This event is emitted on the destination chain -interface DestinationDeliveredWithMetadata { +export interface DestinationDeliveredWithMetadata { kind: "DestinationDelivered"; // The hash of the block where the event was emitted - block_hash: `0x{string}`; + block_hash: HexString; // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; + transaction_hash: HexString; // The block number where the event was emitted block_number: bigint; } // This event is emitted on the destination chain -interface TimeoutMessage { +export interface TimeoutMessage { kind: "TimeoutMessage"; // encoded call for HandlerV1.handlePostRequestTimeouts - calldata: `0x{string}`; + calldata: HexString; } // This event is emitted on hyperbridge -interface DestinationFinalizedWithMetadata { +export interface DestinationFinalizedWithMetadata { kind: "DestinationFinalized"; // The hash of the block where the event was emitted - block_hash: `0x{string}`; + block_hash: HexString; // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; + transaction_hash: HexString; // The block number where the event was emitted block_number: bigint; } // An error was encountered in the stream, the stream will come to an end. -interface ErrorWithMetadata { +export interface ErrorWithMetadata { kind: "Error"; // error description description: string; @@ -265,49 +289,55 @@ interface ErrorWithMetadata { */ export class HyperClient { free(): void; + /** * Initialize the hyperclient * @param {IConfig} config * @returns {Promise} */ static init(config: IConfig): Promise; + /** - * Queries the status of a request and returns `MessageStatusWithMetadata` + * Queries the status of a POST request` * @param {IPostRequest} request * @returns {Promise} */ query_post_request_status( request: IPostRequest, ): Promise; + /** - * Queries the status of a request and returns `MessageStatusWithMetadata` + * Queries the status of a GET request` * @param {IGetRequest} request * @returns {Promise} */ query_get_request_status( request: IGetRequest, ): Promise; + /** - * Accepts a post response and returns a `MessageStatusWithMetadata` + * Queries the status of a POST response` * @param {IPostResponse} response * @returns {Promise} */ query_post_response_status( response: IPostResponse, ): Promise; + /** - * Return the status of a post request as a `ReadableStream` that yields - * `MessageStatusWithMeta` + * Return the status of a post request as a `ReadableStream`. If the stream terminates abruptly, + * perhaps as a result of some error, it can be resumed given some initial state. * @param {IPostRequest} request + * @param {MessageStatusStreamState} state * @returns {Promise>} */ post_request_status_stream( request: IPostRequest, + state: MessageStatusStreamState, ): Promise>; /** - * Return the status of a get request as a `ReadableStream` that yields - * `MessageStatusWithMeta` + * Return the status of a get request as a `ReadableStream` * @param {IGetRequest} request * @returns {Promise>} */ @@ -320,12 +350,18 @@ export class HyperClient { * `TimeoutStatus` This function will not check if the request has timed out, only call it * when you receive a `MesssageStatus::TimeOut` from `query_request_status` or * `request_status_stream`. The stream ends when once it yields a `TimeoutMessage` + * + * If the stream terminates abruptly, perhaps as a result of some error, it can be resumed given some initial state. + * * @param {IPostRequest} request + * @param {TimeoutStreamState} state * @returns {Promise>} */ timeout_post_request( request: IPostRequest, + state: TimeoutStreamState, ): Promise>; + /** * @returns {string | undefined} */ diff --git a/modules/hyperclient/package.json b/modules/hyperclient/package.json index b0bc11703..241ca96bc 100644 --- a/modules/hyperclient/package.json +++ b/modules/hyperclient/package.json @@ -1,7 +1,7 @@ { "name": "@polytope-labs/hyperclient", "description": "The hyperclient is a library for managing (in-flight) ISMP requests", - "version": "0.5.2", + "version": "0.6.2", "author": "Polytope Labs (hello@polytope.technology)", "license": "Apache-2.0", "bugs": { diff --git a/modules/hyperclient/src/indexing.rs b/modules/hyperclient/src/indexing.rs index d7812e1be..5be7bd9d2 100644 --- a/modules/hyperclient/src/indexing.rs +++ b/modules/hyperclient/src/indexing.rs @@ -286,7 +286,7 @@ pub async fn query_request_status_from_indexer( calldata: calldata.into(), } } else { - MessageStatusWithMetadata::HyperbridgeDelivered { + MessageStatusWithMetadata::HyperbridgeVerified { meta: EventMetadata { block_hash: H256::from_slice(&from_hex(&block_hash)?), transaction_hash: H256::from_slice(&from_hex(&transaction_hash)?), @@ -430,7 +430,7 @@ pub async fn query_response_status_from_indexer( calldata: calldata.into(), } } else { - MessageStatusWithMetadata::HyperbridgeDelivered { + MessageStatusWithMetadata::HyperbridgeVerified { meta: EventMetadata { block_hash: H256::from_slice(&from_hex(&block_hash)?), transaction_hash: H256::from_slice(&from_hex(&transaction_hash)?), diff --git a/modules/hyperclient/src/interfaces.rs b/modules/hyperclient/src/interfaces.rs index a1e8072c4..b2c0386f5 100644 --- a/modules/hyperclient/src/interfaces.rs +++ b/modules/hyperclient/src/interfaces.rs @@ -24,20 +24,34 @@ use primitive_types::H160; use serde::{Deserialize, Serialize}; use sp_core::bytes::{from_hex, FromHexError}; +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum JsChainConfig { + Evm(JsEvmConfig), + Substrate(JsSubstrateConfig), +} + #[derive(Clone, Eq, PartialEq, Debug, Default, Deserialize, Serialize)] -pub struct JsChainConfig { +pub struct JsEvmConfig { pub rpc_url: String, pub state_machine: String, pub host_address: String, pub consensus_state_id: String, } +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] +pub struct JsSubstrateConfig { + pub rpc_url: String, + pub consensus_state_id: String, + pub hash_algo: HashAlgorithm, +} + #[derive(Clone, Eq, PartialEq, Debug, Default, Deserialize, Serialize)] pub struct JsHyperbridgeConfig { pub rpc_url: String, } -#[derive(Clone, Eq, PartialEq, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] pub struct JsClientConfig { pub source: JsChainConfig, pub dest: JsChainConfig, @@ -49,8 +63,8 @@ impl TryFrom for ClientConfig { type Error = anyhow::Error; fn try_from(value: JsClientConfig) -> Result { - let to_config = |val: &JsChainConfig| { - if !val.host_address.is_empty() { + let to_config = |val: &JsChainConfig| match val { + JsChainConfig::Evm(val) => { let state_machine = if val.state_machine.starts_with("0x") { let bytes = from_hex(&val.state_machine).map_err(|err| anyhow!("Hex: {err:?}"))?; @@ -80,22 +94,26 @@ impl TryFrom for ClientConfig { }; Ok::<_, anyhow::Error>(ChainConfig::Evm(conf)) - } else { + }, + JsChainConfig::Substrate(val) => { let conf = SubstrateConfig { rpc_url: val.rpc_url.clone(), consensus_state_id: { if val.consensus_state_id.len() != 4 { - Err(anyhow!("Invalid consensus state id"))? + Err(anyhow!( + "Invalid consensus state id: {:?}", + val.consensus_state_id + ))? } let mut dest = [0u8; 4]; dest.copy_from_slice(&val.consensus_state_id.as_bytes()); dest }, - hash_algo: HashAlgorithm::Keccak, + hash_algo: val.hash_algo, }; Ok(ChainConfig::Substrate(conf)) - } + }, }; let indexer = if value.indexer.is_empty() { @@ -139,9 +157,6 @@ pub struct JsPost { pub timeout_timestamp: u64, /// Encoded Request. pub body: String, - /// Height at which this request was emitted on the source chain - #[serde(rename = "txHeight")] - pub tx_height: u64, } impl TryFrom for PostRequest { @@ -204,9 +219,6 @@ pub struct JsGet { pub timeout_timestamp: u64, /// Some application-specific metadata relating to this request pub context: String, - /// Height at which this request was emitted on the source chain - #[serde(rename = "txHeight")] - pub tx_height: u64, } impl TryFrom for GetRequest { @@ -276,7 +288,9 @@ impl TryFrom for PostResponse { #[cfg(test)] mod tests { use crate::{ - interfaces::{JsChainConfig, JsClientConfig, JsHyperbridgeConfig, JsPost, JsPostResponse}, + interfaces::{ + JsChainConfig, JsClientConfig, JsEvmConfig, JsHyperbridgeConfig, JsPost, JsPostResponse, + }, types::{ChainConfig, ClientConfig, EvmConfig, HashAlgorithm, SubstrateConfig}, }; use ethers::prelude::H160; @@ -285,6 +299,7 @@ mod tests { host::StateMachine, router::{PostRequest, PostResponse}, }; + const OP_HOST: H160 = H160(hex!("1B58A47e61Ca7604b634CBB00b4e275cCd7c9E95")); const BSC_HOST: H160 = H160(hex!("022DDE07A21d8c553978b006D93CDe68ac83e677")); @@ -316,14 +331,14 @@ mod tests { indexer: Some("http://localhost:3000/".to_string()), }; - let js_source = JsChainConfig { + let js_source = JsEvmConfig { rpc_url: "https://127.0.0.1:9990".to_string(), state_machine: "EVM-97".to_string(), host_address: hex::encode(&BSC_HOST.0), consensus_state_id: "BSC0".to_string(), }; - let js_dest = JsChainConfig { + let js_dest = JsEvmConfig { rpc_url: "https://127.0.0.1:9990".to_string(), state_machine: "EVM-11155420".to_string(), host_address: hex::encode(&OP_HOST.0), @@ -333,8 +348,8 @@ mod tests { let js_hyperbridge = JsHyperbridgeConfig { rpc_url: "ws://127.0.0.1:9990".to_string() }; let js_client_conf = JsClientConfig { - source: js_source, - dest: js_dest, + source: JsChainConfig::Evm(js_source), + dest: JsChainConfig::Evm(js_dest), hyperbridge: js_hyperbridge, indexer: "http://localhost:3000/".to_string(), }; @@ -367,7 +382,6 @@ mod tests { to: hex::encode(vec![15; 20]), timeout_timestamp: 1_600_000, body: hex::encode(vec![40; 256]), - tx_height: 0, }, response: vec![80; 256], timeout_timestamp: 4_500_000, diff --git a/modules/hyperclient/src/internals/get_request.rs b/modules/hyperclient/src/internals/get_request.rs index 9e4696b82..47a0df435 100644 --- a/modules/hyperclient/src/internals/get_request.rs +++ b/modules/hyperclient/src/internals/get_request.rs @@ -35,7 +35,7 @@ pub async fn query_get_request_status( let relayer = client.hyperbridge.query_request_receipt(commitment).await?; if relayer != Default::default() { // request has been handled by hyperbridge - return Ok(MessageStatusWithMetadata::HyperbridgeDelivered { meta: Default::default() }) + return Ok(MessageStatusWithMetadata::HyperbridgeVerified { meta: Default::default() }) } let timestamp = client.hyperbridge.latest_timestamp().await?.as_secs(); @@ -51,7 +51,7 @@ pub async fn query_get_request_status( pub async fn get_request_status_stream( hyperclient: &HyperClient, get: GetRequest, - tx_height: u64, + intial_state: MessageStatusStreamState, ) -> Result, anyhow::Error> { let source_client = if get.source == hyperclient.dest.state_machine_id().state_id { hyperclient.dest.clone() @@ -64,14 +64,14 @@ pub async fn get_request_status_stream( let hyperbridge_client = hyperclient.hyperbridge.clone(); let commitment = hash_request::(&Request::Get(get.clone())); - let stream = stream::unfold(MessageStatusStreamState::Pending, move |post_request_status| { + let stream = stream::unfold(intial_state, move |post_request_status| { let hyperbridge_client = hyperbridge_client.clone(); let source_client = source_client.clone(); async move { let lambda = || async { match post_request_status { - MessageStatusStreamState::Pending => { + MessageStatusStreamState::Dispatched(tx_height) => { // watch for the finalization of the get request let mut update_stream = hyperbridge_client .state_machine_update_notification(source_client.state_machine_id()) @@ -98,7 +98,7 @@ pub async fn get_request_status_stream( return Ok(Some(( Err(anyhow!( "Encountered an error {:?}: in {:?}", - MessageStatusStreamState::Pending, + MessageStatusStreamState::Dispatched(tx_height), e )), post_request_status, @@ -115,10 +115,10 @@ pub async fn get_request_status_stream( match event { Ok(event) => { return Ok(Some(( - Ok(MessageStatusWithMetadata::HyperbridgeDelivered { + Ok(MessageStatusWithMetadata::HyperbridgeVerified { meta: event.meta.clone(), }), - MessageStatusStreamState::HyperbridgeDelivered( + MessageStatusStreamState::HyperbridgeVerified( event.meta.block_number, ), ))); @@ -130,7 +130,7 @@ pub async fn get_request_status_stream( } Ok(None) }, - MessageStatusStreamState::HyperbridgeDelivered(height) => { + MessageStatusStreamState::HyperbridgeVerified(height) => { let mut stream = source_client .state_machine_update_notification( hyperbridge_client.state_machine_id(), @@ -182,7 +182,7 @@ pub async fn get_request_status_stream( return Ok(Some(( Err(anyhow!( "Encountered an error {:?}: in {:?}", - MessageStatusStreamState::HyperbridgeDelivered(height), + MessageStatusStreamState::HyperbridgeVerified(height), e )), post_request_status, diff --git a/modules/hyperclient/src/internals/post_request.rs b/modules/hyperclient/src/internals/post_request.rs index 60186a02c..6d6414ef6 100644 --- a/modules/hyperclient/src/internals/post_request.rs +++ b/modules/hyperclient/src/internals/post_request.rs @@ -17,7 +17,10 @@ use crate::{ indexing::query_request_status_from_indexer, internals::encode_request_message_and_wait_for_challenge_period, providers::interface::{wait_for_challenge_period, Client}, - types::{BoxStream, MessageStatusStreamState, MessageStatusWithMetadata, TimeoutStatus}, + types::{ + BoxStream, MessageStatusStreamState, MessageStatusWithMetadata, TimeoutStatus, + TimeoutStreamState, + }, HyperClient, Keccak256, }; use anyhow::anyhow; @@ -30,19 +33,6 @@ use ismp::{ }; use primitive_types::H160; -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum TimeoutStreamState { - Pending, - /// Destination state machine has been finalized on hyperbridge - DestinationFinalized(u64), - /// Message has been timed out on hyperbridge - HyperbridgeTimedout(u64), - /// Hyperbridge has been finalized on source chain - HyperbridgeFinalized(u64), - /// Stream has ended - End, -} - /// `query_request_status_internal` is an internal function that /// checks the status of a message pub async fn query_post_request_status_internal( @@ -84,7 +74,7 @@ pub async fn query_post_request_status_internal( let relayer = client.hyperbridge.query_request_receipt(hash).await?; if relayer != H160::zero() { - return Ok(MessageStatusWithMetadata::HyperbridgeDelivered { meta: Default::default() }); + return Ok(MessageStatusWithMetadata::HyperbridgeVerified { meta: Default::default() }); } if hyperbridge_current_timestamp.as_secs() > post.timeout().as_secs() { @@ -99,7 +89,7 @@ pub async fn query_post_request_status_internal( pub async fn post_request_status_stream( hyperclient: &HyperClient, post: PostRequest, - post_request_height: u64, + state: MessageStatusStreamState, ) -> Result, anyhow::Error> { let source_client = if post.source == hyperclient.dest.state_machine_id().state_id { hyperclient.dest.clone() @@ -118,7 +108,7 @@ pub async fn post_request_status_stream( let hyperbridge_client = hyperclient.hyperbridge.clone(); let hyperclient_clone = hyperclient.clone(); - let stream = stream::unfold(MessageStatusStreamState::Pending, move |post_request_status| { + let stream = stream::unfold(state, move |post_request_status| { let dest_client = dest_client.clone(); let hyperbridge_client = hyperbridge_client.clone(); let source_client = source_client.clone(); @@ -129,7 +119,7 @@ pub async fn post_request_status_stream( async move { let lambda = || async { match post_request_status { - MessageStatusStreamState::Pending => { + MessageStatusStreamState::Dispatched(post_request_height) => { let destination_current_timestamp = dest_client.query_timestamp().await?; let relayer_address = dest_client.query_request_receipt(hash).await?; @@ -157,7 +147,7 @@ pub async fn post_request_status_stream( ), ))); }, - MessageStatusWithMetadata::HyperbridgeDelivered { meta } => { + MessageStatusWithMetadata::HyperbridgeVerified { meta } => { return Ok::< Option<( Result<_, anyhow::Error>, @@ -166,7 +156,7 @@ pub async fn post_request_status_stream( anyhow::Error, >(Some(( Ok(msg_status.clone()), - MessageStatusStreamState::HyperbridgeDelivered( + MessageStatusStreamState::HyperbridgeVerified( meta.block_number, ), ))); @@ -235,10 +225,10 @@ pub async fn post_request_status_stream( Option<(Result<_, anyhow::Error>, MessageStatusStreamState)>, anyhow::Error, >(Some(( - Ok(MessageStatusWithMetadata::HyperbridgeDelivered { + Ok(MessageStatusWithMetadata::HyperbridgeVerified { meta: Default::default(), }), - MessageStatusStreamState::HyperbridgeDelivered( + MessageStatusStreamState::HyperbridgeVerified( hyperbridge_client.query_latest_block_height().await?, ), ))); @@ -279,7 +269,9 @@ pub async fn post_request_status_stream( return Ok(Some(( Err(anyhow!( "Encountered an error {:?}: in {:?}", - MessageStatusStreamState::Pending, + MessageStatusStreamState::Dispatched( + post_request_height + ), e )), post_request_status, @@ -299,7 +291,7 @@ pub async fn post_request_status_stream( .flatten() { match msg_status { - MessageStatusWithMetadata::HyperbridgeDelivered { meta } => { + MessageStatusWithMetadata::HyperbridgeVerified { meta } => { return Ok::< Option<( Result<_, anyhow::Error>, @@ -308,7 +300,7 @@ pub async fn post_request_status_stream( anyhow::Error, >(Some(( Ok(msg_status.clone()), - MessageStatusStreamState::HyperbridgeDelivered( + MessageStatusStreamState::HyperbridgeVerified( meta.block_number, ), ))); @@ -363,10 +355,10 @@ pub async fn post_request_status_stream( }); return Ok(Some(( - Ok(MessageStatusWithMetadata::HyperbridgeDelivered { + Ok(MessageStatusWithMetadata::HyperbridgeVerified { meta: meta.unwrap_or_default(), }), - MessageStatusStreamState::HyperbridgeDelivered( + MessageStatusStreamState::HyperbridgeVerified( meta.map(|m| m.block_number).unwrap_or(latest_height), ), ))); @@ -378,10 +370,10 @@ pub async fn post_request_status_stream( match event { Ok(event) => { return Ok(Some(( - Ok(MessageStatusWithMetadata::HyperbridgeDelivered { + Ok(MessageStatusWithMetadata::HyperbridgeVerified { meta: event.meta.clone(), }), - MessageStatusStreamState::HyperbridgeDelivered( + MessageStatusStreamState::HyperbridgeVerified( event.meta.block_number, ), ))); @@ -394,7 +386,7 @@ pub async fn post_request_status_stream( Ok(None) }, - MessageStatusStreamState::HyperbridgeDelivered(height) => { + MessageStatusStreamState::HyperbridgeVerified(height) => { let res = dest_client.query_request_receipt(hash).await?; if let Some(ref msg_status) = @@ -539,7 +531,7 @@ pub async fn post_request_status_stream( return Ok(Some(( Err(anyhow!( "Encountered an error {:?}: in {:?}", - MessageStatusStreamState::HyperbridgeDelivered(height), + MessageStatusStreamState::HyperbridgeVerified(height), e )), post_request_status, @@ -637,6 +629,7 @@ pub async fn post_request_status_stream( pub async fn timeout_post_request_stream( hyperclient: &HyperClient, post: PostRequest, + state: TimeoutStreamState, ) -> Result, anyhow::Error> { let source_client = if post.source == hyperclient.dest.state_machine_id().state_id { hyperclient.dest.clone() @@ -654,7 +647,7 @@ pub async fn timeout_post_request_stream( }; let hyperbridge_client = hyperclient.hyperbridge.clone(); - let stream = stream::unfold(TimeoutStreamState::Pending, move |state| { + let stream = stream::unfold(state, move |state| { let dest_client = dest_client.clone(); let hyperbridge_client = hyperbridge_client.clone(); let source_client = source_client.clone(); @@ -727,8 +720,8 @@ pub async fn timeout_post_request_stream( } else { let height = hyperbridge_client.query_latest_block_height().await?; Ok(Some(( - Ok(TimeoutStatus::HyperbridgeTimedout { meta: Default::default() }), - TimeoutStreamState::HyperbridgeTimedout(height), + Ok(TimeoutStatus::HyperbridgeVerified { meta: Default::default() }), + TimeoutStreamState::HyperbridgeVerified(height), ))) } }, @@ -757,11 +750,11 @@ pub async fn timeout_post_request_stream( .await?; let meta = hyperbridge_client.submit(message).await?; Ok(Some(( - Ok(TimeoutStatus::HyperbridgeTimedout { meta }), - TimeoutStreamState::HyperbridgeTimedout(meta.block_number), + Ok(TimeoutStatus::HyperbridgeVerified { meta }), + TimeoutStreamState::HyperbridgeVerified(meta.block_number), ))) }, - TimeoutStreamState::HyperbridgeTimedout(hyperbridge_height) => { + TimeoutStreamState::HyperbridgeVerified(hyperbridge_height) => { let latest_hyperbridge_height = source_client .query_latest_state_machine_height( hyperbridge_client.state_machine_id(), diff --git a/modules/hyperclient/src/internals/post_response.rs b/modules/hyperclient/src/internals/post_response.rs index 092df9a71..957afe2b2 100644 --- a/modules/hyperclient/src/internals/post_response.rs +++ b/modules/hyperclient/src/internals/post_response.rs @@ -61,7 +61,7 @@ pub async fn query_response_status_internal( let relayer = hyperclient.hyperbridge.query_response_receipt(req_hash).await?; if relayer != H160::zero() { - return Ok(MessageStatusWithMetadata::HyperbridgeDelivered { meta: Default::default() }); + return Ok(MessageStatusWithMetadata::HyperbridgeVerified { meta: Default::default() }); } let hyperbridge_current_timestamp = hyperclient.hyperbridge.latest_timestamp().await?; diff --git a/modules/hyperclient/src/lib.rs b/modules/hyperclient/src/lib.rs index a7b89eea8..58cef13df 100644 --- a/modules/hyperclient/src/lib.rs +++ b/modules/hyperclient/src/lib.rs @@ -28,7 +28,7 @@ pub mod interfaces; extern crate alloc; extern crate core; -use crate::types::ClientConfig; +use crate::types::{ClientConfig, MessageStatusStreamState, TimeoutStreamState}; use crate::{ interfaces::{JsClientConfig, JsGet, JsPost, JsPostResponse}, @@ -50,277 +50,19 @@ pub mod indexing; #[cfg(test)] mod tests; -#[wasm_bindgen(typescript_custom_section)] -const ICONFIG: &'static str = r#" -interface IConfig { - // confuration object for the source chain - source: IChainConfig; - // confuration object for the destination chain - dest: IChainConfig; - // confuration object for hyperbridge - hyperbridge: IHyperbridgeConfig; - // Indexer url - indexer: string; -} - -interface IChainConfig { - // rpc url of the chain - rpc_url: string; - // state machine identifier as a string - state_machine: string; - // contract address of the `IsmpHost` on this chain - host_address: Uint8Array; - // contract address of the `IHandler` on this chain - handler_address: Uint8Array; - // consensus state identifier of this chain on hyperbridge - consensus_state_id: Uint8Array; -} - -interface IHyperbridgeConfig { - // websocket rpc endpoint for hyperbridge - rpc_url: string; -} - -interface IPostRequest { - // The source state machine of this request. - source: string; - // The destination state machine of this request. - dest: string; - // Module Id of the sending module - from: string; - // Module ID of the receiving module - to: string; - // The nonce of this request on the source chain - nonce: bigint; - // Encoded request body. - data: string; - // Timestamp which this request expires in seconds. - timeoutTimestamp: bigint; - // Height at which this request was emitted on the source - txHeight: bigint; -} - -interface IGetRequest { - // The source state machine of this request. - source: string; - // The destination state machine of this request. - dest: string; - // Module Id of the sending module - from: string; - // The nonce of this request on the source chain - nonce: bigint; - // Height at which to read the state machine. - height: bigint; - /// Raw Storage keys that would be used to fetch the values from the counterparty - /// For deriving storage keys for ink contract fields follow the guide in the link below - /// `` - /// The algorithms for calculating raw storage keys for different substrate pallet storage - /// types are described in the following links - /// `` - /// `` - /// `` - /// `` - /// For fetching keys from EVM contracts each key should be 52 bytes - /// This should be a concatenation of contract address and slot hash - keys: string[]; - // Timestamp which this request expires in seconds. - timeoutTimestamp: bigint; - // Height at which this request was emitted on the source - txHeight: bigint; -} - -interface IPostResponse { - // The request that triggered this response. - post: IPostRequest; - // The response message. - response: Uint8Array; - // Timestamp at which this response expires in seconds. - timeout_timestamp: bigint; -} - -type MessageStatus = - | Pending - | SourceFinalized - | HyperbridgeDelivered - | HyperbridgeFinalized - | DestinationDelivered - | Timeout; - -// This transaction is still pending on the source chain -interface Pending { - kind: "Pending"; -} - -// This event is emitted on hyperbridge -interface SourceFinalized { - kind: "SourceFinalized"; -} - -// This event is emitted on hyperbridge -interface HyperbridgeDelivered { - kind: "HyperbridgeDelivered"; -} - -// This event is emitted on the destination chain -interface HyperbridgeFinalized { - kind: "HyperbridgeFinalized"; -} - -// This event is emitted on the destination chain -interface DestinationDelivered { - kind: "DestinationDelivered"; -} - -// The request has now timed out -interface Timeout { - kind: "Timeout"; -} - -// The request has now timed out -interface DestinationFinalized { - kind: "DestinationFinalized"; -} - -// The request has now timed out -interface HyperbridgeTimedout { - kind: "HyperbridgeTimedout"; -} - - -// The request has now timed out -interface HyperbridgeTimedout { - kind: "HyperbridgeTimedout"; -} - -// The possible states of an inflight request -type MessageStatusWithMeta = - | SourceFinalizedWithMetadata - | HyperbridgeDeliveredWithMetadata - | HyperbridgeFinalizedWithMetadata - | DestinationDeliveredWithMetadata - | Timeout - | ErrorWithMetadata; - -// The possible states of a timed-out request -type TimeoutStatusWithMeta = - | DestinationFinalizedWithMetadata - | HyperbridgeTimedoutWithMetadata - | HyperbridgeFinalizedWithMetadata - | TimeoutMessage - | ErrorWithMetadata; - - -// This event is emitted on hyperbridge -interface SourceFinalizedWithMetadata { - kind: "SourceFinalized"; - // Block height of the source chain that was finalized. - finalized_height: bigint; - // The hash of the block where the event was emitted - block_hash: `0x{string}`; - // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; - // The block number where the event was emitted - block_number: bigint; -} - -// This event is emitted on hyperbridge -interface HyperbridgeDeliveredWithMetadata { - kind: "HyperbridgeDelivered"; - // The hash of the block where the event was emitted - block_hash: `0x{string}`; - // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; - // The block number where the event was emitted - block_number: bigint; -} - -// This event is emitted on the destination chain -interface HyperbridgeFinalizedWithMetadata { - kind: "HyperbridgeFinalized"; - // Block height of hyperbridge chain that was finalized. - finalized_height: bigint; - // The hash of the block where the event was emitted - block_hash: `0x{string}`; - // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; - // The block number where the event was emitted - block_number: bigint; -} - -// This event is emitted on hyperbridge -interface HyperbridgeTimedoutWithMetadata { - kind: "HyperbridgeTimedout"; - // The hash of the block where the event was emitted - block_hash: `0x{string}`; - // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; - // The block number where the event was emitted - block_number: bigint; -} - -// This event is emitted on the destination chain -interface DestinationDeliveredWithMetadata { - kind: "DestinationDelivered"; - // The hash of the block where the event was emitted - block_hash: `0x{string}`; - // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; - // The block number where the event was emitted - block_number: bigint; -} - -// This event is emitted on the destination chain -interface TimeoutMessage { - kind: "TimeoutMessage"; - // encoded call for HandlerV1.handlePostRequestTimeouts - calldata: Uint8Array, -} - -// This event is emitted on hyperbridge -interface DestinationFinalizedWithMetadata { - kind: "DestinationFinalized"; - // The hash of the block where the event was emitted - block_hash: `0x{string}`; - // The hash of the extrinsic responsible for the event - transaction_hash: `0x{string}`; - // The block number where the event was emitted - block_number: bigint; -} - - -// An error was encountered in the stream, the stream will come to an end. -interface ErrorWithMetadata { - kind: "Error"; - // error description - description: string -} -"#; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "IConfig")] - pub type IConfig; - - #[wasm_bindgen(typescript_type = "IPostRequest")] - pub type IPostRequest; - - #[wasm_bindgen(typescript_type = "IPostResponse")] - pub type IPostResponse; - - #[wasm_bindgen(typescript_type = "IGetRequest")] - pub type IGetRequest; -} - /// The hyperclient, allows the clients of hyperbridge to manage their in-flight ISMP requests /// across multiple chains. #[wasm_bindgen] #[derive(Clone)] pub struct HyperClient { + /// Internal client for the source chain #[wasm_bindgen(skip)] pub source: AnyClient, #[wasm_bindgen(skip)] + /// Internal client for the destination chain pub dest: AnyClient, #[wasm_bindgen(skip)] + /// Internal client for Hyperbridge pub hyperbridge: SubstrateClient, #[wasm_bindgen(skip)] pub indexer: Option, @@ -346,24 +88,21 @@ impl HyperClient { #[wasm_bindgen] impl HyperClient { /// Initialize the hyperclient - pub async fn init(config: IConfig) -> Result { + pub async fn init(config: JsValue) -> Result { let lambda = || async move { - let config = serde_wasm_bindgen::from_value::(config.into()).unwrap(); + let config = serde_wasm_bindgen::from_value::(config).unwrap(); let config: ClientConfig = config.try_into()?; HyperClient::new(config).await }; lambda().await.map_err(|err: anyhow::Error| { - JsError::new(&format!("Could not create hyperclient {err:?}")) + JsError::new(&format!("Could not create hyperclient: {err:?}")) }) } /// Queries the status of a request and returns `MessageStatusWithMetadata` - pub async fn query_post_request_status( - &self, - request: IPostRequest, - ) -> Result { + pub async fn query_post_request_status(&self, request: JsValue) -> Result { let lambda = || async move { let post = serde_wasm_bindgen::from_value::(request.into()).unwrap(); let post: PostRequest = post.try_into()?; @@ -381,7 +120,7 @@ impl HyperClient { } /// Queries the status of a request and returns `MessageStatusWithMetadata` - pub async fn query_get_request_status(&self, request: IGetRequest) -> Result { + pub async fn query_get_request_status(&self, request: JsValue) -> Result { let lambda = || async move { let get = serde_wasm_bindgen::from_value::(request.into()).unwrap(); let get: GetRequest = get.try_into()?; @@ -399,12 +138,9 @@ impl HyperClient { } /// Accepts a post response and returns a `MessageStatusWithMetadata` - pub async fn query_post_response_status( - &self, - response: IPostResponse, - ) -> Result { + pub async fn query_post_response_status(&self, response: JsValue) -> Result { let lambda = || async move { - let post = serde_wasm_bindgen::from_value::(response.into()).unwrap(); + let post = serde_wasm_bindgen::from_value::(response).unwrap(); let response: PostResponse = post.try_into()?; let status = internals::query_response_status_internal(&self, response).await?; Ok(serde_wasm_bindgen::to_value(&status).expect("Infallible")) @@ -423,11 +159,13 @@ impl HyperClient { /// `MessageStatusWithMeta` pub async fn post_request_status_stream( &self, - request: IPostRequest, + request: JsValue, + initial_state: JsValue, ) -> Result { let lambda = || async move { - let post = serde_wasm_bindgen::from_value::(request.into()).unwrap(); - let height = post.tx_height; + let post = serde_wasm_bindgen::from_value::(request).unwrap(); + let state = + serde_wasm_bindgen::from_value::(initial_state).unwrap(); let post: PostRequest = post.try_into()?; // Obtaining the request stream and the timeout stream @@ -435,7 +173,7 @@ impl HyperClient { internals::message_timeout_stream(post.timeout_timestamp, self.source.clone()) .await; - let request_status = internals::post_request_status_stream(&self, post, height).await?; + let request_status = internals::post_request_status_stream(&self, post, state).await?; let stream = futures::stream::select(request_status, timed_out).map(|res| { res.map(|status| serde_wasm_bindgen::to_value(&status).expect("Infallible")) @@ -462,19 +200,21 @@ impl HyperClient { /// `MessageStatusWithMeta` pub async fn get_request_status_stream( &self, - request: IGetRequest, + request: JsValue, + initial_state: JsValue, ) -> Result { let lambda = || async move { - let get = serde_wasm_bindgen::from_value::(request.into()).unwrap(); - let height = get.tx_height; + let get = serde_wasm_bindgen::from_value::(request).unwrap(); let get: GetRequest = get.try_into()?; + let state = + serde_wasm_bindgen::from_value::(initial_state).unwrap(); // Obtaining the request stream and the timeout stream let timed_out = internals::message_timeout_stream(get.timeout_timestamp, self.hyperbridge.clone()) .await; - let request_status = internals::get_request_status_stream(&self, get, height).await?; + let request_status = internals::get_request_status_stream(&self, get, state).await?; let stream = futures::stream::select(request_status, timed_out).map(|res| { res.map(|status| serde_wasm_bindgen::to_value(&status).expect("Infallible")) .map_err(|e| { @@ -502,22 +242,26 @@ impl HyperClient { /// `request_status_stream`. The stream ends when once it yields a `TimeoutMessage` pub async fn timeout_post_request( &self, - request: IPostRequest, + request: JsValue, + initial_state: JsValue, ) -> Result { let lambda = || async move { - let post = serde_wasm_bindgen::from_value::(request.into()).unwrap(); + let post = serde_wasm_bindgen::from_value::(request).unwrap(); + let state = + serde_wasm_bindgen::from_value::(initial_state).unwrap(); let post: PostRequest = post.try_into()?; - let stream = internals::timeout_post_request_stream(&self, post).await?.map(|value| { - value - .map(|status| serde_wasm_bindgen::to_value(&status).expect("Infallible")) - .map_err(|e| { - serde_wasm_bindgen::to_value(&TimeoutStatus::Error { - description: alloc::format!("{e:?}"), + let stream = + internals::timeout_post_request_stream(&self, post, state).await?.map(|value| { + value + .map(|status| serde_wasm_bindgen::to_value(&status).expect("Infallible")) + .map_err(|e| { + serde_wasm_bindgen::to_value(&TimeoutStatus::Error { + description: alloc::format!("{e:?}"), + }) + .expect("Infallible") }) - .expect("Infallible") - }) - }); + }); let js_stream = ReadableStream::from_stream(stream); Ok(js_stream.into_raw()) diff --git a/modules/hyperclient/src/testing.rs b/modules/hyperclient/src/testing.rs index e10ea21c3..4c43f6b86 100644 --- a/modules/hyperclient/src/testing.rs +++ b/modules/hyperclient/src/testing.rs @@ -31,8 +31,8 @@ use crate::{ internals::{post_request_status_stream, timeout_post_request_stream}, providers::interface::Client, types::{ - ChainConfig, ClientConfig, EvmConfig, HashAlgorithm, MessageStatusWithMetadata, - SubstrateConfig, TimeoutStatus, + ChainConfig, ClientConfig, EvmConfig, HashAlgorithm, MessageStatusStreamState, + MessageStatusWithMetadata, SubstrateConfig, TimeoutStatus, TimeoutStreamState, }, HyperClient, }; @@ -135,7 +135,12 @@ pub async fn subscribe_to_request_status() -> Result<(), anyhow::Error> { let block = receipt.block_number.unwrap(); tracing::info!("\n\nTx block: {block}\n\n"); - let mut stream = post_request_status_stream(&hyperclient, post, block.low_u64()).await?; + let mut stream = post_request_status_stream( + &hyperclient, + post, + MessageStatusStreamState::Dispatched(block.low_u64()), + ) + .await?; while let Some(res) = stream.next().await { match res { @@ -259,8 +264,12 @@ pub async fn test_timeout_request() -> Result<(), anyhow::Error> { let block = receipt.block_number.unwrap(); tracing::info!("\n\nTx block: {block}\n\n"); - let request_status = - post_request_status_stream(&hyperclient, post.clone(), block.low_u64()).await?; + let request_status = post_request_status_stream( + &hyperclient, + post.clone(), + MessageStatusStreamState::Dispatched(block.low_u64()), + ) + .await?; // Obtaining the request stream and the timeout stream let timed_out = @@ -283,7 +292,8 @@ pub async fn test_timeout_request() -> Result<(), anyhow::Error> { } } - let mut stream = timeout_post_request_stream(&hyperclient, post).await?; + let mut stream = + timeout_post_request_stream(&hyperclient, post, TimeoutStreamState::Pending).await?; while let Some(res) = stream.next().await { match res { @@ -409,7 +419,7 @@ pub async fn get_request_handling() -> Result<(), anyhow::Error> { let mut stream = internals::get_request_status_stream( &hyperclient, get_request.clone(), - receipt.block_number.unwrap().low_u64(), + MessageStatusStreamState::Dispatched(receipt.block_number.unwrap().low_u64()), ) .await?; diff --git a/modules/hyperclient/src/types.rs b/modules/hyperclient/src/types.rs index 12c8942a7..cdcb0e35f 100644 --- a/modules/hyperclient/src/types.rs +++ b/modules/hyperclient/src/types.rs @@ -101,20 +101,65 @@ pub struct EventMetadata { pub block_number: u64, } +#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct Bytes(pub Vec); + +impl AsRef<[u8]> for Bytes { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl From> for Bytes { + fn from(value: Vec) -> Bytes { + Bytes(value) + } +} + +impl From for Vec { + fn from(value: Bytes) -> Vec { + value.0 + } +} + +impl fmt::Debug for Bytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Bytes").field(&HexFmt(self.0.clone())).finish() + } +} + #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -#[serde(tag = "kind")] -pub enum MessageStatus { +pub enum TimeoutStreamState { Pending, - /// Source state machine has been finalized on hyperbridge. - SourceFinalized, - /// Message has been delivered to hyperbridge - HyperbridgeDelivered, - /// Messaged has been finalized on hyperbridge - HyperbridgeFinalized, - /// Delivered to destination + /// Destination state machine has been finalized on Hyperbridge + DestinationFinalized(u64), + /// The message time out has been verified by Hyperbridge, holds the block where the message + /// was verified + HyperbridgeVerified(u64), + /// Hyperbridge has been finalized on source chain + HyperbridgeFinalized(u64), + /// Stream has ended + End, +} + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub enum MessageStatusStreamState { + /// Waiting for the message to be finalized on the source chain, holds the tx height for the + /// source chain. + Dispatched(u64), + /// Source state machine has been finalized on hyperbridge, holds the block number at which the + /// source was finalized on hyperbridge + SourceFinalized(u64), + /// The message has been verified by Hyperbridge, holds the block where the message was + /// verified + HyperbridgeVerified(u64), + /// Message has been finalized by hyperbridge, holds the destination block number where + /// hyperbridge was finalized. + HyperbridgeFinalized(u64), + /// Message has been delivered to destination DestinationDelivered, - /// Message has timed out - Timeout, + /// Stream has ended, check the message status + End, } #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] @@ -129,8 +174,8 @@ pub enum MessageStatusWithMetadata { #[serde(flatten)] meta: EventMetadata, }, - /// Message has been delivered to hyperbridge - HyperbridgeDelivered { + /// The message has been verified by Hyperbridge + HyperbridgeVerified { /// Metadata about the event on hyperbridge #[serde(flatten)] meta: EventMetadata, @@ -161,51 +206,6 @@ pub enum MessageStatusWithMetadata { Timeout, } -#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct Bytes(pub Vec); - -impl AsRef<[u8]> for Bytes { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl From> for Bytes { - fn from(value: Vec) -> Bytes { - Bytes(value) - } -} - -impl From for Vec { - fn from(value: Bytes) -> Vec { - value.0 - } -} - -impl fmt::Debug for Bytes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Bytes").field(&HexFmt(self.0.clone())).finish() - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum MessageStatusStreamState { - /// Waiting for the message to be finalized on the source chain - Pending, - /// Source state machine has been finalized on hyperbridge, holds the block number at which the - /// source was finalized on hyperbridge - SourceFinalized(u64), - /// Message has been delivered to hyperbridge, holds the block where the message was delivered - HyperbridgeDelivered(u64), - /// Message has been finalized by hyperbridge, holds the destination block number where - /// hyperbridge was finalized. - HyperbridgeFinalized(u64), - /// Message has been delivered to destination - DestinationDelivered, - /// Stream has ended, check the message status - End, -} - #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] #[serde(tag = "kind")] pub enum TimeoutStatus { @@ -216,8 +216,8 @@ pub enum TimeoutStatus { #[serde(flatten)] meta: EventMetadata, }, - /// Message has been timed out on hyperbridge - HyperbridgeTimedout { + /// The message time out has been verified by Hyperbridge + HyperbridgeVerified { /// Metadata about the event on hyperbridge #[serde(flatten)] meta: EventMetadata, @@ -290,7 +290,7 @@ impl ClientConfig { #[cfg(test)] mod tests { - use crate::types::{MessageStatus, MessageStatusWithMetadata}; + use crate::types::{MessageStatusStreamState, MessageStatusWithMetadata, TimeoutStreamState}; #[test] fn test_serialization() -> Result<(), anyhow::Error> { @@ -300,7 +300,21 @@ mod tests { meta: Default::default() })? ); - assert_eq!(r#"{"kind":"Timeout"}"#, json::to_string(&MessageStatus::Timeout)?); + + assert_eq!( + r#"{"DestinationFinalized":24}"#, + json::to_string(&TimeoutStreamState::DestinationFinalized(24))? + ); + assert_eq!(r#""Pending""#, json::to_string(&TimeoutStreamState::Pending)?); + assert_eq!( + r#"{"HyperbridgeVerified":24}"#, + json::to_string(&MessageStatusStreamState::HyperbridgeVerified(24))? + ); + + assert_eq!( + r#"{"Dispatched":23}"#, + json::to_string(&MessageStatusStreamState::Dispatched(23))? + ); Ok(()) }