Skip to content

Commit

Permalink
Protobuf encoding improvements (#432)
Browse files Browse the repository at this point in the history
* Add protobuf encoding for Height and CommitmentRoot

* Relax MutEncoder constraint for EncodeU64ProtoField to use TryInto

* Wire up encoding for Timestamp and CommitmentRoot

* Encode Timestamp directly without using prost::Message

* Fix clippy

* Disable default features for ibc and ibc-proto

* Redesign impl_type_url! macro to be implemented directly by schema type

* Implement protobuf encoding for WasmClientMessage

* Rename WrappedTendermintClientState to WasmTendermintClientState

* Add ConvertIbcAny

* Rename EncodeWithContext to WithContext

* Implement Converter for WithContext

* Add encode/decode via ClientMessage converters

* Use prost Any

* Add changelog
  • Loading branch information
soareschen authored Sep 18, 2024
1 parent df8f288 commit 6e231ae
Show file tree
Hide file tree
Showing 42 changed files with 427 additions and 129 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## v0.2.0 (pre-release)

- Protobuf encoding improvements [#432](https://github.com/informalsystems/hermes-sdk/pull/432)
- Redesign `impl_type_url!` macro to implement `SchemaGetter` on existing component type.
- Schema components that only use `impl_type_url!` directly are no longer wrapped with `DelegateEncoding`.
- Rename `EncodeWithContext` to `WithContext`.
- Rename `WrappedTendermintClientState` to `WasmTendermintClientState`.
- Implement Protobuf encoding for `WasmClientMessage`.
- Implement `MutEncoder` for `Timestamp` and `CommitmentRoot`.
- Relax `MutEncoder` constraint for `EncodeU64ProtoField` to use `TryInto`.

- Introduce cosmos- and wasm- encoding components crates [#431](https://github.com/informalsystems/hermes-sdk/pull/431)
- Add new `hermes-cosmos-encoding-components` crate.
- Add new `hermes-wasm-encoding-components` crate.
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ overflow-checks = true

[workspace.dependencies]
async-trait = { version = "0.1.82" }
ibc = { version = "0.54.0" }
ibc-proto = { version = "0.47.0" }
ibc = { version = "0.54.0", default-features = false }
ibc-proto = { version = "0.47.0", default-features = false }
ibc-relayer = { version = "0.29.2" }
ibc-relayer-types = { version = "0.29.2" }
ibc-telemetry = { version = "0.29.2" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ define_components! {
]:
CosmosEncodingComponents,
SchemaGetterComponent:
DelegateEncoding<CosmosTypeUrlSchemas>,
CosmosTypeUrlSchemas,
}
}
10 changes: 5 additions & 5 deletions crates/cosmos/cosmos-chain-components/src/encoding/convert.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cgp::prelude::*;
use hermes_encoding_components::impls::convert::{ConvertFrom, TryConvertFrom};
use hermes_encoding_components::impls::with_context::EncodeWithContext;
use hermes_encoding_components::impls::with_context::WithContext;
use hermes_protobuf_encoding_components::impls::any::{DecodeAsAnyProtobuf, EncodeAsAnyProtobuf};
use hermes_protobuf_encoding_components::types::strategy::ViaProtobuf;
use ibc::core::commitment_types::merkle::MerkleProof;
Expand All @@ -25,10 +25,10 @@ delegate_components! {
(MerkleProof, ProtoMerkleProof): ConvertFrom,
(ProtoMerkleProof, MerkleProof): TryConvertFrom,

(TendermintClientState, Any): EncodeAsAnyProtobuf<ViaProtobuf, EncodeWithContext>,
(Any, TendermintClientState): DecodeAsAnyProtobuf<ViaProtobuf, EncodeWithContext>,
(TendermintClientState, Any): EncodeAsAnyProtobuf<ViaProtobuf, WithContext>,
(Any, TendermintClientState): DecodeAsAnyProtobuf<ViaProtobuf, WithContext>,

(TendermintConsensusState, Any): EncodeAsAnyProtobuf<ViaProtobuf, EncodeWithContext>,
(Any, TendermintConsensusState): DecodeAsAnyProtobuf<ViaProtobuf, EncodeWithContext>,
(TendermintConsensusState, Any): EncodeAsAnyProtobuf<ViaProtobuf, WithContext>,
(Any, TendermintConsensusState): DecodeAsAnyProtobuf<ViaProtobuf, WithContext>,
}
}
21 changes: 9 additions & 12 deletions crates/cosmos/cosmos-chain-components/src/encoding/type_url.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
use cgp::prelude::*;
use hermes_protobuf_encoding_components::impl_type_url;
use ibc::clients::tendermint::types::{
TENDERMINT_CLIENT_STATE_TYPE_URL, TENDERMINT_CONSENSUS_STATE_TYPE_URL,
};

use crate::types::tendermint::{TendermintClientState, TendermintConsensusState};

pub struct CosmosTypeUrlSchemas;

delegate_components! {
CosmosTypeUrlSchemas {
TendermintClientState: TendermintClientStateUrl,
TendermintConsensusState: TendermintConsensusStateUrl,
}
}

impl_type_url!(
TendermintClientStateUrl,
"/ibc.lightclients.tendermint.v1.ClientState",
CosmosTypeUrlSchemas,
TendermintClientState,
TENDERMINT_CLIENT_STATE_TYPE_URL,
);

impl_type_url!(
TendermintConsensusStateUrl,
"/ibc.lightclients.tendermint.v1.ConsensusState",
CosmosTypeUrlSchemas,
TendermintConsensusState,
TENDERMINT_CONSENSUS_STATE_TYPE_URL,
);
1 change: 1 addition & 0 deletions crates/cosmos/cosmos-encoding-components/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ hermes-encoding-components = { workspace = true }
hermes-protobuf-encoding-components = { workspace = true }

ibc = { workspace = true }
ibc-proto = { workspace = true }

prost = { workspace = true }
prost-types = { workspace = true }
10 changes: 10 additions & 0 deletions crates/cosmos/cosmos-encoding-components/src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ pub use hermes_protobuf_encoding_components::components::{
};
use hermes_protobuf_encoding_components::types::strategy::ViaProtobuf;
use ibc::core::client::types::Height;
use ibc::core::commitment_types::commitment::CommitmentRoot;
use ibc::primitives::Timestamp;
use prost_types::Any;

use crate::impls::commitment_root::EncodeCommitmentRoot;
use crate::impls::height::EncodeHeight;
use crate::impls::timestamp::EncodeTimestamp;

define_components! {
CosmosEncodingComponents {
Expand Down Expand Up @@ -54,5 +58,11 @@ delegate_components! {

(ViaProtobuf, Height):
EncodeHeight,

(ViaProtobuf, CommitmentRoot):
EncodeCommitmentRoot,

(ViaProtobuf, Timestamp):
EncodeTimestamp,
}
}
30 changes: 30 additions & 0 deletions crates/cosmos/cosmos-encoding-components/src/impls/any.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use cgp::prelude::HasErrorType;
use hermes_encoding_components::traits::convert::Converter;
use ibc::primitives::proto::Any as IbcAny;
use prost_types::Any as ProstAny;

pub struct ConvertIbcAny;

impl<Encoding> Converter<Encoding, ProstAny, IbcAny> for ConvertIbcAny
where
Encoding: HasErrorType,
{
fn convert(_encoding: &Encoding, from: &ProstAny) -> Result<IbcAny, Encoding::Error> {
Ok(IbcAny {
type_url: from.type_url.clone(),
value: from.value.clone(),
})
}
}

impl<Encoding> Converter<Encoding, IbcAny, ProstAny> for ConvertIbcAny
where
Encoding: HasErrorType,
{
fn convert(_encoding: &Encoding, from: &IbcAny) -> Result<ProstAny, Encoding::Error> {
Ok(ProstAny {
type_url: from.type_url.clone(),
value: from.value.clone(),
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use cgp::prelude::*;
use hermes_encoding_components::impls::encode_mut::from::DecodeFrom;
use hermes_encoding_components::traits::encode_mut::MutEncoder;
use hermes_encoding_components::traits::transform::Transformer;
use hermes_encoding_components::traits::types::encode_buffer::HasEncodeBufferType;
use hermes_protobuf_encoding_components::components::MutDecoderComponent;
use hermes_protobuf_encoding_components::impls::encode_mut::proto_field::bytes::EncodeByteField;
use ibc::core::commitment_types::commitment::CommitmentRoot;

pub struct EncodeCommitmentRoot;

delegate_components! {
EncodeCommitmentRoot {
MutDecoderComponent: DecodeFrom<
Self,
EncodeByteField<1>
>,
}
}

impl<Encoding, Strategy> MutEncoder<Encoding, Strategy, CommitmentRoot> for EncodeCommitmentRoot
where
Encoding: HasEncodeBufferType + HasErrorType,
EncodeByteField<1>: for<'a> MutEncoder<Encoding, Strategy, &'a [u8]>,
{
fn encode_mut(
encoding: &Encoding,
value: &CommitmentRoot,
buffer: &mut Encoding::EncodeBuffer,
) -> Result<(), Encoding::Error> {
EncodeByteField::encode_mut(encoding, &value.as_bytes(), buffer)?;

Ok(())
}
}

impl Transformer for EncodeCommitmentRoot {
type From = Vec<u8>;

type To = CommitmentRoot;

fn transform(from: Vec<u8>) -> CommitmentRoot {
CommitmentRoot::from(from)
}
}
9 changes: 3 additions & 6 deletions crates/cosmos/cosmos-encoding-components/src/impls/height.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use cgp::prelude::{CanRaiseError, HasErrorType};
use hermes_encoding_components::impls::encode_mut::combine::CombineEncoders;
use hermes_encoding_components::impls::encode_mut::pair::EncoderPair;
use hermes_encoding_components::traits::decode_mut::MutDecoder;
use hermes_encoding_components::traits::encode_mut::MutEncoder;
use hermes_encoding_components::traits::types::decode_buffer::HasDecodeBufferType;
use hermes_encoding_components::traits::types::encode_buffer::HasEncodeBufferType;
use hermes_encoding_components::HList;
use hermes_protobuf_encoding_components::impls::encode_mut::proto_field::u64::EncodeU64ProtoField;
use ibc::core::client::types::error::ClientError;
use ibc::core::client::types::Height;
Expand Down Expand Up @@ -36,15 +34,14 @@ where
impl<Encoding, Strategy> MutDecoder<Encoding, Strategy, Height> for EncodeHeight
where
Encoding: HasDecodeBufferType + CanRaiseError<ClientError>,
CombineEncoders<HList![EncodeU64ProtoField<1>, EncodeU64ProtoField<2>,]>:
MutDecoder<Encoding, Strategy, HList![u64, u64]>,
EncoderPair<EncodeU64ProtoField<1>, EncodeU64ProtoField<2>>:
MutDecoder<Encoding, Strategy, (u64, u64)>,
{
fn decode_mut(
encoding: &Encoding,
buffer: &mut Encoding::DecodeBuffer<'_>,
) -> Result<Height, Encoding::Error> {
let HList![revision_number, revision_height] =
CombineEncoders::decode_mut(encoding, buffer)?;
let (revision_number, revision_height) = EncoderPair::decode_mut(encoding, buffer)?;

Height::new(revision_number, revision_height).map_err(Encoding::raise_error)
}
Expand Down
3 changes: 3 additions & 0 deletions crates/cosmos/cosmos-encoding-components/src/impls/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
pub mod any;
pub mod commitment_root;
pub mod height;
pub mod timestamp;
63 changes: 63 additions & 0 deletions crates/cosmos/cosmos-encoding-components/src/impls/timestamp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use core::num::TryFromIntError;

use cgp::prelude::{CanRaiseError, HasErrorType};
use hermes_encoding_components::impls::encode_mut::pair::EncoderPair;
use hermes_encoding_components::traits::decode_mut::MutDecoder;
use hermes_encoding_components::traits::encode_mut::MutEncoder;
use hermes_encoding_components::traits::types::decode_buffer::HasDecodeBufferType;
use hermes_encoding_components::traits::types::encode_buffer::HasEncodeBufferType;
use hermes_protobuf_encoding_components::impls::encode_mut::proto_field::u64::EncodeU64ProtoField;
use ibc::core::primitives::{Timestamp, TimestampError};
use ibc_proto::google::protobuf::Timestamp as ProtoTimestamp;

pub struct EncodeTimestamp;

impl<Encoding, Strategy> MutEncoder<Encoding, Strategy, Timestamp> for EncodeTimestamp
where
Encoding: HasEncodeBufferType + HasErrorType,
EncoderPair<EncodeU64ProtoField<1>, EncodeU64ProtoField<2>>:
MutEncoder<Encoding, Strategy, (i64, i32)>,
{
fn encode_mut(
encoding: &Encoding,
value: &Timestamp,
buffer: &mut Encoding::EncodeBuffer,
) -> Result<(), Encoding::Error> {
// We have no choice but to use ProtoTimestamp to encode for now,
// because the Timstamp field is currently private, and it is
// impossible to get the seconds and nanoseconds without first
// converting it to ProtoTimestamp.

let proto_timestamp = ProtoTimestamp::from(*value);

EncoderPair::encode_mut(
encoding,
&(proto_timestamp.seconds, proto_timestamp.nanos),
buffer,
)?;

Ok(())
}
}

impl<Encoding, Strategy> MutDecoder<Encoding, Strategy, Timestamp> for EncodeTimestamp
where
Encoding: HasDecodeBufferType + CanRaiseError<TryFromIntError> + CanRaiseError<TimestampError>,
EncoderPair<EncodeU64ProtoField<1>, EncodeU64ProtoField<2>>:
MutDecoder<Encoding, Strategy, (i64, i32)>,
{
fn decode_mut(
encoding: &Encoding,
buffer: &mut Encoding::DecodeBuffer<'_>,
) -> Result<Timestamp, Encoding::Error> {
let (seconds, nanos) = EncoderPair::decode_mut(encoding, buffer)?;

let timestamp = Timestamp::from_unix_timestamp(
seconds.try_into().map_err(Encoding::raise_error)?,
nanos.try_into().map_err(Encoding::raise_error)?,
)
.map_err(Encoding::raise_error)?;

Ok(timestamp)
}
}
7 changes: 6 additions & 1 deletion crates/cosmos/cosmos-relayer/src/contexts/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use hermes_cosmos_chain_components::types::tendermint::TendermintConsensusState;
use hermes_encoding_components::impls::default_encoding::GetDefaultEncoding;
use hermes_encoding_components::traits::convert::CanConvertBothWays;
use hermes_encoding_components::traits::encode_and_decode::CanEncodeAndDecode;
use hermes_encoding_components::traits::encode_and_decode_mut::CanEncodeAndDecodeMut;
use hermes_encoding_components::traits::has_encoding::{
DefaultEncodingGetter, EncodingGetterComponent, HasEncodingType, ProvideEncodingType,
};
Expand All @@ -15,7 +16,9 @@ use hermes_encoding_components::types::AsBytes;
use hermes_protobuf_encoding_components::impls::encode_mut::chunk::ProtoChunks;
use hermes_protobuf_encoding_components::types::strategy::{ViaAny, ViaProtobuf};
use ibc::core::client::types::Height;
use ibc::core::commitment_types::commitment::CommitmentRoot;
use ibc::core::commitment_types::merkle::MerkleProof;
use ibc::primitives::Timestamp;
use ibc_relayer_types::clients::ics07_tendermint::client_state::ClientState as TendermintClientState;
use prost::bytes::BufMut;
use prost_types::Any;
Expand Down Expand Up @@ -85,7 +88,9 @@ pub trait CheckCosmosEncoding:
+ CanEncodeAndDecode<ViaAny, TendermintConsensusState>
+ CanConvertBothWays<Any, TendermintClientState>
+ CanConvertBothWays<Any, TendermintConsensusState>
+ CanEncodeAndDecode<ViaProtobuf, Height>
+ CanEncodeAndDecodeMut<ViaProtobuf, Height>
+ CanEncodeAndDecodeMut<ViaProtobuf, Timestamp>
+ CanEncodeAndDecodeMut<ViaProtobuf, CommitmentRoot>
{
}

Expand Down
5 changes: 4 additions & 1 deletion crates/cosmos/cosmos-relayer/src/impls/error.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use alloc::string::FromUtf8Error;
use core::array::TryFromSliceError;
use core::convert::Infallible;
use core::num::ParseIntError;
use core::num::{ParseIntError, TryFromIntError};
use core::str::Utf8Error;
use hermes_protobuf_encoding_components::impls::encode_mut::chunk::{
InvalidWireType, UnsupportedWireType,
};
use hermes_protobuf_encoding_components::impls::encode_mut::proto_field::decode_required::RequiredFieldTagNotFound;
use ibc::primitives::TimestampError;

use cgp::core::error::{
DelegateErrorRaiser, ErrorRaiser, ErrorRaiserComponent, ErrorTypeComponent,
Expand Down Expand Up @@ -121,6 +122,7 @@ delegate_components! {
TendermintProtoError,
TendermintRpcError,
TendermintClientError,
TimestampError,
Ics02Error,
Ics03Error,
Ics23Error,
Expand All @@ -134,6 +136,7 @@ delegate_components! {
ClientError,
CommitmentError,
Utf8Error,
TryFromIntError,
TryFromSliceError,

// TODO: make it retryable?
Expand Down
4 changes: 2 additions & 2 deletions crates/cosmos/cosmos-wasm-relayer/src/context/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ use tendermint_rpc::{HttpClient, Url};
use crate::components::cosmos_to_wasm_cosmos::CosmosToWasmCosmosComponents;
use crate::context::encoding::{ProvideWasmCosmosEncoding, WasmCosmosEncoding};
use crate::impls::client_state::ProvideWrappedTendermintClientState;
use crate::types::client_state::WrappedTendermintClientState;
use crate::types::client_state::WasmTendermintClientState;

#[derive(Clone)]
pub struct WasmCosmosChain {
Expand Down Expand Up @@ -564,7 +564,7 @@ impl HasEventSubscription for WasmCosmosChain {
}

pub trait CanUseWasmCosmosChain:
HasClientStateType<WasmCosmosChain, ClientState = WrappedTendermintClientState>
HasClientStateType<WasmCosmosChain, ClientState = WasmTendermintClientState>
+ HasConsensusStateType<WasmCosmosChain, ConsensusState = TendermintConsensusState>
+ CanQueryBalance
// + CanIbcTransferToken<WasmCosmosChain>
Expand Down
Loading

0 comments on commit 6e231ae

Please sign in to comment.