diff --git a/Cargo.lock b/Cargo.lock index 53034388f4..c0658a400e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6265,6 +6265,7 @@ dependencies = [ "contracts", "cosmwasm-std", "derive_more", + "either", "ethereum_ssz", "ethers-core 2.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array", @@ -6285,6 +6286,7 @@ dependencies = [ "sha2 0.10.8", "ssz_types", "subtle-encoding", + "thiserror", "tracing", "tree_hash", "typenum", diff --git a/lib/chain-utils/src/evm.rs b/lib/chain-utils/src/evm.rs index 0391638b5b..cf5d2d3610 100644 --- a/lib/chain-utils/src/evm.rs +++ b/lib/chain-utils/src/evm.rs @@ -1,4 +1,4 @@ -use std::{fmt::Display, ops::Div, str::FromStr, sync::Arc}; +use std::{ops::Div, str::FromStr, sync::Arc}; use beacon_api::client::BeaconApiClient; use contracts::{ @@ -40,13 +40,12 @@ use unionlabs::{ google::protobuf::any::Any, lightclients::{cometbls, ethereum, tendermint::fraction::Fraction, wasm}, }, - id::{ChannelId, Id, IdParseError}, - id_type, + id::{ChannelId, ClientId, ConnectionId}, traits::{Chain, ClientState}, EmptyString, TryFromEthAbiErrorOf, TryFromProto, }; -use crate::{chain_client_id, private_key::PrivateKey, ChainEvent, EventSource, Pool}; +use crate::{private_key::PrivateKey, ChainEvent, EventSource, Pool}; pub type CometblsMiddleware = SignerMiddleware>, Wallet>; @@ -95,7 +94,7 @@ impl Chain for Evm { type ClientId = EvmClientId; - type ClientType = EvmClientType; + type ClientType = String; fn chain_id(&self) -> ::ChainId { self.chain_id @@ -412,13 +411,7 @@ impl Evm { } } -chain_client_id! { - #[ty = EvmClientType] - pub enum EvmClientId { - #[id(ty = "cometbls")] - Cometbls(Id<_>), - } -} +pub type EvmClientId = ClientId; #[derive(Debug)] pub enum EvmEventSourceError { @@ -436,9 +429,9 @@ pub enum EvmEventSourceError { ConnectionOpenInitConnectionConversion( TryFromEthAbiErrorOf>, ), - Parse(IdParseError), - ClientTypeParse(::Err), - ClientIdParse(::Err), + ClientIdParse(::Err), + ConnectionIdParse(::Err), + ChannelIdParse(::Err), EthAbi(ethers::core::abi::Error), } @@ -569,13 +562,13 @@ impl EventSource for Evm { .packet .source_channel .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, packet_dst_port: packet_ack.packet.destination_port, packet_dst_channel: packet_ack .packet .destination_channel .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, packet_channel_ordering: channel_data.ordering, connection_id: channel_data.connection_hops[0].clone(), })) @@ -592,13 +585,13 @@ impl EventSource for Evm { channel_id: event .channel_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, counterparty_port_id: channel.counterparty.port_id, counterparty_channel_id: channel .counterparty .channel_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, connection_id: channel.connection_hops[0].clone(), })) } @@ -612,13 +605,13 @@ impl EventSource for Evm { channel_id: event .channel_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, counterparty_port_id: channel.counterparty.port_id, counterparty_channel_id: channel .counterparty .channel_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, connection_id: channel.connection_hops[0].clone(), })) } @@ -632,14 +625,14 @@ impl EventSource for Evm { channel_id: event .channel_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, // TODO: Ensure that event.counterparty_channel_id is `EmptyString` counterparty_channel_id: EmptyString, counterparty_port_id: event.counterparty_port_id, connection_id: event .connection_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ConnectionIdParse)?, version: channel.version, })) } @@ -653,17 +646,17 @@ impl EventSource for Evm { channel_id: event .channel_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, counterparty_port_id: event.counterparty_port_id, counterparty_channel_id: channel .counterparty .channel_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, connection_id: event .connection_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ConnectionIdParse)?, version: event.version, })) } @@ -675,7 +668,7 @@ impl EventSource for Evm { connection_id: event .connection_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ConnectionIdParse)?, client_id: connection.client_id, counterparty_client_id: connection.counterparty.client_id, counterparty_connection_id: connection @@ -691,7 +684,7 @@ impl EventSource for Evm { connection_id: event .connection_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ConnectionIdParse)?, client_id: connection.client_id, counterparty_client_id: connection.counterparty.client_id, counterparty_connection_id: connection @@ -725,7 +718,7 @@ impl EventSource for Evm { connection_id: event .connection_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ConnectionIdParse)?, client_id: connection.client_id, counterparty_client_id: connection.counterparty.client_id, counterparty_connection_id: connection @@ -741,7 +734,7 @@ impl EventSource for Evm { connection_id: event .connection_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ConnectionIdParse)?, client_id: connection.client_id, counterparty_client_id: connection.counterparty.client_id, counterparty_connection_id: connection @@ -778,9 +771,7 @@ impl EventSource for Evm { .0 .parse() .map_err(EvmEventSourceError::ClientIdParse)?, - client_type: client_type - .parse() - .map_err(EvmEventSourceError::ClientTypeParse)?, + client_type, consensus_height: client_state.0.latest_height, })) } @@ -801,13 +792,13 @@ impl EventSource for Evm { .packet .source_channel .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, packet_dst_port: event.packet.destination_port, packet_dst_channel: event .packet .destination_channel .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, packet_channel_ordering: channel.ordering, connection_id: channel.connection_hops[0].clone(), })) @@ -828,7 +819,7 @@ impl EventSource for Evm { packet_src_channel: event .source_channel .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, // REVIEW: Should we query the packet instead? Or is that the same info? Is it even possible to // query packets from the evm? packet_dst_port: channel.counterparty.port_id, @@ -836,7 +827,7 @@ impl EventSource for Evm { .counterparty .channel_id .parse() - .map_err(EvmEventSourceError::Parse)?, + .map_err(EvmEventSourceError::ChannelIdParse)?, packet_channel_ordering: channel.ordering, connection_id: channel.connection_hops[0].clone(), })) diff --git a/lib/chain-utils/src/lib.rs b/lib/chain-utils/src/lib.rs index ca431777af..33e39479b2 100644 --- a/lib/chain-utils/src/lib.rs +++ b/lib/chain-utils/src/lib.rs @@ -17,7 +17,7 @@ use unionlabs::{ ethereum::H256, events::IbcEvent, ibc::core::client::height::Height, - traits::{Chain, ClientState, Id}, + traits::{Chain, ClientState}, }; pub mod evm; @@ -44,181 +44,6 @@ pub struct ChainEvent { pub event: IbcEvent, } -macro_rules! chain_client_id { - ( - #[ty = $Ty:ident] - pub enum $Enum:ident { - $( - // will wrap in `Id<_>` - #[id(ty = $ty:literal)] - $Variant:ident(Id<_>), - )+ - } - ) => { - #[derive(Debug, Clone, PartialEq, ::serde::Serialize, ::serde::Deserialize)] - #[serde(untagged)] - pub enum $Enum { - $( - $Variant(Id<$Variant>), - )+ - } - - const _: () = { - const STRINGS: &[&str] = &[$($ty),*]; - - const LEN: usize = STRINGS.len(); - - let mut i = 0; - let mut j = 0; - - while i < LEN { - while j < LEN { - if i == j { - j += 1; - continue; - } else { - assert!(!const_str_equal(STRINGS[i], STRINGS[j]), "strings are not unique"); - } - j += 1; - } - i += 1; - } - - // https://internals.rust-lang.org/t/why-i-cannot-compare-two-static-str-s-in-a-const-context/17726/8 - const fn const_bytes_equal(lhs: &[u8], rhs: &[u8]) -> bool { - if lhs.len() != rhs.len() { - return false; - } - let mut i = 0; - while i < lhs.len() { - if lhs[i] != rhs[i] { - return false; - } - i += 1; - } - true - } - - const fn const_str_equal(lhs: &str, rhs: &str) -> bool { - const_bytes_equal(lhs.as_bytes(), rhs.as_bytes()) - } - }; - - $( - impl From> for $Enum { - fn from(id: Id<$Variant>) -> Self { - Self::$Variant(id) - } - } - - impl TryFrom<$Enum> for Id<$Variant> { - type Error = $Enum; - - fn try_from(value: $Enum) -> Result { - match value { - $Enum::$Variant(id) => Ok(id), - #[allow(unreachable_patterns)] // for when new types are added - _ => Err(value), - } - } - } - - impl From<$Variant> for $Ty { - fn from(id: $Variant) -> Self { - Self::$Variant(id) - } - } - - impl TryFrom<$Ty> for $Variant { - type Error = $Ty; - - fn try_from(value: $Ty) -> Result { - match value { - $Ty::$Variant(id) => Ok(id), - #[allow(unreachable_patterns)] // for when new types are added - _ => Err(value), - } - } - } - )+ - - #[derive(Debug, Clone, PartialEq, ::serde::Serialize, ::serde::Deserialize)] - #[serde(untagged)] - pub enum $Ty { - $( - $Variant($Variant), - )+ - } - - $( - id_type! { - #[ty = $ty] - pub struct $Variant; - } - )+ - - impl crate::Id for $Enum { - type FromStrErr = crate::ChainClientIdParseError; - } - - impl FromStr for $Enum { - type Err = crate::ChainClientIdParseError; - - fn from_str(s: &str) -> Result { - $( - if let Ok(ok) = s.parse::>().map(Self::$Variant) { - return Ok(ok); - } - )+ - - Err(crate::ChainClientIdParseError { - expected: &[$($ty),+], - found: s.to_string(), - }) - } - } - - impl Display for $Enum { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - $( - Self::$Variant(id) => f.write_fmt(format_args!("{id}")), - )+ - } - } - } - - - impl FromStr for $Ty { - type Err = crate::ChainClientIdParseError; - - fn from_str(s: &str) -> Result { - match s { - $( - $ty => Ok(Self::$Variant($Variant)), - )+ - _ => Err(crate::ChainClientIdParseError { - expected: &[$($ty),+], - found: s.to_string(), - }) - } - } - } - - impl Display for $Ty { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - $( - Self::$Variant(_) => f.write_str($ty), - )+ - } - } - } - }; -} - -pub(crate) use chain_client_id; - // TODO: Make this a more generic error and put it in unionlabs::errors #[derive(Debug, Clone, PartialEq)] pub struct ChainClientIdParseError { diff --git a/lib/chain-utils/src/union.rs b/lib/chain-utils/src/union.rs index 6c636cc612..5979ae40e1 100644 --- a/lib/chain-utils/src/union.rs +++ b/lib/chain-utils/src/union.rs @@ -1,4 +1,4 @@ -use std::{fmt::Display, num::ParseIntError, str::FromStr}; +use std::num::ParseIntError; use ethers::prelude::k256::ecdsa; use futures::{stream, Future, FutureExt, Stream, StreamExt}; @@ -17,8 +17,7 @@ use unionlabs::{ google::protobuf::{any::Any, duration::Duration}, lightclients::{cometbls, wasm}, }, - id::Id, - id_type, + id::ClientId, tendermint::abci::{event::Event, event_attribute::EventAttribute}, traits::{Chain, ClientState}, CosmosAccountId, @@ -62,7 +61,7 @@ impl Chain for Union { type ClientId = UnionClientId; - type ClientType = UnionClientType; + type ClientType = String; fn chain_id(&self) -> ::ChainId { self.chain_id.clone() @@ -545,16 +544,7 @@ impl MaybeRecoverableError for BroadcastTxCommitError { } } -// TODO: This is for all cosmos chains; rename? -crate::chain_client_id! { - #[ty = UnionClientType] - pub enum UnionClientId { - #[id(ty = "08-wasm")] - Wasm(Id<_>), - #[id(ty = "07-tendermint")] - Tendermint(Id<_>), - } -} +pub type UnionClientId = ClientId; #[derive(Debug)] pub enum UnionEventSourceError { @@ -949,5 +939,44 @@ pub mod tm_types { // TODO: Figure out what this is and where it comes from // ErrPanic = errorsmod.ErrPanic ) + + #[err(name = ChannelError, codespace = "channel")] + var ( + ErrChannelExists = errorsmod.Register(SubModuleName, 2, "channel already exists") + ErrChannelNotFound = errorsmod.Register(SubModuleName, 3, "channel not found") + ErrInvalidChannel = errorsmod.Register(SubModuleName, 4, "invalid channel") + ErrInvalidChannelState = errorsmod.Register(SubModuleName, 5, "invalid channel state") + ErrInvalidChannelOrdering = errorsmod.Register(SubModuleName, 6, "invalid channel ordering") + ErrInvalidCounterparty = errorsmod.Register(SubModuleName, 7, "invalid counterparty channel") + ErrInvalidChannelCapability = errorsmod.Register(SubModuleName, 8, "invalid channel capability") + ErrChannelCapabilityNotFound = errorsmod.Register(SubModuleName, 9, "channel capability not found") + ErrSequenceSendNotFound = errorsmod.Register(SubModuleName, 10, "sequence send not found") + ErrSequenceReceiveNotFound = errorsmod.Register(SubModuleName, 11, "sequence receive not found") + ErrSequenceAckNotFound = errorsmod.Register(SubModuleName, 12, "sequence acknowledgement not found") + ErrInvalidPacket = errorsmod.Register(SubModuleName, 13, "invalid packet") + ErrPacketTimeout = errorsmod.Register(SubModuleName, 14, "packet timeout") + ErrTooManyConnectionHops = errorsmod.Register(SubModuleName, 15, "too many connection hops") + ErrInvalidAcknowledgement = errorsmod.Register(SubModuleName, 16, "invalid acknowledgement") + ErrAcknowledgementExists = errorsmod.Register(SubModuleName, 17, "acknowledgement for packet already exists") + ErrInvalidChannelIdentifier = errorsmod.Register(SubModuleName, 18, "invalid channel identifier") + + // packets already relayed errors + ErrPacketReceived = errorsmod.Register(SubModuleName, 19, "packet already received") + ErrPacketCommitmentNotFound = errorsmod.Register(SubModuleName, 20, "packet commitment not found") // may occur for already received acknowledgements or timeouts and in rare cases for packets never sent + + // ORDERED channel error + ErrPacketSequenceOutOfOrder = errorsmod.Register(SubModuleName, 21, "packet sequence is out of order") + + // cspell:ignore Antehandler + // Antehandler error + ErrRedundantTx = errorsmod.Register(SubModuleName, 22, "packet messages are redundant") + + // Perform a no-op on the current Msg + ErrNoOpMsg = errorsmod.Register(SubModuleName, 23, "message is redundant, no-op will be performed") + + ErrInvalidChannelVersion = errorsmod.Register(SubModuleName, 24, "invalid channel version") + ErrPacketNotSent = errorsmod.Register(SubModuleName, 25, "packet has not been sent") + ErrInvalidTimeout = errorsmod.Register(SubModuleName, 26, "invalid packet timeout") + ) } } diff --git a/lib/unionlabs/Cargo.toml b/lib/unionlabs/Cargo.toml index f6290bbe9b..e4086814f5 100644 --- a/lib/unionlabs/Cargo.toml +++ b/lib/unionlabs/Cargo.toml @@ -44,6 +44,8 @@ wasmparser = { version = "0.113" } paste = { version = "1.0" } derive_more = { version = "0.99.17", default-features = false, features = ["display"] } clap = { version = "4.4.6", default-features = false, features = ["derive", "std"] } +either = "1.9.0" +thiserror = "1.0.49" [dev-dependencies] rand = "0.8.5" diff --git a/lib/unionlabs/src/bounded_int.rs b/lib/unionlabs/src/bounded.rs similarity index 100% rename from lib/unionlabs/src/bounded_int.rs rename to lib/unionlabs/src/bounded.rs diff --git a/lib/unionlabs/src/ibc/core/channel/channel.rs b/lib/unionlabs/src/ibc/core/channel/channel.rs index ba33542199..c3d0976c44 100644 --- a/lib/unionlabs/src/ibc/core/channel/channel.rs +++ b/lib/unionlabs/src/ibc/core/channel/channel.rs @@ -3,7 +3,8 @@ use serde::{Deserialize, Serialize}; use crate::{ errors::{required, MissingField, UnknownEnumVariant}, ibc::core::channel::{counterparty::Counterparty, order::Order, state::State}, - id::{ConnectionId, IdParseError}, + id::{ConnectionId, ConnectionIdValidator}, + validated::{Validate, ValidateT}, Proto, TypeUrl, }; @@ -38,7 +39,7 @@ pub enum TryFromChannelError { MissingField(MissingField), State(UnknownEnumVariant), Ordering(UnknownEnumVariant), - ConnectionHops(IdParseError), + ConnectionHops(>::Error), } impl TryFrom for Channel { @@ -52,7 +53,7 @@ impl TryFrom for Channel { connection_hops: proto .connection_hops .into_iter() - .map(|x| x.parse()) + .map(ValidateT::validate) .collect::>() .map_err(TryFromChannelError::ConnectionHops)?, version: proto.version, @@ -90,7 +91,7 @@ impl From for contracts::ibc_handler::IbcCoreChannelV1ChannelData { pub enum TryFromEthAbiChannelError { State(UnknownEnumVariant), Ordering(UnknownEnumVariant), - ConnectionHops(IdParseError), + ConnectionHops(>::Error), } #[cfg(feature = "ethabi")] @@ -113,7 +114,7 @@ impl TryFrom for Channel { connection_hops: value .connection_hops .into_iter() - .map(|x| x.parse()) + .map(ValidateT::validate) .collect::>() .map_err(TryFromEthAbiChannelError::ConnectionHops)?, version: value.version, diff --git a/lib/unionlabs/src/ibc/core/channel/packet.rs b/lib/unionlabs/src/ibc/core/channel/packet.rs index ce700a14ae..029662f4dd 100644 --- a/lib/unionlabs/src/ibc/core/channel/packet.rs +++ b/lib/unionlabs/src/ibc/core/channel/packet.rs @@ -3,7 +3,8 @@ use serde::{Deserialize, Serialize}; use crate::{ errors::{required, MissingField}, ibc::core::client::height::Height, - id::{ChannelId, IdParseError}, + id::{ChannelId, ChannelIdValidator}, + validated::{Validate, ValidateT}, Proto, TypeUrl, }; @@ -46,8 +47,8 @@ impl From for protos::ibc::core::channel::v1::Packet { #[derive(Debug)] pub enum TryFromPacketError { MissingField(MissingField), - SourceChannel(IdParseError), - DestinationChannel(IdParseError), + SourceChannel(>::Error), + DestinationChannel(>::Error), } impl TryFrom for Packet { @@ -59,12 +60,12 @@ impl TryFrom for Packet { source_port: proto.source_port, source_channel: proto .source_channel - .parse() + .validate() .map_err(TryFromPacketError::SourceChannel)?, destination_port: proto.destination_port, destination_channel: proto .destination_channel - .parse() + .validate() .map_err(TryFromPacketError::DestinationChannel)?, data: proto.data, timeout_height: required!(proto.timeout_height)?.into(), @@ -76,8 +77,8 @@ impl TryFrom for Packet { #[cfg(feature = "ethabi")] #[derive(Debug)] pub enum TryFromEthAbiPacketError { - SourceChannel(IdParseError), - DestinationChannel(IdParseError), + SourceChannel(>::Error), + DestinationChannel(>::Error), } #[cfg(feature = "ethabi")] diff --git a/lib/unionlabs/src/ibc/google/protobuf/duration.rs b/lib/unionlabs/src/ibc/google/protobuf/duration.rs index 50e322c71b..53a94b6dcf 100644 --- a/lib/unionlabs/src/ibc/google/protobuf/duration.rs +++ b/lib/unionlabs/src/ibc/google/protobuf/duration.rs @@ -11,7 +11,7 @@ use serde::{ }; use crate::{ - bounded_int::{BoundedI128, BoundedI32, BoundedI64, BoundedIntError}, + bounded::{BoundedI128, BoundedI32, BoundedI64, BoundedIntError}, macros::result_try, Proto, TypeUrl, }; diff --git a/lib/unionlabs/src/ibc/google/protobuf/timestamp.rs b/lib/unionlabs/src/ibc/google/protobuf/timestamp.rs index 2aa3152d92..0990d9c2e1 100644 --- a/lib/unionlabs/src/ibc/google/protobuf/timestamp.rs +++ b/lib/unionlabs/src/ibc/google/protobuf/timestamp.rs @@ -8,7 +8,7 @@ use serde::{ }; use crate::{ - bounded_int::{BoundedI32, BoundedI64, BoundedIntError}, + bounded::{BoundedI32, BoundedI64, BoundedIntError}, ibc::google::protobuf::duration::{Duration, NANOS_PER_SECOND}, Proto, TypeUrl, }; diff --git a/lib/unionlabs/src/lib.rs b/lib/unionlabs/src/lib.rs index 8a25fc69d5..c6cb307dde 100644 --- a/lib/unionlabs/src/lib.rs +++ b/lib/unionlabs/src/lib.rs @@ -44,7 +44,7 @@ pub mod events; pub mod ethereum_consts_traits; -pub mod bounded_int; +pub mod bounded; pub mod proof; @@ -85,16 +85,21 @@ pub mod errors { pub(crate) use required; // Expected one length, but found another. - #[derive(Debug, PartialEq, Eq)] + #[derive(Debug, PartialEq, Eq, thiserror::Error)] + #[error("invalid length: expected {expected}, found {found}")] pub struct InvalidLength { pub expected: ExpectedLength, pub found: usize, } - #[derive(Debug, PartialEq, Eq)] + #[derive(Debug, PartialEq, Eq, derive_more::Display)] pub enum ExpectedLength { + #[display(fmt = "exactly {_0}")] Exact(usize), + #[display(fmt = "less than {_0}")] LessThan(usize), + #[display(fmt = "between ({_0}, {_0})")] + Between(usize, usize), } #[derive(Debug, PartialEq, Eq)] @@ -348,221 +353,80 @@ impl Display for CosmosAccountId { } pub mod id { - use std::{ - error::Error, - fmt::{Debug, Display}, - num::ParseIntError, - str::FromStr, - }; - - use serde::{Deserialize, Serialize}; - - /// An id of the form `-`. - #[derive(PartialEq, Serialize, Deserialize)] - #[serde( - bound(serialize = "Type: IdType", deserialize = "Type: IdType"), - try_from = "&str", - into = "String" - )] - pub struct Id { - ty: Type, - id: u32, - } + use std::fmt::Debug; - pub trait IdType: - Display - + FromStr - + Debug - + Clone - + PartialEq - + Serialize - + for<'de> Deserialize<'de> - + Send - + Sync - + 'static - { - const TYPE: &'static str; - } - - impl crate::traits::Id for Id { - type FromStrErr = ::Err; - } + use crate::{ + errors::{ExpectedLength, InvalidLength}, + validated::{Validate, Validated}, + }; - impl Clone for Id { - fn clone(&self) -> Self { - Self { - ty: self.ty.clone(), - id: self.id, + pub type PortIdValidator = (Bounded<2, 128>, Ics024IdentifierCharacters); + pub type PortId = Validated; + pub type ClientIdValidator = (Bounded<9, 64>, Ics024IdentifierCharacters); + pub type ClientId = Validated; + pub type ConnectionIdValidator = (Bounded<10, 64>, Ics024IdentifierCharacters); + pub type ConnectionId = Validated; + pub type ChannelIdValidator = (Bounded<8, 64>, Ics024IdentifierCharacters); + pub type ChannelId = Validated; + + #[test] + fn cid2() { + let c = ChannelId::new("channel-1".into()).unwrap(); + + dbg!(c); + } + + // https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements#paths-identifiers-separators + pub struct Ics024IdentifierCharacters; + + #[derive(Debug, thiserror::Error)] + #[error("invalid ics-024 identifier character: `{0}`")] + pub struct InvalidIcs024IdentifierCharacter(char); + + impl> Validate for Ics024IdentifierCharacters { + type Error = InvalidIcs024IdentifierCharacter; + + fn validate(t: T) -> Result { + for c in t.as_ref().chars() { + match c { + 'a'..='z' + | 'A'..='Z' + | '0'..='9' + | '.' + | '_' + | '+' + | '-' + | '#' + | '[' + | ']' + | '<' + | '>' => {} + _ => return Err(InvalidIcs024IdentifierCharacter(c)), + } } - } - } - - impl Debug for Id { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("{:?}({})", self.ty, self.id)) - } - } - - impl Id { - pub fn new(ty: Type, id: u32) -> Self { - Self { ty, id } - } - } - - impl Display for Id { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("{}-{}", self.ty, self.id)) - } - } - impl From> for String { - fn from(value: Id) -> Self { - value.to_string() + Ok(t) } } - #[derive(Debug)] - pub enum IdParseError { - Type(InvalidIdType), - Id(ParseIntError), - InvalidFormat { found: String }, - } - - impl Display for IdParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - IdParseError::Type(ty) => f.write_fmt(format_args!( - "unable to parse the type portion of the id: {ty}" - )), - IdParseError::Id(id) => f.write_fmt(format_args!( - "unable to parse the numeric portion of the id: {id}" - )), - IdParseError::InvalidFormat { found } => f.write_fmt(format_args!( - "the id was not in the expected format `-`: {found}" - )), - } - } - } + pub struct Bounded; - impl Error for IdParseError {} + impl, const MIN: usize, const MAX: usize> Validate for Bounded { + type Error = InvalidLength; - impl FromStr for Id { - type Err = IdParseError; + fn validate(s: T) -> Result { + let len = s.as_ref().len(); - fn from_str(s: &str) -> Result { - match s.rsplit_once('-') { - Some((ty, id)) => Ok(Self { - ty: ty.parse().map_err(IdParseError::Type)?, - id: id.parse().map_err(IdParseError::Id)?, - }), - None => Err(IdParseError::InvalidFormat { - found: s.to_string(), - }), + if (MIN..=MAX).contains(&len) { + Ok(s) + } else { + Err(InvalidLength { + expected: ExpectedLength::Between(MIN, MAX), + found: len, + }) } } } - - impl TryFrom<&str> for Id { - type Error = as FromStr>::Err; - - fn try_from(value: &str) -> Result { - value.parse() - } - } - - #[derive(Debug, Clone, PartialEq)] - pub struct InvalidIdType { - pub expected: &'static str, - pub found: String, - } - - impl Display for InvalidIdType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "expected `{}`, found `{}`", - self.expected, self.found, - )) - } - } - - impl Error for InvalidIdType {} - - #[macro_export] - macro_rules! id_type { - ( - $(#[doc = $doc:literal])* - #[ty = $ty:literal] - pub struct $Struct:ident; - ) => { - #[derive(Debug, Clone, PartialEq)] - pub struct $Struct; - - impl ::std::str::FromStr for $Struct { - type Err = $crate::id::InvalidIdType; - - fn from_str(s: &str) -> Result { - matches!(s, $ty) - .then_some(Self) - .ok_or($crate::id::InvalidIdType { - expected: $ty, - found: s.to_string(), - }) - } - } - - impl ::std::fmt::Display for $Struct { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str($ty) - } - } - - impl serde::Serialize for $Struct { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.collect_str(self) - } - } - - impl<'de> serde::Deserialize<'de> for $Struct { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - <&str>::deserialize(deserializer).and_then(|s| { - s.parse() - // TODO fix error situation - // FromStr::Err has no bounds - .map_err(|_| { - serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &$ty) - }) - }) - } - } - - impl $crate::id::IdType for $Struct { - const TYPE: &'static str = $ty; - } - }; - } - - pub use id_type; - - id_type! { - /// Id type for `connection_id`. - #[ty = "connection"] - pub struct Connection; - } - - id_type! { - /// Id for `channel_id`. - #[ty = "channel"] - pub struct Channel; - } - - pub type ConnectionId = Id; - pub type ChannelId = Id; } pub mod traits { @@ -585,6 +449,7 @@ pub mod traits { lightclients::{cometbls, ethereum, wasm}, }, id::ChannelId, + validated::{Validate, Validated}, }; /// A convenience trait for a string id (`ChainId`, `ClientId`, `ConnectionId`, etc) @@ -608,6 +473,14 @@ pub mod traits { type FromStrErr = std::string::ParseError; } + impl + 'static> Id for Validated + where + T::FromStrErr: Error, + V::Error: Error, + { + type FromStrErr = ::Err; + } + /// Represents a chain. One [`Chain`] may have many related [`LightClient`]s for connecting to /// various other [`Chain`]s, all sharing a common config. pub trait Chain { @@ -865,3 +738,204 @@ impl TryFrom<&[u8]> for WasmClientType { }) } } + +pub mod validated { + use std::{ + fmt::{Debug, Display}, + marker::PhantomData, + str::FromStr, + }; + + use either::Either; + use serde::{Deserialize, Serialize}; + + #[derive(Serialize, Deserialize)] + #[serde( + transparent, + bound(serialize = "T: Serialize", deserialize = "T: for<'d> Deserialize<'d>") + )] + pub struct Validated>(T, #[serde(skip)] PhantomData V>); + + pub trait ValidateT: Sized { + fn validate>(self) -> Result, V::Error> { + Validated::new(self) + } + } + + impl ValidateT for T {} + + impl> Debug for Validated { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Validated").field(&self.0).finish() + } + } + + impl> Display for Validated { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + + impl> FromStr for Validated { + type Err = Either; + + fn from_str(s: &str) -> Result { + Validated::new(s.parse().map_err(Either::Left)?).map_err(Either::Right) + } + } + + impl> Clone for Validated { + fn clone(&self) -> Self { + Self(self.0.clone(), PhantomData) + } + } + + impl> PartialEq for Validated { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } + } + + impl> Validated { + pub fn new(t: T) -> Result { + V::validate(t).map(|ok| Validated(ok, PhantomData)) + } + + pub fn value(self) -> T { + self.0 + } + + pub fn mutate( + self, + f: impl FnOnce(T) -> U, + ) -> Result, >::Error> + where + V: Validate, + { + Validated::new(f(self.0)) + } + } + + pub trait Validate: Sized { + type Error; + + fn validate(t: T) -> Result; + } + + impl, V2: Validate> Validate for (V1, V2) { + type Error = Either; + + fn validate(t: T) -> Result { + match V1::validate(t).map(|t| V2::validate(t)) { + Ok(Ok(t)) => Ok(t), + Ok(Err(e)) => Err(Either::Right(e)), + Err(e) => Err(Either::Left(e)), + } + } + } + + impl Validate for () { + type Error = (); + + fn validate(t: T) -> Result { + Ok(t) + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn validate() { + #[derive(Debug, PartialEq)] + struct NonZero; + #[derive(Debug, PartialEq)] + struct NonMax; + #[derive(Debug, PartialEq)] + struct NotEight; + + impl Validate for NonZero { + type Error = Self; + + fn validate(t: u8) -> Result { + if t == 0 { + Err(NonZero) + } else { + Ok(t) + } + } + } + + impl Validate for NonMax { + type Error = Self; + + fn validate(t: u8) -> Result { + if t == u8::MAX { + Err(NonMax) + } else { + Ok(t) + } + } + } + + impl Validate for NotEight { + type Error = Self; + + fn validate(t: u8) -> Result { + if t == 8 { + Err(NotEight) + } else { + Ok(t) + } + } + } + + assert_eq!(Validated::<_, NonZero>::new(0), Err(NonZero)); + + assert_eq!( + Validated::<_, (NonZero, ())>::new(0), + Err(Either::Left(NonZero)) + ); + + assert_eq!( + Validated::<_, (NonZero, NonMax)>::new(0), + Err(Either::Left(NonZero)) + ); + + assert_eq!( + Validated::<_, (NonZero, NonMax)>::new(u8::MAX), + Err(Either::Right(NonMax)) + ); + + assert_eq!( + Validated::<_, (NonZero, NonMax)>::new(8), + Ok(Validated(8, PhantomData)) + ); + + assert_eq!( + Validated::<_, (NonZero, (NonMax, NotEight))>::new(8), + Err(Either::Right(Either::Right(NotEight))) + ); + + assert_eq!( + Validated::<_, (NotEight, (NonMax, NonZero))>::new(8), + Err(Either::Left(NotEight)) + ); + + assert_eq!( + Validated::<_, (NotEight, (NonMax, NonZero))>::new(7) + .unwrap() + .mutate(|t| t + 1), + Err(Either::Left(NotEight)) + ); + + assert_eq!( + Validated::<_, (NotEight, (NonMax, NonZero))>::new(7) + .unwrap() + .mutate(|t| t + 2), + Ok(Validated(9, PhantomData)) + ); + } + } +} diff --git a/lib/unionlabs/src/tendermint/types/canonical_vote.rs b/lib/unionlabs/src/tendermint/types/canonical_vote.rs index a2d51748f4..e34252db57 100644 --- a/lib/unionlabs/src/tendermint/types/canonical_vote.rs +++ b/lib/unionlabs/src/tendermint/types/canonical_vote.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ - bounded_int::BoundedI64, + bounded::BoundedI64, tendermint::types::{canonical_block_id::CanonicalBlockId, signed_msg_type::SignedMsgType}, Proto, TypeUrl, }; diff --git a/lib/unionlabs/src/tendermint/types/commit.rs b/lib/unionlabs/src/tendermint/types/commit.rs index 2217a00a4d..56237a3a62 100644 --- a/lib/unionlabs/src/tendermint/types/commit.rs +++ b/lib/unionlabs/src/tendermint/types/commit.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ - bounded_int::{BoundedI32, BoundedI64, BoundedIntError}, + bounded::{BoundedI32, BoundedI64, BoundedIntError}, errors::{required, MissingField}, tendermint::types::{block_id::BlockId, commit_sig::CommitSig}, Proto, TryFromProtoErrorOf, TypeUrl, @@ -34,8 +34,8 @@ impl crate::EthAbi for Commit { #[cfg(feature = "ethabi")] #[derive(Debug)] pub enum TryFromEthAbiCommitError { - Height(crate::bounded_int::BoundedIntError), - Round(crate::bounded_int::BoundedIntError), + Height(crate::bounded::BoundedIntError), + Round(crate::bounded::BoundedIntError), BlockId(crate::TryFromEthAbiErrorOf), Signatures(crate::TryFromEthAbiErrorOf), } diff --git a/lib/unionlabs/src/tendermint/types/header.rs b/lib/unionlabs/src/tendermint/types/header.rs index d4e54f47a4..1a7f74198e 100644 --- a/lib/unionlabs/src/tendermint/types/header.rs +++ b/lib/unionlabs/src/tendermint/types/header.rs @@ -3,7 +3,7 @@ use rs_merkle::{algorithms::Sha256, Hasher, MerkleTree}; use serde::{Deserialize, Serialize}; use crate::{ - bounded_int::{BoundedI64, BoundedIntError}, + bounded::{BoundedI64, BoundedIntError}, errors::{required, InvalidLength, MissingField}, ethereum::{Address, H256}, ibc::google::protobuf::timestamp::Timestamp, diff --git a/light-clients/ethereum-light-client/src/client.rs b/light-clients/ethereum-light-client/src/client.rs index e854d5b4cb..ea58c089a9 100644 --- a/light-clients/ethereum-light-client/src/client.rs +++ b/light-clients/ethereum-light-client/src/client.rs @@ -474,7 +474,6 @@ mod test { core::commitment::merkle_root::MerkleRoot, lightclients::{cometbls, ethereum}, }, - id::{self, ConnectionId}, proof::{ClientConsensusStatePath, ClientStatePath, ConnectionPath}, IntoProto, }; @@ -1037,7 +1036,7 @@ mod test { do_verify_membership( ConnectionPath { - connection_id: ConnectionId::new(id::Connection, 0), + connection_id: "connection-0".parse().unwrap(), } .to_string(), storage_root, @@ -1072,7 +1071,7 @@ mod test { for proof in proofs { assert!(do_verify_membership( ConnectionPath { - connection_id: ConnectionId::new(id::Connection, 0), + connection_id: "connection-0".parse().unwrap(), } .to_string(), storage_root.clone(), @@ -1092,7 +1091,7 @@ mod test { assert!(do_verify_membership( ConnectionPath { - connection_id: ConnectionId::new(id::Connection, 0), + connection_id: "connection-0".parse().unwrap(), } .to_string(), storage_root, @@ -1111,7 +1110,7 @@ mod test { assert!(do_verify_membership( ConnectionPath { - connection_id: ConnectionId::new(id::Connection, 0), + connection_id: "connection-0".parse().unwrap(), } .to_string(), storage_root, @@ -1151,7 +1150,7 @@ mod test { assert_eq!( do_verify_non_membership( ConnectionPath { - connection_id: ConnectionId::new(id::Connection, 0), + connection_id: "connection-0".parse().unwrap(), } .to_string(), storage_root, diff --git a/voyager-config.json b/voyager-config.json index dff5df34eb..8340267d93 100644 --- a/voyager-config.json +++ b/voyager-config.json @@ -3,7 +3,7 @@ "ethereum-devnet": { "chain_type": "evm", "preset_base": "minimal", - "ibc_handler_address": "0xeda338e4dc46038493b885327842fd3e301cab39", + "ibc_handler_address": "0x774667629726ec1FaBEbCEc0D9139bD1C8f72a23", "signers": [ { "raw": "0x4e9444a6efd6d42725a250b650a781da2737ea308c839eaccb0f7f3dbd2fea77" diff --git a/voyager/src/chain.rs b/voyager/src/chain.rs index 6cf0070b04..af9822ab55 100644 --- a/voyager/src/chain.rs +++ b/voyager/src/chain.rs @@ -13,7 +13,6 @@ use serde::{Deserialize, Serialize}; use unionlabs::{ ethereum_consts_traits::{Mainnet, Minimal}, ibc::core::client::height::{HeightFromStrError, IsHeight}, - id, traits::{self, Chain}, }; @@ -98,9 +97,18 @@ pub trait LightClient: Send + Sync + Sized { type ClientId: traits::Id + TryFrom<::ClientId> + Into<::ClientId>; - type ClientType: id::IdType + type ClientType: Display + + FromStr + + Debug + + Clone + + PartialEq + + Serialize + + for<'de> Deserialize<'de> + + Send + + Sync + TryFrom<::ClientType> - + Into<::ClientType>; + + Into<::ClientType> + + 'static; /// The config required to construct this light client. type Config: Debug + Clone + PartialEq + Serialize + for<'de> Deserialize<'de>; @@ -224,13 +232,14 @@ macro_rules! try_from_relayer_msg { type Error = RelayerMsg; fn try_from(value: RelayerMsg) -> Result, RelayerMsg> { match value { - RelayerMsg::Lc(AnyLcMsg::$Lc(LcMsg::$LcMsg(Identified { + RelayerMsg::Lc(crate::msg::AnyLightClientIdentified::$Lc(Identified { chain_id, - data: + data: LcMsg::$LcMsg( $LcMsg::LightClientSpecific($Specific($Msg::$Var( data, ))), - }))) => Ok(Identified { chain_id, data }), + ), + })) => Ok(Identified { chain_id, data }), _ => Err(value), } } @@ -267,9 +276,9 @@ macro_rules! this_is_a_hack_look_away { ) ) => { $( - impl From> for AggregateData { - fn from(Identified { chain_id, data }: Identified<$Lc, $Ty>) -> AggregateData { - AggregateData::$Lc(Identified { + impl From> for crate::msg::AggregateData { + fn from(Identified { chain_id, data }: Identified<$Lc, $Ty>) -> crate::msg::AggregateData { + crate::msg::AggregateData::$Lc(Identified { chain_id, data: Data::LightClientSpecific(LightClientSpecificData($Msg::$Var( data, @@ -277,6 +286,22 @@ macro_rules! this_is_a_hack_look_away { }) } } + + impl TryFrom for Identified<$Lc, $Ty> { + type Error = crate::msg::AggregateData; + + fn try_from(value: crate::msg::AggregateData) -> Result, crate::msg::AggregateData> { + match value { + crate::msg::AnyLightClientIdentified::$Lc(Identified { + chain_id, + data: Data::LightClientSpecific(LightClientSpecificData($Msg::$Var( + data, + ))), + }) => Ok(Identified { chain_id, data }), + _ => Err(value), + } + } + } )+ }; diff --git a/voyager/src/chain/evm.rs b/voyager/src/chain/evm.rs index 2531b3ea98..f186edab7d 100644 --- a/voyager/src/chain/evm.rs +++ b/voyager/src/chain/evm.rs @@ -54,7 +54,7 @@ use unionlabs::{ wasm, }, }, - id::{Id, IdType}, + id::ClientId, proof::{ AcknowledgementPath, ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, ConnectionPath, IbcPath, @@ -71,7 +71,7 @@ use crate::{ QueryHeight, StateProof, }, msg::{ - aggregate::{Aggregate, LightClientSpecificAggregate}, + aggregate::{Aggregate, AnyAggregate, LightClientSpecificAggregate}, data, data::{Data, LightClientSpecificData}, fetch, @@ -80,7 +80,8 @@ use crate::{ msg::{Msg, MsgUpdateClientData}, seq, wait, wait::WaitForTimestamp, - AggregateData, AggregateReceiver, AnyLcMsg, DoAggregate, Identified, LcMsg, RelayerMsg, + AggregateData, AggregateReceiver, AnyLcMsg, AnyLightClientIdentified, DoAggregate, + Identified, LcMsg, RelayerMsg, }, queue::aggregate_data::{do_aggregate, UseAggregate}, }; @@ -186,8 +187,8 @@ impl LightClient for CometblsMainnet { type HostChain = Evm; type Counterparty = EthereumMainnet; - type ClientId = Id; - type ClientType = chain_utils::evm::Cometbls; + type ClientId = ClientId; + type ClientType = String; type Config = CometblsConfig; @@ -234,8 +235,8 @@ impl LightClient for CometblsMinimal { type HostChain = Evm; type Counterparty = EthereumMinimal; - type ClientId = Id; - type ClientType = chain_utils::evm::Cometbls; + type ClientId = ClientId; + type ClientType = String; type Config = CometblsConfig; @@ -291,8 +292,8 @@ where Aggregate = CometblsAggregateMsg, >, LightClientSpecificFetch: From>, - AnyLcMsg: From>, - AggregateReceiver: From)>, + AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, { [RelayerMsg::Aggregate { queue: [seq([fetch( @@ -712,6 +713,24 @@ pub struct FinalityUpdate(pub LightClientFinalityUpdate); #[serde(bound(serialize = "", deserialize = ""))] pub struct LightClientUpdates(pub Vec>); +// fn outer() +// where +// C: ChainSpec, +// L: LightClient< +// HostChain = Evm, +// Aggregate = CometblsAggregateMsg, +// Fetch = CometblsFetchMsg, +// >, +// { +// fn lc_specific< +// T: TryFrom, Error = AnyLightClientIdentified> +// + Into>, +// >() { +// } + +// lc_specific::>>() +// } + impl DoAggregate for CometblsAggregateMsg where C: ChainSpec, @@ -730,11 +749,11 @@ where Identified>: TryFrom + Into, - AnyLcMsg: From>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, AggregateData: From)>, - AggregateReceiver: From>>, + AggregateReceiver: From)>, { fn do_aggregate( Identified { chain_id, data }: Identified, @@ -769,6 +788,8 @@ where #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Args)] pub struct CometblsConfig { + #[arg(long)] + pub client_type: String, #[arg(long)] pub cometbls_client_address: Address, } @@ -869,7 +890,7 @@ where Fetch = CometblsFetchMsg, Aggregate = CometblsAggregateMsg, >, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From>>, { // When we fetch the update at this height, the `next_sync_committee` will @@ -997,7 +1018,7 @@ where ), Msg::CreateClient(data) => { let register_client_result = ibc_handler.register_client( - L::ClientType::TYPE.to_string(), + data.config.client_type.clone(), data.config.cometbls_client_address.clone().into(), ); @@ -1016,8 +1037,7 @@ where ibc_handler, CreateClientCall { msg: contracts::shared_types::MsgCreateClient { - // TODO: Add this to the config - client_type: L::ClientType::TYPE.to_string(), + client_type: data.config.client_type, client_state_bytes: data.msg.client_state.into_proto_bytes().into(), consensus_state_bytes: data .msg @@ -1114,7 +1134,7 @@ where C: ChainSpec, L: LightClient, Fetch = CometblsFetchMsg, Data = CometblsDataMsg>, LightClientSpecificData: From>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { let msg = match msg { CometblsFetchMsg::FetchFinalityUpdate(PhantomData {}) => CometblsDataMsg::FinalityUpdate( @@ -1472,8 +1492,8 @@ where Identified>: TryFrom + Into, - AnyLcMsg: From>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, L: LightClient>, C: ChainSpec, @@ -1596,7 +1616,7 @@ where >, Identified>: TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From>>, { type AggregatedData = HList![ @@ -1628,18 +1648,16 @@ where // Eth chain is more than 1 signature period ahead of us. We need to do sync committee // updates until we reach the `target_period - 1`. RelayerMsg::Aggregate { - queue: [RelayerMsg::Lc(AnyLcMsg::from(LcMsg::::Fetch( - Identified { - chain_id, - data: Fetch::LightClientSpecific(LightClientSpecificFetch( - CometblsFetchMsg::FetchLightClientUpdates(FetchLightClientUpdates { - trusted_period, - target_period, - __marker: PhantomData, - }), - )), - }, - )))] + queue: [fetch::( + chain_id, + LightClientSpecificFetch(CometblsFetchMsg::FetchLightClientUpdates( + FetchLightClientUpdates { + trusted_period, + target_period, + __marker: PhantomData, + }, + )), + )] .into(), data: [].into(), receiver: AggregateReceiver::from(Identified { @@ -1670,7 +1688,7 @@ where Identified>: TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From>>, { type AggregatedData = HList![ diff --git a/voyager/src/chain/union.rs b/voyager/src/chain/union.rs index a037b3f480..4254824fbe 100644 --- a/voyager/src/chain/union.rs +++ b/voyager/src/chain/union.rs @@ -6,7 +6,7 @@ use std::{ use chain_utils::{ evm::Evm, - union::{BroadcastTxCommitError, Union, Wasm}, + union::{BroadcastTxCommitError, Union}, }; use clap::Args; use frame_support_procedural::{CloneNoBound, DebugNoBound, PartialEqNoBound}; @@ -21,7 +21,7 @@ use protos::{ use serde::{Deserialize, Serialize}; use tendermint_rpc::Client; use unionlabs::{ - bounded_int::BoundedI64, + bounded::BoundedI64, ethereum::{Address, H256, H512}, ethereum_consts_traits::{ChainSpec, Mainnet, Minimal}, ibc::{ @@ -29,7 +29,7 @@ use unionlabs::{ google::protobuf::{any::Any, timestamp::Timestamp}, lightclients::{cometbls, ethereum, wasm}, }, - id::Id, + id::ClientId, proof::{ AcknowledgementPath, ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, ConnectionPath, IbcPath, @@ -67,7 +67,8 @@ use crate::{ msg::{Msg, MsgUpdateClientData}, seq, wait, wait::WaitForBlock, - AggregateData, AggregateReceiver, AnyLcMsg, DoAggregate, Identified, LcMsg, RelayerMsg, + AggregateData, AggregateReceiver, AnyLcMsg, AnyLightClientIdentified, DoAggregate, + Identified, LcMsg, RelayerMsg, }, queue::aggregate_data::{do_aggregate, UseAggregate}, }; @@ -92,8 +93,8 @@ impl LightClient for EthereumMinimal { type HostChain = Union; type Counterparty = CometblsMinimal; - type ClientId = Id; - type ClientType = Wasm; + type ClientId = ClientId; + type ClientType = String; type Config = EthereumConfig; @@ -140,7 +141,7 @@ async fn do_fetch(union: &Union, msg: EthereumFetchMsg) -> Vec, Data = EthereumDataMsg>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateData: From)>, AggregateReceiver: From)>, { @@ -362,7 +363,7 @@ where Fetch = EthereumFetchMsg, >, L::Counterparty: LightClient>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateData: From)>, AggregateReceiver: From)>, { @@ -420,8 +421,8 @@ impl LightClient for EthereumMainnet { type HostChain = Union; type Counterparty = CometblsMainnet; - type ClientId = Id; - type ClientType = Wasm; + type ClientId = ClientId; + type ClientType = String; type Config = EthereumConfig; @@ -619,7 +620,7 @@ where Identified>: UseAggregate, Identified>: UseAggregate, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { fn do_aggregate( @@ -1022,7 +1023,7 @@ where TryFrom + Into, Identified>: TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { type AggregatedData = HList![Identified>, Identified>, Identified>]; @@ -1210,8 +1211,8 @@ where Identified>: TryFrom + Into, - AnyLcMsg: From>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![Identified>]; diff --git a/voyager/src/msg.rs b/voyager/src/msg.rs index 4f7e39eb1b..c9ca77dfed 100644 --- a/voyager/src/msg.rs +++ b/voyager/src/msg.rs @@ -20,7 +20,7 @@ use crate::{ LightClient, }, msg::{ - aggregate::{Aggregate, AnyAggregate}, + aggregate::AnyAggregate, data::{AnyData, Data}, event::{AnyEvent, Event}, fetch::{AnyFetch, Fetch}, @@ -63,7 +63,7 @@ pub enum DeferPoint { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[allow(clippy::large_enum_variant)] pub enum RelayerMsg { - Lc(AnyLcMsg), + Lc(AnyLightClientIdentified), DeferUntil { point: DeferPoint, seconds: u64, @@ -82,12 +82,44 @@ pub enum RelayerMsg { /// Messages that are expected to resolve to [`Data`]. queue: VecDeque, /// The resolved data messages. - data: VecDeque, + data: VecDeque>, /// The message that will utilize the aggregated data. - receiver: AggregateReceiver, + receiver: AnyLightClientIdentified, }, } +impl TryFrom> for AnyLightClientIdentified { + type Error = AnyLightClientIdentified; + + fn try_from(value: AnyLightClientIdentified) -> Result { + match value { + AnyLightClientIdentified::EthereumMainnet(i) => >::try_from(i.data) + .map(|d| Identified::new(i.chain_id.clone(), d)) + .map(AnyLightClientIdentified::EthereumMainnet) + .map_err(|l| Identified::new(i.chain_id, l)) + .map_err(AnyLightClientIdentified::EthereumMainnet), + AnyLightClientIdentified::EthereumMinimal(i) => >::try_from(i.data) + .map(|d| Identified::new(i.chain_id.clone(), d)) + .map(AnyLightClientIdentified::EthereumMinimal) + .map_err(|l| Identified::new(i.chain_id, l)) + .map_err(AnyLightClientIdentified::EthereumMinimal), + AnyLightClientIdentified::CometblsMainnet(i) => >::try_from(i.data) + .map(|d| Identified::new(i.chain_id, d)) + .map(AnyLightClientIdentified::CometblsMainnet) + .map_err(|l| Identified::new(i.chain_id, l)) + .map_err(AnyLightClientIdentified::CometblsMainnet), + AnyLightClientIdentified::CometblsMinimal(i) => >::try_from(i.data) + .map(|d| Identified::new(i.chain_id, d)) + .map(AnyLightClientIdentified::CometblsMinimal) + .map_err(|l| Identified::new(i.chain_id, l)) + .map_err(AnyLightClientIdentified::CometblsMinimal), + } + } +} + +pub type AggregateReceiver = AnyLightClientIdentified; +pub type AggregateData = AnyLightClientIdentified; + impl std::fmt::Display for RelayerMsg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -135,122 +167,62 @@ impl std::fmt::Display for RelayerMsg { } } -enum_variants_conversions! { - #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] - pub enum AggregateData { - // The 08-wasm client tracking the state of Evm. - EthereumMainnet(identified!(Data)), - // The 08-wasm client tracking the state of Evm. - EthereumMinimal(identified!(Data)), - // The solidity client on Evm tracking the state of Union. - CometblsMainnet(identified!(Data)), - // The solidity client on Evm tracking the state of Union. - CometblsMinimal(identified!(Data)), - } -} - -impl Display for AggregateData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AggregateData::EthereumMainnet(data) => { - write!(f, "Data::EthereumMainnet({}, {})", data.chain_id, data.data) - } - AggregateData::EthereumMinimal(data) => { - write!(f, "Data::EthereumMinimal({}, {})", data.chain_id, data.data) - } - AggregateData::CometblsMainnet(data) => { - write!(f, "Data::CometblsMainnet({}, {})", data.chain_id, data.data) - } - AggregateData::CometblsMinimal(data) => { - write!(f, "Data::CometblsMinimal({}, {})", data.chain_id, data.data) - } - } - } -} - -impl TryFrom for AggregateData { +impl TryFrom for AnyLightClientIdentified { type Error = RelayerMsg; fn try_from(value: RelayerMsg) -> Result { match value { - RelayerMsg::Lc(AnyLcMsg::EthereumMainnet(LcMsg::Data(data))) => { - Ok(AggregateData::EthereumMainnet(data)) - } - RelayerMsg::Lc(AnyLcMsg::EthereumMinimal(LcMsg::Data(data))) => { - Ok(AggregateData::EthereumMinimal(data)) - } - RelayerMsg::Lc(AnyLcMsg::CometblsMainnet(LcMsg::Data(data))) => { - Ok(AggregateData::CometblsMainnet(data)) - } - RelayerMsg::Lc(AnyLcMsg::CometblsMinimal(LcMsg::Data(data))) => { - Ok(AggregateData::CometblsMinimal(data)) - } + RelayerMsg::Lc(ok) => Ok(ok), _ => Err(value), } } } -enum_variants_conversions! { - #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, derive_more::Display)] - // TODO: Rename this - pub enum AggregateReceiver { - // The 08-wasm client tracking the state of Evm. - #[display(fmt = "EthereumMainnet({}, {})", "_0.chain_id", "_0.data")] - EthereumMainnet(identified!(Aggregate)), - // The 08-wasm client tracking the state of Evm. - #[display(fmt = "EthereumMinimal({}, {})", "_0.chain_id", "_0.data")] - EthereumMinimal(identified!(Aggregate)), - // The solidity client on Evm tracking the state of Union. - #[display(fmt = "CometblsMainnet({}, {})", "_0.chain_id", "_0.data")] - CometblsMainnet(identified!(Aggregate)), - // The solidity client on Evm tracking the state of Union. - #[display(fmt = "CometblsMinimal({}, {})", "_0.chain_id", "_0.data")] - CometblsMinimal(identified!(Aggregate)), - } -} - -impl TryFrom for AnyLcMsg { +impl TryFrom for AnyLightClientIdentified { type Error = RelayerMsg; fn try_from(value: RelayerMsg) -> Result { match value { - RelayerMsg::Lc(ok) => Ok(ok), + RelayerMsg::Lc(any_lc_msg) => { + AnyLightClientIdentified::::try_from(any_lc_msg).map_err(RelayerMsg::Lc) + } _ => Err(value), } } } -impl From for RelayerMsg { - fn from(value: AnyLcMsg) -> Self { +impl From> for RelayerMsg { + fn from(value: AnyLightClientIdentified) -> Self { Self::Lc(value) } } impl TryFrom for LcMsg where - AnyLcMsg: TryFrom + Into, - LcMsg: TryFrom + Into, + LcMsg: TryFrom, Error = AnyLightClientIdentified> + + Into>, { type Error = RelayerMsg; fn try_from(value: RelayerMsg) -> Result { - LcMsg::::try_from(AnyLcMsg::try_from(value)?).map_err(Into::into) + LcMsg::::try_from(>::try_from(value)?) + .map_err(Into::into) } } impl From> for RelayerMsg where - AnyLcMsg: From>, + AnyLightClientIdentified: From>, { fn from(value: LcMsg) -> Self { - RelayerMsg::Lc(AnyLcMsg::from(value)) + RelayerMsg::Lc(>::from(value)) } } macro_rules! any_enum { ( $(#[doc = $outer_doc:literal])* - #[any = $Any:ident($AnyInner:ty)] + #[any = $Any:ident] pub enum $Enum:ident { $( $(#[doc = $doc:literal])* @@ -264,6 +236,7 @@ macro_rules! any_enum { #[derive(frame_support_procedural::DebugNoBound, frame_support_procedural::CloneNoBound, frame_support_procedural::PartialEqNoBound, serde::Serialize, serde::Deserialize)] #[serde(bound(serialize = "", deserialize = ""))] $(#[doc = $outer_doc])* + #[allow(clippy::large_enum_variant)] pub enum $Enum { $( $(#[doc = $doc])* @@ -276,7 +249,60 @@ macro_rules! any_enum { pub enum $Any {} impl crate::msg::AnyLightClient for $Any { - type Inner = $AnyInner; + type Inner = $Enum; + } + + impl TryFrom> for $Enum { + type Error = crate::msg::LcMsg; + + fn try_from(value: crate::msg::LcMsg) -> Result { + if let crate::msg::LcMsg::$Enum(t) = value { + Ok(t) + } else { + Err(value) + } + } + } + + impl From>> for crate::msg::RelayerMsg + where + crate::msg::LcMsg: From>, + crate::msg::AnyLightClientIdentified: + From>> + { + fn from(value: crate::msg::Identified>) -> Self { + Self::Lc( + >::from( + crate::msg::Identified { + chain_id: value.chain_id, data: crate::msg::LcMsg::from(value.data) + } + ) + ) + } + } + + impl TryFrom for crate::msg::Identified> + where + crate::msg::AnyLightClientIdentified: TryFrom + Into, + crate::msg::Identified>: TryFrom, Error = crate::msg::AnyLightClientIdentified> + + Into>, + crate::msg::InnerOf<$Any, L>: TryFrom, Error = crate::msg::LcMsg> + Into>, + { + type Error = crate::msg::RelayerMsg; + fn try_from(value: crate::msg::RelayerMsg) -> Result { + let any_lc_msg = >::try_from(value)?; + let identified_lc_msg = >>::try_from(any_lc_msg) + .map_err(>::from)?; + let data = + >::try_from(identified_lc_msg.data).map_err(|x: crate::msg::LcMsg| { + Into::>::into(crate::msg::Identified::>::new( + identified_lc_msg.chain_id.clone(), + x, + )) + })?; + + Ok(crate::msg::Identified::new(identified_lc_msg.chain_id, data)) + } } $( @@ -300,46 +326,43 @@ macro_rules! any_enum { impl TryInto> for crate::msg::RelayerMsg where - crate::msg::AnyLcMsg: TryFrom + Into, - crate::msg::LcMsg: TryFrom + Into, + crate::msg::AnyLightClientIdentified: TryFrom + Into, + crate::msg::LcMsg: TryFrom, Error = crate::msg::AnyLightClientIdentified> + Into>, crate::msg::Identified: TryFrom, Error = crate::msg::LcMsg> + Into>, { type Error = crate::msg::RelayerMsg; fn try_into(self) -> Result, crate::msg::RelayerMsg> { - crate::msg::AnyLcMsg::try_from(self) + >::try_from(self) .and_then(|x| >::try_from(x).map_err(Into::into)) .and_then(|x| { >::try_from(x) .map_err(Into::>::into) - .map_err(Into::::into) + .map_err(Into::>::into) .map_err(Into::::into) }) } } - impl TryFrom> for crate::msg::Identified { + impl TryFrom> for $VariantInner { type Error = crate::msg::LcMsg; fn try_from(value: crate::msg::LcMsg) -> Result> { match value { - crate::msg::LcMsg::$Enum(crate::msg::Identified { - chain_id, - data: $Enum::$Variant(data), - }) => Ok(crate::msg::Identified { chain_id, data }), + crate::msg::LcMsg::$Enum($Enum::$Variant(data)) => Ok(data), _ => Err(value), } } } - impl TryFrom for crate::msg::Identified + impl TryFrom> for crate::msg::Identified where - crate::msg::LcMsg: TryFrom + Into, + crate::msg::LcMsg: TryFrom, Error = crate::msg::AnyLightClientIdentified> + Into>, Self: TryFrom, Error = crate::msg::LcMsg> + Into>, { - type Error = crate::msg::AnyLcMsg; + type Error = crate::msg::AnyLightClientIdentified; - fn try_from(value: crate::msg::AnyLcMsg) -> Result { + fn try_from(value: crate::msg::AnyLightClientIdentified) -> Result> { crate::msg::LcMsg::::try_from(value).and_then(|x| Self::try_from(x).map_err(Into::into)) } } @@ -350,6 +373,7 @@ macro_rules! any_enum { pub(crate) use any_enum; +// TODO: Move to file pub mod aggregate { use std::fmt::Display; @@ -366,12 +390,12 @@ pub mod aggregate { use super::ChainIdOf; use crate::{ chain::{ChainOf, HeightOf, LightClient}, - msg::{fetch::FetchStateProof, identified}, + msg::fetch::FetchStateProof, }; any_enum! { /// Aggregate data, using data from [`AggregateData`] - #[any = AnyAggregate(identified!(Aggregate))] + #[any = AnyAggregate] pub enum Aggregate { ConnectionOpenTry(AggregateConnectionOpenTry), ConnectionOpenAck(AggregateConnectionOpenAck), @@ -657,27 +681,216 @@ pub mod aggregate { } } -impl TryFrom for Identified -where - T: TryFrom, Error = Data> + Into> + Debug + Clone + PartialEq, - identified!(Data): TryFrom + Into, +// impl TryFrom for Identified +// where +// T: TryFrom, Error = Data> + Into> + Debug + Clone + PartialEq, +// identified!(Data): TryFrom + Into, +// { +// type Error = AggregateData; + +// fn try_from(value: AggregateData) -> Result { +// let Identified { chain_id, data } = )>::try_from(value)?; + +// match T::try_from(data) { +// Ok(t) => Ok(Identified { chain_id, data: t }), +// Err(data) => Err(Identified { chain_id, data }.into()), +// } +// } +// } + +pub trait AnyLightClient { + type Inner: Debug + + Display + + Clone + + PartialEq + + Serialize + + for<'de> Deserialize<'de>; +} + +#[derive( + DebugNoBound, CloneNoBound, PartialEqNoBound, Serialize, Deserialize, derive_more::Display, +)] +#[serde(bound(serialize = "", deserialize = ""))] +#[allow(clippy::large_enum_variant)] +pub enum AnyLightClientIdentified { + // The 08-wasm client tracking the state of Evm. + #[display(fmt = "EthereumMainnet({}, {})", "_0.chain_id", "_0.data")] + EthereumMainnet(Identified>), + // The 08-wasm client tracking the state of Evm. + #[display(fmt = "EthereumMinimal({}, {})", "_0.chain_id", "_0.data")] + EthereumMinimal(Identified>), + // The solidity client on Evm tracking the state of Union. + #[display(fmt = "CometblsMainnet({}, {})", "_0.chain_id", "_0.data")] + CometblsMainnet(Identified>), + // The solidity client on Evm tracking the state of Union. + #[display(fmt = "CometblsMinimal({}, {})", "_0.chain_id", "_0.data")] + CometblsMinimal(Identified>), +} + +// pub trait Map { +// type This; + +// fn map(this: Self::This, f: impl FnOnce(T) -> U) -> Self::This; +// } + +// impl Map for Option { +// type This = Option; + +// fn map(this: Self::This, f: impl FnOnce(T) -> U) -> Self::This { +// this.map(f) +// } +// } + +// impl Map for Result { +// type This = Result; + +// fn map(this: Self::This, f: impl FnOnce(T) -> U) -> Self::This { +// this.map(f) +// } +// } + +// impl AnyLightClientIdentified { +// pub fn map(self, a: A) -> A::Output> +// where +// U: AnyLightClient, +// A: AnyLightClientMapper, +// // >::Output<> +// >::Output< +// Identified>, +// >: Map< +// Identified>, +// This<>::Output< +// Identified>, +// >> = >::Output< +// Identified>, +// >, +// >, +// { +// use AnyLightClientIdentified as AnyLc; + +// let t = match self { +// AnyLc::EthereumMainnet(t) => { +// let t: A::Output> = > as Map<_>>::map::>(a.map(t), |x| AnyLc::EthereumMainnet(x)); +// } +// AnyLc::EthereumMinimal(t) => { +// > as Map<_>>::map::>(a.map(t), |x| AnyLc::EthereumMinimal(x)) +// } +// AnyLc::CometblsMainnet(t) => { +// > as Map<_>>::map::>(a.map(t), |x| AnyLc::CometblsMainnet(x)) +// } +// AnyLc::CometblsMinimal(t) => { +// > as Map<_>>::map::>(a.map(t), |x| AnyLc::CometblsMinimal(x)) +// } +// // EthereumMainnet(t) => a.map(t), +// // EthereumMinimal(t) => a.map(t), +// // CometblsMainnet(t) => a.map(t), +// // CometblsMinimal(t) => a.map(t), +// }; + +// todo!() +// } +// } + +// trait AnyLightClientMapper { +// type Output: Map = Self::Output>; + +// fn map( +// self, +// t: Identified>, +// ) -> Self::Output>>; + +// // fn map2( +// // self, +// // t: Identified>, +// // ) -> Self::Output>>; +// } + +impl From>> + for AnyLightClientIdentified { - type Error = AggregateData; + fn from(v: Identified>) -> Self { + Self::EthereumMainnet(v) + } +} + +impl TryFrom> + for Identified> +{ + type Error = AnyLightClientIdentified; + fn try_from(v: AnyLightClientIdentified) -> Result { + if let AnyLightClientIdentified::EthereumMainnet(v) = v { + Ok(v) + } else { + Err(v) + } + } +} - fn try_from(value: AggregateData) -> Result { - let Identified { chain_id, data } = )>::try_from(value)?; +impl From>> + for AnyLightClientIdentified +{ + fn from(v: Identified>) -> Self { + Self::EthereumMinimal(v) + } +} - match T::try_from(data) { - Ok(t) => Ok(Identified { chain_id, data: t }), - Err(data) => Err(Identified { chain_id, data }.into()), +impl TryFrom> + for Identified> +{ + type Error = AnyLightClientIdentified; + fn try_from(v: AnyLightClientIdentified) -> Result { + if let AnyLightClientIdentified::EthereumMinimal(v) = v { + Ok(v) + } else { + Err(v) } } } -pub trait AnyLightClient { - type Inner: Debug + Clone + PartialEq + Serialize + for<'de> Deserialize<'de>; +impl From>> + for AnyLightClientIdentified +{ + fn from(v: Identified>) -> Self { + Self::CometblsMainnet(v) + } } +impl TryFrom> + for Identified> +{ + type Error = AnyLightClientIdentified; + fn try_from(v: AnyLightClientIdentified) -> Result { + if let AnyLightClientIdentified::CometblsMainnet(v) = v { + Ok(v) + } else { + Err(v) + } + } +} + +impl From>> + for AnyLightClientIdentified +{ + fn from(v: Identified>) -> Self { + Self::CometblsMinimal(v) + } +} + +impl TryFrom> + for Identified> +{ + type Error = AnyLightClientIdentified; + fn try_from(v: AnyLightClientIdentified) -> Result { + if let AnyLightClientIdentified::CometblsMinimal(v) = v { + Ok(v) + } else { + Err(v) + } + } +} + +pub type AnyDataIdentified = AnyLightClientIdentified; + macro_rules! enum_variants_conversions { ( $(#[$meta:meta])* @@ -759,6 +972,7 @@ mod tests { data::Data, event, event::{Event, IbcEvent}, + fetch, fetch::{ AnyFetch, Fetch, FetchSelfClientState, FetchSelfConsensusState, FetchTrustedClientState, @@ -880,18 +1094,25 @@ mod tests { )); print_json(RelayerMsg::Timeout { - timeout_timestamp: 1, - msg: Box::new(RelayerMsg::Lc(AnyLcMsg::CometblsMinimal(LcMsg::Event( - Identified { - chain_id: eth_chain_id, - data: crate::msg::event::Event::Command( - crate::msg::event::Command::UpdateClient { - client_id: parse!("cometbls-0"), - counterparty_client_id: parse!("08-wasm-0"), - }, - ), + timeout_timestamp: u64::MAX, + msg: Box::new(event::( + eth_chain_id, + crate::msg::event::Command::UpdateClient { + client_id: parse!("cometbls-0"), + counterparty_client_id: parse!("08-wasm-0"), }, - )))), + )), + }); + + print_json(RelayerMsg::Timeout { + timeout_timestamp: u64::MAX, + msg: Box::new(event::( + union_chain_id.clone(), + crate::msg::event::Command::UpdateClient { + client_id: parse!("08-wasm-0"), + counterparty_client_id: parse!("cometbls-0"), + }, + )), }); println!("\ncreate client msgs\n"); @@ -900,18 +1121,18 @@ mod tests { [ RelayerMsg::Aggregate { queue: [ - RelayerMsg::Lc(AnyLcMsg::EthereumMinimal(LcMsg::Fetch(Identified { - chain_id: union_chain_id.clone(), - data: Fetch::SelfClientState(FetchSelfClientState { + fetch::( + union_chain_id.clone(), + FetchSelfClientState { at: QueryHeight::Latest, - }), - }))), - RelayerMsg::Lc(AnyLcMsg::EthereumMinimal(LcMsg::Fetch(Identified { - chain_id: union_chain_id.clone(), - data: Fetch::SelfConsensusState(FetchSelfConsensusState { + }, + ), + fetch::( + union_chain_id.clone(), + FetchSelfConsensusState { at: QueryHeight::Latest, - }), - }))), + }, + ) ] .into(), data: [].into_iter().collect(), @@ -919,6 +1140,7 @@ mod tests { chain_id: eth_chain_id, data: Aggregate::CreateClient(AggregateCreateClient { config: CometblsConfig { + client_type: "cometbls".to_string(), cometbls_client_address: Address(hex!( "83428c7db9815f482a39a1715684dcf755021997" )), @@ -928,18 +1150,18 @@ mod tests { }, RelayerMsg::Aggregate { queue: [ - RelayerMsg::Lc(AnyLcMsg::CometblsMinimal(LcMsg::Fetch(Identified { - chain_id: eth_chain_id, - data: Fetch::SelfClientState(FetchSelfClientState { + fetch::( + eth_chain_id, + FetchSelfClientState { at: QueryHeight::Latest, - }), - }))), - RelayerMsg::Lc(AnyLcMsg::CometblsMinimal(LcMsg::Fetch(Identified { - chain_id: eth_chain_id, - data: Fetch::SelfConsensusState(FetchSelfConsensusState { + }, + ), + fetch::( + eth_chain_id, + FetchSelfConsensusState { at: QueryHeight::Latest, - }), - }))), + }, + ) ] .into(), data: [].into_iter().collect(), @@ -1000,42 +1222,30 @@ pub(crate) use identified; #[serde(bound(serialize = "", deserialize = ""))] #[allow(clippy::large_enum_variant)] pub enum LcMsg { - #[display(fmt = "Event({}, {})", "_0.chain_id", "_0.data")] + #[display(fmt = "Event({})", "_0")] Event(InnerOf), // data that has been read - #[display(fmt = "Data({}, {})", "_0.chain_id", "_0.data")] + #[display(fmt = "Data({})", "_0")] Data(InnerOf), // read - #[display(fmt = "Fetch({}, {})", "_0.chain_id", "_0.data")] + #[display(fmt = "Fetch({})", "_0")] Fetch(InnerOf), // write - #[display(fmt = "Msg({}, {})", "_0.chain_id", "_0.data")] + #[display(fmt = "Msg({})", "_0")] Msg(InnerOf), - #[display(fmt = "Wait({}, {})", "_0.chain_id", "_0.data")] + #[display(fmt = "Wait({})", "_0")] Wait(InnerOf), // REVIEW: Does this make sense as a top-level message? - #[display(fmt = "Aggregate({}, {})", "_0.chain_id", "_0.data")] + #[display(fmt = "Aggregate({})", "_0")] Aggregate(InnerOf), } pub type InnerOf = ::Inner; -enum_variants_conversions! { - #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, derive_more::Display)] - pub enum AnyLcMsg { - // The 08-wasm client tracking the state of Evm. - #[display(fmt = "EthereumMainnet({})", "_0")] - EthereumMainnet(LcMsg), - // The 08-wasm client tracking the state of Evm. - #[display(fmt = "EthereumMinimal({})", "_0")] - EthereumMinimal(LcMsg), - // The solidity client on Evm tracking the state of Union. - #[display(fmt = "CometblsMainnet({})", "_0")] - CometblsMainnet(LcMsg), - // The solidity client on Evm tracking the state of Union. - #[display(fmt = "CometblsMinimal({})", "_0")] - CometblsMinimal(LcMsg), - } +pub enum AnyLcMsg {} + +impl AnyLightClient for AnyLcMsg { + type Inner = LcMsg; } #[derive(DebugNoBound, CloneNoBound, PartialEqNoBound, Serialize, Deserialize)] @@ -1043,6 +1253,8 @@ enum_variants_conversions! { serialize = "Data: ::serde::Serialize", deserialize = "Data: for<'d> Deserialize<'d>" ))] +// TODO: `Data: AnyLightClient` +// prerequisites: derive macro for AnyLightClient pub struct Identified { pub chain_id: ChainIdOf, pub data: Data, @@ -1058,7 +1270,10 @@ pub trait DoAggregate: Sized + Debug + Clone + PartialEq where L: LightClient, { - fn do_aggregate(_: Identified, _: VecDeque) -> Vec; + fn do_aggregate( + _: Identified, + _: VecDeque>, + ) -> Vec; } // helper fns @@ -1080,50 +1295,50 @@ pub fn defer(timestamp: u64) -> RelayerMsg { pub fn fetch(chain_id: ChainIdOf, t: impl Into>) -> RelayerMsg where - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { - RelayerMsg::Lc(AnyLcMsg::from(LcMsg::Fetch(Identified::new( + RelayerMsg::Lc(AnyLightClientIdentified::from(Identified::new( chain_id, - t.into(), - )))) + LcMsg::Fetch(t.into()), + ))) } pub fn msg(chain_id: ChainIdOf, t: impl Into>) -> RelayerMsg where - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { - RelayerMsg::Lc(AnyLcMsg::from(LcMsg::Msg(Identified::new( + RelayerMsg::Lc(AnyLightClientIdentified::from(Identified::new( chain_id, - t.into(), - )))) + LcMsg::Msg(t.into()), + ))) } -pub fn event(chain_id: ChainIdOf, t: impl Into>) -> RelayerMsg +pub fn data(chain_id: ChainIdOf, t: impl Into>) -> RelayerMsg where - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { - RelayerMsg::Lc(AnyLcMsg::from(LcMsg::Event(Identified::new( + RelayerMsg::Lc(AnyLightClientIdentified::from(Identified::new( chain_id, - t.into(), - )))) + LcMsg::Data(t.into()), + ))) } pub fn wait(chain_id: ChainIdOf, t: impl Into>) -> RelayerMsg where - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { - RelayerMsg::Lc(AnyLcMsg::from(LcMsg::Wait(Identified::new( + RelayerMsg::Lc(AnyLightClientIdentified::from(Identified::new( chain_id, - t.into(), - )))) + LcMsg::Wait(t.into()), + ))) } -pub fn data(chain_id: ChainIdOf, t: impl Into>) -> RelayerMsg +pub fn event(chain_id: ChainIdOf, t: impl Into>) -> RelayerMsg where - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { - RelayerMsg::Lc(AnyLcMsg::from(LcMsg::Data(Identified::new( + RelayerMsg::Lc(AnyLightClientIdentified::from(Identified::new( chain_id, - t.into(), - )))) + LcMsg::Event(t.into()), + ))) } diff --git a/voyager/src/msg/data.rs b/voyager/src/msg/data.rs index d946cb34ee..e2d385a9d6 100644 --- a/voyager/src/msg/data.rs +++ b/voyager/src/msg/data.rs @@ -14,12 +14,15 @@ use unionlabs::{ use crate::{ chain::{ChainOf, ClientStateOf, ConsensusStateOf, HeaderOf, HeightOf, LightClient}, - msg::{any_enum, fetch::FetchPacketAcknowledgement, identified, StateProofOf}, + msg::{ + any_enum, fetch::FetchPacketAcknowledgement, identified, AnyLightClientIdentified, + StateProofOf, + }, }; any_enum! { /// Data that will likely be used in a [`RelayerMsg::Aggregate`]. - #[any = AnyData(identified!(Data))] + #[any = AnyData] pub enum Data { SelfClientState(SelfClientState), SelfConsensusState(SelfConsensusState), @@ -159,6 +162,29 @@ macro_rules! data_msg { }) } } + + impl TryFrom> + for crate::msg::Identified> + where + identified!(Data): TryFrom< + crate::msg::AnyLightClientIdentified, + Error = crate::msg::AnyLightClientIdentified, + > + Into>, + { + type Error = AnyLightClientIdentified; + + fn try_from(value: crate::msg::AnyLightClientIdentified) -> Result { + let crate::msg::Identified { chain_id, data } = + >>::try_from(value)?; + + Ok(crate::msg::Identified::new( + chain_id.clone(), + <$Ty>::try_from(data).map_err(|x: Data| { + Into::>::into(crate::msg::Identified::new(chain_id, x)) + })?, + )) + } + } )+ }; } diff --git a/voyager/src/msg/event.rs b/voyager/src/msg/event.rs index 12efaa73cd..a475ecbc30 100644 --- a/voyager/src/msg/event.rs +++ b/voyager/src/msg/event.rs @@ -6,11 +6,11 @@ use unionlabs::ethereum::H256; use crate::{ chain::{ChainOf, HeightOf, LightClient}, - msg::{any_enum, identified}, + msg::any_enum, }; any_enum! { - #[any = AnyEvent(identified!(Event))] + #[any = AnyEvent] pub enum Event { Ibc(IbcEvent), Command(Command), diff --git a/voyager/src/msg/fetch.rs b/voyager/src/msg/fetch.rs index b246fb8a1d..336d6265bb 100644 --- a/voyager/src/msg/fetch.rs +++ b/voyager/src/msg/fetch.rs @@ -11,12 +11,12 @@ use unionlabs::{ use crate::{ chain::{ChainOf, HeightOf, LightClient, QueryHeight}, - msg::{any_enum, identified, ChainIdOf}, + msg::{any_enum, ChainIdOf}, }; any_enum! { /// Fetch some data that will likely be used in a [`RelayerMsg::Aggregate`]. - #[any = AnyFetch(identified!(Fetch))] + #[any = AnyFetch] pub enum Fetch { TrustedClientState(FetchTrustedClientState), diff --git a/voyager/src/msg/msg.rs b/voyager/src/msg/msg.rs index 2e0b8f6a5b..56b959e46c 100644 --- a/voyager/src/msg/msg.rs +++ b/voyager/src/msg/msg.rs @@ -19,12 +19,12 @@ use unionlabs::ibc::core::{ use crate::{ chain::{ChainOf, ClientStateOf, ConsensusStateOf, HeaderOf, HeightOf, LightClient}, - msg::{any_enum, identified}, + msg::any_enum, }; any_enum! { /// Defines messages that are sent *to* the lightclient `L`. - #[any = AnyMsg(identified!(Msg))] + #[any = AnyMsg] pub enum Msg { ConnectionOpenInit(MsgConnectionOpenInitData), ConnectionOpenTry(MsgConnectionOpenTryData), diff --git a/voyager/src/msg/wait.rs b/voyager/src/msg/wait.rs index 2bf1a37f65..c8c63cce63 100644 --- a/voyager/src/msg/wait.rs +++ b/voyager/src/msg/wait.rs @@ -5,12 +5,12 @@ use serde::{Deserialize, Serialize}; use crate::{ chain::{ChainOf, HeightOf, LightClient}, - msg::{any_enum, identified, ChainIdOf}, + msg::{any_enum, ChainIdOf}, }; any_enum! { /// Defines messages that are sent *to* the lightclient `L`. - #[any = AnyWait(identified!(Wait))] + #[any = AnyWait] pub enum Wait { Block(WaitForBlock), Timestamp(WaitForTimestamp), diff --git a/voyager/src/queue.rs b/voyager/src/queue.rs index a8bc6cbc54..b52da9b704 100644 --- a/voyager/src/queue.rs +++ b/voyager/src/queue.rs @@ -3,15 +3,12 @@ use std::{ error::Error, fmt::{Debug, Display}, marker::PhantomData, + str::FromStr, sync::{Arc, Mutex}, time::{Duration, SystemTime, UNIX_EPOCH}, }; -use chain_utils::{ - evm::{Evm, EvmClientId, EvmClientType}, - union::{Union, UnionClientId, UnionClientType}, - EventSource, -}; +use chain_utils::{evm::Evm, union::Union, EventSource}; use frame_support_procedural::DebugNoBound; use frunk::{hlist_pat, HList}; use futures::{future::BoxFuture, stream, Future, FutureExt, StreamExt, TryStreamExt}; @@ -23,8 +20,10 @@ use tokio::task::JoinSet; use unionlabs::{ ethereum_consts_traits::{Mainnet, Minimal}, events::{ - ConnectionOpenAck, ConnectionOpenConfirm, ConnectionOpenInit, ConnectionOpenTry, - CreateClient, IbcEvent, UpdateClient, + AcknowledgePacket, ChannelOpenAck, ChannelOpenConfirm, ChannelOpenInit, ChannelOpenTry, + ClientMisbehaviour, ConnectionOpenAck, ConnectionOpenConfirm, ConnectionOpenInit, + ConnectionOpenTry, CreateClient, IbcEvent, RecvPacket, SendPacket, SubmitEvidence, + TimeoutPacket, UpdateClient, WriteAcknowledgement, }, ibc::core::{ channel::{ @@ -74,11 +73,11 @@ use crate::{ }, data, data::{ - AcknowledgementProof, ChannelEnd, ChannelEndProof, ClientConsensusStateProof, + AcknowledgementProof, AnyData, ChannelEnd, ChannelEndProof, ClientConsensusStateProof, ClientStateProof, CommitmentProof, ConnectionEnd, ConnectionProof, Data, PacketAcknowledgement, SelfClientState, SelfConsensusState, TrustedClientState, }, - defer, enum_variants_conversions, + defer, enum_variants_conversions, event, event::Event, fetch, fetch::{ @@ -94,8 +93,8 @@ use crate::{ }, retry, seq, wait, wait::{Wait, WaitForBlock, WaitForTimestamp, WaitForTrustedHeight}, - AggregateData, AggregateReceiver, AnyLcMsg, ChainIdOf, DeferPoint, DoAggregate, Identified, - LcMsg, RelayerMsg, + AggregateData, AggregateReceiver, AnyLcMsg, AnyLightClientIdentified, ChainIdOf, + DeferPoint, DoAggregate, Identified, LcMsg, RelayerMsg, }, queue::aggregate_data::UseAggregate, DELAY_PERIOD, @@ -421,236 +420,24 @@ impl Voyager { .map(|(chain_id, chain)| { chain .events(()) - .map_ok(move |event| { - if chain_id != event.chain_id { + .map_ok(move |chain_event| { + if chain_id != chain_event.chain_id { tracing::warn!( "chain {chain_id} produced an event from chain {}", - event.chain_id + chain_event.chain_id ); } - let event = match event.event { - IbcEvent::CreateClient(create_client) => { - match create_client.client_type { - EvmClientType::Cometbls(_) => { - LcMsg::::Event(Identified { - chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::CreateClient(CreateClient { - client_id: create_client - .client_id - .try_into() - .expect( - "only cometbls supported currently", - ), - client_type: chain_utils::evm::Cometbls, - consensus_height: create_client - .consensus_height, - }), - }), - }) - } - } - } - IbcEvent::UpdateClient(_) => todo!(), - IbcEvent::ClientMisbehaviour(_) => todo!(), - IbcEvent::SubmitEvidence(_) => todo!(), - IbcEvent::ConnectionOpenInit(init) => match init.client_id { - EvmClientId::Cometbls(client_id) => { - if let Ok(counterparty_client_id) = init - .counterparty_client_id - .parse::<::ClientId>() - { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ConnectionOpenInit( - ConnectionOpenInit { - connection_id: init.connection_id, - client_id, - counterparty_client_id, - counterparty_connection_id: init - .counterparty_connection_id, - }, - ), - }), - }) - } else { - panic!() - } - } - }, - IbcEvent::ConnectionOpenTry(try_) => match try_.client_id { - EvmClientId::Cometbls(client_id) => { - if let Ok(counterparty_client_id) = try_ - .counterparty_client_id - .parse::<::ClientId>() - { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ConnectionOpenTry( - ConnectionOpenTry { - connection_id: try_.connection_id, - client_id, - counterparty_client_id, - counterparty_connection_id: try_ - .counterparty_connection_id, - }, - ), - }), - }) - } else { - panic!() - } - } - }, - IbcEvent::ConnectionOpenAck(ack) => match ack.client_id { - EvmClientId::Cometbls(client_id) => { - if let Ok(counterparty_client_id) = ack - .counterparty_client_id - .parse::<::ClientId>() - { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ConnectionOpenAck( - ConnectionOpenAck { - connection_id: ack.connection_id, - client_id, - counterparty_client_id, - counterparty_connection_id: ack - .counterparty_connection_id, - }, - ), - }), - }) - } else { - panic!() - } - } - }, - IbcEvent::ConnectionOpenConfirm(confirm) => match confirm.client_id - { - EvmClientId::Cometbls(client_id) => { - if let Ok(counterparty_client_id) = confirm - .counterparty_client_id - .parse::<::ClientId>() - { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ConnectionOpenConfirm( - ConnectionOpenConfirm { - connection_id: confirm.connection_id, - client_id, - counterparty_client_id, - counterparty_connection_id: confirm - .counterparty_connection_id, - }, - ), - }), - }) - } else { - panic!() - } - } + event::( + chain_event.chain_id, + crate::msg::event::IbcEvent { + block_hash: chain_event.block_hash, + height: chain_event.height, + event: chain_event_to_lc_event::( + chain_event.event, + ), }, - // NOTE: CometblsMinimal assumed for now for channel events - IbcEvent::ChannelOpenInit(init) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ChannelOpenInit(init), - }), - }) - } - IbcEvent::ChannelOpenTry(try_) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ChannelOpenTry(try_), - }), - }) - } - IbcEvent::ChannelOpenAck(ack) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ChannelOpenAck(ack), - }), - }) - } - IbcEvent::ChannelOpenConfirm(confirm) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ChannelOpenConfirm(confirm), - }), - }) - } - IbcEvent::RecvPacket(packet) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::RecvPacket(packet), - }), - }) - } - IbcEvent::SendPacket(packet) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::SendPacket(packet), - }), - }) - } - IbcEvent::AcknowledgePacket(ack_packet) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::AcknowledgePacket(ack_packet), - }), - }) - } - IbcEvent::WriteAcknowledgement(write_ack) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::WriteAcknowledgement(write_ack), - }), - }) - } - IbcEvent::TimeoutPacket(_) => todo!(), - }; - - RelayerMsg::Lc(AnyLcMsg::from(event)) + ) }) .map_err(|x| Box::new(x) as Box) }) @@ -660,261 +447,24 @@ impl Voyager { .map(|(chain_id, chain)| { chain .events(()) - .map_ok(move |event| { - if chain_id != event.chain_id { + .map_ok(move |chain_event| { + if chain_id != chain_event.chain_id { tracing::warn!( "chain {chain_id} produced an event from chain {}", - event.chain_id + chain_event.chain_id ); } - let event = match event.event { - IbcEvent::CreateClient(create_client) => { - match create_client.client_type { - // TODO: Introspect the contract for a client type beyond just "wasm" - UnionClientType::Wasm(_) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::CreateClient(CreateClient { - client_id: create_client - .client_id - .try_into() - .expect( - "only cometbls supported currently", - ), - client_type: chain_utils::union::Wasm, - consensus_height: create_client - .consensus_height, - }), - }), - }) - } - UnionClientType::Tendermint(_) => todo!(), - } - } - IbcEvent::UpdateClient(updated) => match updated.client_id { - UnionClientId::Wasm(client_id) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::UpdateClient(UpdateClient { - client_id, - client_type: chain_utils::union::Wasm, - consensus_heights: updated.consensus_heights, - header: updated.header, - }), - }), - }) - } - UnionClientId::Tendermint(_) => todo!(), - }, - IbcEvent::ClientMisbehaviour(_) => todo!(), - IbcEvent::SubmitEvidence(_) => todo!(), - IbcEvent::ConnectionOpenInit(init) => match init.client_id { - UnionClientId::Wasm(client_id) => { - if let Ok(counterparty_client_id) = init - .counterparty_client_id - .parse::<::ClientId>() - { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ConnectionOpenInit( - ConnectionOpenInit { - connection_id: init.connection_id, - client_id, - counterparty_client_id, - counterparty_connection_id: init - .counterparty_connection_id, - }, - ), - }), - }) - } else { - panic!() - } - } - UnionClientId::Tendermint(_) => todo!(), - }, - IbcEvent::ConnectionOpenTry(try_) => match try_.client_id { - UnionClientId::Wasm(client_id) => { - if let Ok(counterparty_client_id) = try_ - .counterparty_client_id - .parse::<::ClientId>() - { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ConnectionOpenTry( - ConnectionOpenTry { - connection_id: try_.connection_id, - client_id, - counterparty_client_id, - counterparty_connection_id: try_ - .counterparty_connection_id, - }, - ), - }), - }) - } else { - panic!() - } - } - UnionClientId::Tendermint(_) => todo!(), - }, - IbcEvent::ConnectionOpenAck(ack) => match ack.client_id { - UnionClientId::Wasm(client_id) => { - if let Ok(counterparty_client_id) = ack - .counterparty_client_id - .parse::<::ClientId>() - { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ConnectionOpenAck( - ConnectionOpenAck { - connection_id: ack.connection_id, - client_id, - counterparty_client_id, - counterparty_connection_id: ack - .counterparty_connection_id, - }, - ), - }), - }) - } else { - panic!() - } - } - UnionClientId::Tendermint(_) => todo!(), + event::( + chain_event.chain_id, + crate::msg::event::IbcEvent { + block_hash: chain_event.block_hash, + height: chain_event.height, + event: chain_event_to_lc_event::( + chain_event.event, + ), }, - IbcEvent::ConnectionOpenConfirm(confirm) => match confirm.client_id - { - UnionClientId::Wasm(client_id) => { - if let Ok(counterparty_client_id) = confirm - .counterparty_client_id - .parse::<::ClientId>() - { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ConnectionOpenConfirm( - ConnectionOpenConfirm { - connection_id: confirm.connection_id, - client_id, - counterparty_client_id, - counterparty_connection_id: confirm - .counterparty_connection_id, - }, - ), - }), - }) - } else { - panic!() - } - } - UnionClientId::Tendermint(_) => todo!(), - }, - - // NOTE: EthereumMinimal assumed for now for channel events - IbcEvent::ChannelOpenInit(init) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ChannelOpenInit(init), - }), - }) - } - IbcEvent::ChannelOpenTry(try_) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ChannelOpenTry(try_), - }), - }) - } - IbcEvent::ChannelOpenAck(ack) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ChannelOpenAck(ack), - }), - }) - } - IbcEvent::ChannelOpenConfirm(confirm) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::ChannelOpenConfirm(confirm), - }), - }) - } - - IbcEvent::RecvPacket(recv_packet) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::RecvPacket(recv_packet), - }), - }) - } - IbcEvent::SendPacket(send_packet) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::SendPacket(send_packet), - }), - }) - } - IbcEvent::AcknowledgePacket(ack_packet) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::AcknowledgePacket(ack_packet), - }), - }) - } - IbcEvent::WriteAcknowledgement(write_ack) => { - LcMsg::::Event(Identified { - chain_id: event.chain_id, - data: Event::Ibc(crate::msg::event::IbcEvent { - block_hash: event.block_hash, - height: event.height, - event: IbcEvent::WriteAcknowledgement(write_ack), - }), - }) - } - IbcEvent::TimeoutPacket(_) => todo!(), - }; - - RelayerMsg::Lc(AnyLcMsg::from(event)) + ) }) .map_err(|x| Box::new(x) as Box) }) @@ -1009,17 +559,17 @@ impl Worker { } let res = match any_lc_msg { - AnyLcMsg::EthereumMainnet(msg) => { + AnyLightClientIdentified::EthereumMainnet(msg) => { let vec: Vec = self.handle_msg_generic::(msg).await.map_err(AnyLcError::EthereumMainnet)?; vec } - AnyLcMsg::EthereumMinimal(msg) => { + AnyLightClientIdentified::EthereumMinimal(msg) => { self.handle_msg_generic::(msg).await.map_err(AnyLcError::EthereumMinimal)? } - AnyLcMsg::CometblsMainnet(msg) => { + AnyLightClientIdentified::CometblsMainnet(msg) => { self.handle_msg_generic::(msg).await.map_err(AnyLcError::CometblsMainnet)? } - AnyLcMsg::CometblsMinimal(msg) => { + AnyLightClientIdentified::CometblsMinimal(msg) => { self.handle_msg_generic::(msg).await.map_err(AnyLcError::CometblsMinimal)? } }; @@ -1046,7 +596,7 @@ impl Worker { timeout_timestamp, msg, } => { - // if we haven't hit the time yet, requeue the defer msg + // if we haven't hit the timeout yet, handle the msg if now() > timeout_timestamp { tracing::warn!(json = %serde_json::to_string(&msg).unwrap(), "message expired"); @@ -1098,7 +648,7 @@ impl Worker { let msgs = self.handle_msg(msg, depth + 1).await?; for m in msgs { - match m.try_into() { + match >::try_from(m) { Ok(d) => { data.push_back(d); } @@ -1146,12 +696,15 @@ impl Worker { .boxed() } - async fn handle_msg_generic(&self, msg: LcMsg) -> Result, LcError> + async fn handle_msg_generic( + &self, + msg: identified!(LcMsg), + ) -> Result, LcError> where L: LightClient, Self: GetLc, - AnyLcMsg: From>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, // TODO: Remove once we no longer unwrap in handle_fetch <::ClientId as TryFrom< @@ -1161,32 +714,28 @@ impl Worker { <::HostChain as Chain>::ClientId, >>::Error: Debug, { - match msg { - LcMsg::Event(event) => Ok(handle_event(self.get_lc(&event.chain_id), event.data)), + let l = self.get_lc(&msg.chain_id); + + match msg.data { + LcMsg::Event(event) => Ok(handle_event(l, event)), LcMsg::Data(data) => { // TODO: Figure out a way to bubble it up to the top level - // if depth == 0 { - tracing::error!(data = %serde_json::to_string(&data).unwrap(), "received data outside of an aggregation"); - - // panic!(); + tracing::error!( + data = %serde_json::to_string(&data).unwrap(), + "received data outside of an aggregation" + ); Ok([].into()) - // } else { - // [RelayerMsg::Lc(AnyLcMsg::from(LcMsg::Data(data)))].into() - // } } - LcMsg::Fetch(fetch) => Ok(handle_fetch(self.get_lc(&fetch.chain_id), fetch.data).await), - LcMsg::Msg(msg) => { + LcMsg::Fetch(fetch) => Ok(handle_fetch(l, fetch).await), + LcMsg::Msg(m) => { // NOTE: `Msg`s don't requeue any `RelayerMsg`s; they are side-effect only. - self.get_lc(&msg.chain_id) - .msg(msg.data) - .await - .map_err(LcError::Msg)?; + l.msg(m).await.map_err(LcError::Msg)?; Ok([].into()) } - LcMsg::Wait(wait) => Ok(handle_wait(&self.get_lc(&wait.chain_id), wait.data).await), + LcMsg::Wait(wait) => Ok(handle_wait(l, wait).await), LcMsg::Aggregate(_) => { todo!() } @@ -1269,7 +818,7 @@ impl GetLc for Worker { fn handle_event(l: L, event: crate::msg::event::Event) -> Vec where - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { match event { @@ -1636,7 +1185,7 @@ fn mk_aggregate_update( event_height: HeightOf>, ) -> RelayerMsg where - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { RelayerMsg::Aggregate { @@ -1663,7 +1212,7 @@ where async fn handle_fetch(l: L, fetch: Fetch) -> Vec where - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, // TODO: Remove once we no longer unwrap <::ClientId as TryFrom< <::HostChain as Chain>::ClientId, @@ -1842,10 +1391,10 @@ where relayer_msg } -async fn handle_wait(l: &L, wait_msg: Wait) -> Vec +async fn handle_wait(l: L, wait_msg: Wait) -> Vec where - AnyLcMsg: From>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, { match wait_msg { Wait::Block(WaitForBlock(height)) => { @@ -1975,8 +1524,8 @@ where identified!(PacketAcknowledgement): TryFrom + Into, - AnyLcMsg: From>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, AggregateData: From)>, AggregateReceiver: From)>, { @@ -2161,7 +1710,7 @@ impl UseAggregate for identified!(AggregateChannelHandshakeUp where identified!(ConnectionEnd): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { type AggregatedData = HList![identified!(ConnectionEnd)]; @@ -2225,7 +1774,7 @@ impl UseAggregate for identified!(AggregatePacketUpdateClient where identified!(ConnectionEnd): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { type AggregatedData = HList![identified!(ConnectionEnd)]; @@ -2295,7 +1844,7 @@ where impl UseAggregate for identified!(AggregateConnectionFetchFromChannelEnd) where identified!(ChannelEnd): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![identified!(ChannelEnd)]; @@ -2328,8 +1877,8 @@ impl UseAggregate for identified!(AggregateUpdateClientFromCl where identified!(TrustedClientState): TryFrom + Into, - // AnyLcMsg: From>, - AnyLcMsg: From>, + // AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { type AggregatedData = HList![identified!(TrustedClientState)]; @@ -2386,8 +1935,8 @@ impl UseAggregate for identified!(AggregateUpdateClient) where identified!(TrustedClientState): TryFrom + Into, - // AnyLcMsg: From>, - AnyLcMsg: From>, + // AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { type AggregatedData = HList![identified!(TrustedClientState)]; @@ -2446,7 +1995,7 @@ impl UseAggregate where identified!(TrustedClientState): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { type AggregatedData = HList![identified!(TrustedClientState)]; @@ -2497,7 +2046,7 @@ impl UseAggregate for identified!(AggregateWaitForTrustedHeig where identified!(TrustedClientState): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { type AggregatedData = HList![identified!(TrustedClientState)]; @@ -2545,7 +2094,7 @@ impl UseAggregate for identified!(ConsensusStateProofAtLatest where identified!(TrustedClientState): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, AggregateReceiver: From)>, { type AggregatedData = HList![identified!(TrustedClientState)]; @@ -2584,8 +2133,8 @@ impl UseAggregate for identified!(AggregateMsgAfterUpdate) where identified!(TrustedClientState): TryFrom + Into, - AnyLcMsg: From>, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, + AnyLightClientIdentified: From)>, AggregateData: From)>, AggregateReceiver: From)>, { @@ -3074,7 +2623,7 @@ where TryFrom + Into, identified!(ConnectionProof): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![ identified!(TrustedClientState), @@ -3173,7 +2722,7 @@ where TryFrom + Into, identified!(ConnectionProof): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![ identified!(TrustedClientState), @@ -3266,7 +2815,7 @@ where TryFrom + Into, identified!(ConnectionProof): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![ identified!(TrustedClientState), @@ -3323,7 +2872,7 @@ where TryFrom + Into, identified!(ConnectionEnd): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![ identified!(TrustedClientState), @@ -3401,7 +2950,7 @@ where TryFrom + Into, identified!(ChannelEndProof): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![ identified!(TrustedClientState), @@ -3461,7 +3010,7 @@ where TryFrom + Into, identified!(ChannelEndProof): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![ identified!(TrustedClientState), @@ -3518,7 +3067,7 @@ where TryFrom + Into, identified!(CommitmentProof): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![ identified!(TrustedClientState), @@ -3585,7 +3134,7 @@ where TryFrom + Into, identified!(AcknowledgementProof): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![ identified!(TrustedClientState), @@ -3657,7 +3206,7 @@ impl UseAggregate for identified!(AggregateFetchCounterpartyS where identified!(TrustedClientState): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![identified!(TrustedClientState),]; @@ -3667,7 +3216,7 @@ where data: AggregateFetchCounterpartyStateProof { counterparty_client_id: _, - fetch, + fetch: fetch_, }, }: Self, hlist_pat![Identified { @@ -3683,12 +3232,7 @@ where let counterparty_chain_id: ChainIdOf = trusted_client_state.chain_id(); - RelayerMsg::Lc(AnyLcMsg::from(LcMsg::::Fetch( - Identified { - chain_id: counterparty_chain_id, - data: Fetch::StateProof(fetch), - }, - ))) + fetch::(counterparty_chain_id, fetch_) } } @@ -3698,7 +3242,7 @@ where TryFrom + Into, identified!(SelfConsensusState): TryFrom + Into, - AnyLcMsg: From>, + AnyLightClientIdentified: From)>, { type AggregatedData = HList![ identified!(SelfClientState), @@ -3771,3 +3315,258 @@ fn flatten() { dbg!(msg); } + +fn chain_event_to_lc_event( + event: IbcEvent<::ClientId, ::ClientType, String>, +) -> IbcEvent::ClientId> +where + ::ClientId>>::Error: Debug, + ::ClientType>>::Error: Debug, + <::ClientId as FromStr>::Err: Debug, +{ + match event { + IbcEvent::CreateClient(CreateClient { + client_id, + client_type, + consensus_height, + }) => IbcEvent::CreateClient(CreateClient { + client_id: client_id.try_into().unwrap(), + client_type: client_type.try_into().unwrap(), + consensus_height, + }), + IbcEvent::UpdateClient(UpdateClient { + client_id, + client_type, + consensus_heights, + header, + }) => IbcEvent::UpdateClient(UpdateClient { + client_id: client_id.try_into().unwrap(), + client_type: client_type.try_into().unwrap(), + consensus_heights, + header, + }), + IbcEvent::ClientMisbehaviour(ClientMisbehaviour { + client_id, + client_type, + consensus_height, + }) => IbcEvent::ClientMisbehaviour(ClientMisbehaviour { + client_id: client_id.try_into().unwrap(), + client_type: client_type.try_into().unwrap(), + consensus_height, + }), + IbcEvent::SubmitEvidence(SubmitEvidence { evidence_hash }) => { + IbcEvent::SubmitEvidence(SubmitEvidence { evidence_hash }) + } + IbcEvent::ConnectionOpenInit(ConnectionOpenInit { + connection_id, + client_id, + counterparty_client_id, + counterparty_connection_id, + }) => IbcEvent::ConnectionOpenInit(ConnectionOpenInit { + connection_id, + client_id: client_id.try_into().unwrap(), + counterparty_client_id: counterparty_client_id.parse().unwrap(), + counterparty_connection_id, + }), + IbcEvent::ConnectionOpenTry(ConnectionOpenTry { + connection_id, + client_id, + counterparty_client_id, + counterparty_connection_id, + }) => IbcEvent::ConnectionOpenTry(ConnectionOpenTry { + connection_id, + client_id: client_id.try_into().unwrap(), + counterparty_client_id: counterparty_client_id.parse().unwrap(), + counterparty_connection_id, + }), + IbcEvent::ConnectionOpenAck(ConnectionOpenAck { + connection_id, + client_id, + counterparty_client_id, + counterparty_connection_id, + }) => IbcEvent::ConnectionOpenAck(ConnectionOpenAck { + connection_id, + client_id: client_id.try_into().unwrap(), + counterparty_client_id: counterparty_client_id.parse().unwrap(), + counterparty_connection_id, + }), + IbcEvent::ConnectionOpenConfirm(ConnectionOpenConfirm { + connection_id, + client_id, + counterparty_client_id, + counterparty_connection_id, + }) => IbcEvent::ConnectionOpenConfirm(ConnectionOpenConfirm { + connection_id, + client_id: client_id.try_into().unwrap(), + counterparty_client_id: counterparty_client_id.parse().unwrap(), + counterparty_connection_id, + }), + IbcEvent::ChannelOpenInit(ChannelOpenInit { + port_id, + channel_id, + counterparty_channel_id, + counterparty_port_id, + connection_id, + version, + }) => IbcEvent::ChannelOpenInit(ChannelOpenInit { + port_id, + channel_id, + counterparty_channel_id, + counterparty_port_id, + connection_id, + version, + }), + IbcEvent::ChannelOpenTry(ChannelOpenTry { + port_id, + channel_id, + counterparty_port_id, + counterparty_channel_id, + connection_id, + version, + }) => IbcEvent::ChannelOpenTry(ChannelOpenTry { + port_id, + channel_id, + counterparty_port_id, + counterparty_channel_id, + connection_id, + version, + }), + IbcEvent::ChannelOpenAck(ChannelOpenAck { + port_id, + channel_id, + counterparty_port_id, + counterparty_channel_id, + connection_id, + }) => IbcEvent::ChannelOpenAck(ChannelOpenAck { + port_id, + channel_id, + counterparty_port_id, + counterparty_channel_id, + connection_id, + }), + IbcEvent::ChannelOpenConfirm(ChannelOpenConfirm { + port_id, + channel_id, + counterparty_port_id, + counterparty_channel_id, + connection_id, + }) => IbcEvent::ChannelOpenConfirm(ChannelOpenConfirm { + port_id, + channel_id, + counterparty_port_id, + counterparty_channel_id, + connection_id, + }), + IbcEvent::WriteAcknowledgement(WriteAcknowledgement { + packet_data_hex, + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_ack_hex, + connection_id, + }) => IbcEvent::WriteAcknowledgement(WriteAcknowledgement { + packet_data_hex, + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_ack_hex, + connection_id, + }), + IbcEvent::RecvPacket(RecvPacket { + packet_data_hex, + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_channel_ordering, + connection_id, + }) => IbcEvent::RecvPacket(RecvPacket { + packet_data_hex, + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_channel_ordering, + connection_id, + }), + IbcEvent::SendPacket(SendPacket { + packet_data_hex, + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_channel_ordering, + connection_id, + }) => IbcEvent::SendPacket(SendPacket { + packet_data_hex, + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_channel_ordering, + connection_id, + }), + IbcEvent::AcknowledgePacket(AcknowledgePacket { + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_channel_ordering, + connection_id, + }) => IbcEvent::AcknowledgePacket(AcknowledgePacket { + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_channel_ordering, + connection_id, + }), + IbcEvent::TimeoutPacket(TimeoutPacket { + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_channel_ordering, + connection_id, + }) => IbcEvent::TimeoutPacket(TimeoutPacket { + packet_timeout_height, + packet_timeout_timestamp, + packet_sequence, + packet_src_port, + packet_src_channel, + packet_dst_port, + packet_dst_channel, + packet_channel_ordering, + connection_id, + }), + } +}