From 10a6190a50089db612edb10cc52c49ebf1ce075a Mon Sep 17 00:00:00 2001 From: benluelo Date: Mon, 8 Jan 2024 23:28:02 -0500 Subject: [PATCH] wip --- flake.lock | 8 +- flake.nix | 2 +- generated/rust/src/cosmos.ics23.v1.rs | 26 ++- lib/ics23/src/ops/inner_op.rs | 9 +- lib/ics23/src/proof_specs.rs | 29 ++-- lib/ics23/src/verify.rs | 6 +- lib/ics23/test-suite/src/main.rs | 159 +++++++++++++++++-- lib/unionlabs/src/cosmos/ics23/hash_op.rs | 5 +- lib/unionlabs/src/cosmos/ics23/inner_spec.rs | 13 +- tools/rust-proto.nix | 26 ++- 10 files changed, 230 insertions(+), 53 deletions(-) diff --git a/flake.lock b/flake.lock index 4ad71f91a6..c7580fbe49 100644 --- a/flake.lock +++ b/flake.lock @@ -294,17 +294,17 @@ "ics23": { "flake": false, "locked": { - "lastModified": 1681373664, - "narHash": "sha256-vxlzs69D6Jscal9bSBeuVcX0Xibp69pcclWt5NldLtM=", + "lastModified": 1703190832, + "narHash": "sha256-B0gPOZe1UOtM5RE6RqBoQ5Z6dQViNzs72zyELX+QAxk=", "owner": "cosmos", "repo": "ics23", - "rev": "74ce807b7be39a7e0afb4e2efb8e28a57965f57b", + "rev": "bf89d957b019bb9a2f381edb1f24d06957807690", "type": "github" }, "original": { "owner": "cosmos", "repo": "ics23", - "rev": "74ce807b7be39a7e0afb4e2efb8e28a57965f57b", + "rev": "bf89d957b019bb9a2f381edb1f24d06957807690", "type": "github" } }, diff --git a/flake.nix b/flake.nix index 9a4bd2ae0e..238a791f8d 100644 --- a/flake.nix +++ b/flake.nix @@ -45,7 +45,7 @@ flake = false; }; ics23 = { - url = "github:cosmos/ics23?rev=74ce807b7be39a7e0afb4e2efb8e28a57965f57b"; + url = "github:cosmos/ics23?rev=bf89d957b019bb9a2f381edb1f24d06957807690"; flake = false; }; cosmosproto = { diff --git a/generated/rust/src/cosmos.ics23.v1.rs b/generated/rust/src/cosmos.ics23.v1.rs index 51c4830022..d495e1dee2 100644 --- a/generated/rust/src/cosmos.ics23.v1.rs +++ b/generated/rust/src/cosmos.ics23.v1.rs @@ -24,12 +24,16 @@ #[derive(Clone, PartialEq, ::prost::Message)] pub struct ExistenceProof { #[prost(bytes = "vec", tag = "1")] + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::base64"))] pub key: ::prost::alloc::vec::Vec, #[prost(bytes = "vec", tag = "2")] + #[cfg_attr(feature = "serde", serde(with = "::serde_utils::base64"))] pub value: ::prost::alloc::vec::Vec, #[prost(message, optional, tag = "3")] + #[cfg_attr(feature = "serde", serde(default))] pub leaf: ::core::option::Option, #[prost(message, repeated, tag = "4")] + #[cfg_attr(feature = "serde", serde(default))] pub path: ::prost::alloc::vec::Vec, } /// NonExistenceProof takes a proof of two neighbors, one left of the desired key, @@ -96,13 +100,16 @@ pub mod commitment_proof { #[derive(Clone, PartialEq, ::prost::Message)] pub struct LeafOp { #[prost(enumeration = "HashOp", tag = "1")] + #[cfg_attr(feature = "serde", serde(default))] pub hash: i32, #[prost(enumeration = "HashOp", tag = "2")] #[cfg_attr(feature = "serde", serde(default))] pub prehash_key: i32, #[prost(enumeration = "HashOp", tag = "3")] + #[cfg_attr(feature = "serde", serde(default))] pub prehash_value: i32, #[prost(enumeration = "LengthOp", tag = "4")] + #[cfg_attr(feature = "serde", serde(default))] pub length: i32, /// prefix is a fixed bytes that may optionally be included at the beginning to differentiate /// a leaf node from an inner node. @@ -111,6 +118,7 @@ pub struct LeafOp { all(feature = "json-schema", feature = "std"), schemars(with = "String") )] + #[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "serde", serde(with = "::serde_utils::base64"))] pub prefix: ::prost::alloc::vec::Vec, } @@ -143,6 +151,7 @@ pub struct InnerOp { pub hash: i32, #[prost(bytes = "vec", tag = "2")] #[cfg_attr(feature = "serde", serde(with = "::serde_utils::base64"))] + #[cfg_attr(feature = "serde", serde(default))] #[cfg_attr( all(feature = "json-schema", feature = "std"), schemars(with = "String") @@ -150,6 +159,7 @@ pub struct InnerOp { pub prefix: ::prost::alloc::vec::Vec, #[prost(bytes = "vec", tag = "3")] #[cfg_attr(feature = "serde", serde(with = "::serde_utils::base64"))] + #[cfg_attr(feature = "serde", serde(default))] #[cfg_attr( all(feature = "json-schema", feature = "std"), schemars(with = "String") @@ -194,6 +204,7 @@ pub struct ProofSpec { /// prehash_key specified by LeafOp to compare lexical ordering of keys for /// non-existence proofs. #[prost(bool, tag = "5")] + #[cfg_attr(feature = "serde", serde(default))] pub prehash_key_before_comparison: bool, } /// InnerSpec contains all store-specific structure info to determine if two proofs from a @@ -328,11 +339,14 @@ pub enum HashOp { NoHash = 0, Sha256 = 1, Sha512 = 2, - Keccak = 3, + Keccak256 = 3, Ripemd160 = 4, /// ripemd160(sha256(x)) Bitcoin = 5, Sha512256 = 6, + Blake2b512 = 7, + Blake2s256 = 8, + Blake3 = 9, } impl HashOp { /// String value of the enum field names used in the ProtoBuf definition. @@ -344,10 +358,13 @@ impl HashOp { HashOp::NoHash => "NO_HASH", HashOp::Sha256 => "SHA256", HashOp::Sha512 => "SHA512", - HashOp::Keccak => "KECCAK", + HashOp::Keccak256 => "KECCAK256", HashOp::Ripemd160 => "RIPEMD160", HashOp::Bitcoin => "BITCOIN", HashOp::Sha512256 => "SHA512_256", + HashOp::Blake2b512 => "BLAKE2B_512", + HashOp::Blake2s256 => "BLAKE2S_256", + HashOp::Blake3 => "BLAKE3", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -356,10 +373,13 @@ impl HashOp { "NO_HASH" => Some(Self::NoHash), "SHA256" => Some(Self::Sha256), "SHA512" => Some(Self::Sha512), - "KECCAK" => Some(Self::Keccak), + "KECCAK256" => Some(Self::Keccak256), "RIPEMD160" => Some(Self::Ripemd160), "BITCOIN" => Some(Self::Bitcoin), "SHA512_256" => Some(Self::Sha512256), + "BLAKE2B_512" => Some(Self::Blake2b512), + "BLAKE2S_256" => Some(Self::Blake2s256), + "BLAKE3" => Some(Self::Blake3), _ => None, } } diff --git a/lib/ics23/src/ops/inner_op.rs b/lib/ics23/src/ops/inner_op.rs index 231cb1338c..a1408adc10 100644 --- a/lib/ics23/src/ops/inner_op.rs +++ b/lib/ics23/src/ops/inner_op.rs @@ -1,8 +1,5 @@ use unionlabs::cosmos::ics23::{ - hash_op::HashOp, - inner_op::InnerOp, - inner_spec::{InnerSpecMaxPrefixLen, InnerSpecMinPrefixLen}, - proof_spec::ProofSpec, + hash_op::HashOp, inner_op::InnerOp, inner_spec::PositiveI32AsUsize, proof_spec::ProofSpec, }; use super::{hash_op, validate_iavl_ops}; @@ -20,12 +17,12 @@ pub enum SpecMismatchError { #[error("inner prefix too short, got ({prefix_len}) while the min length is ({min_len})")] InnerOpPrefixTooShort { prefix_len: usize, - min_len: InnerSpecMinPrefixLen, + min_len: PositiveI32AsUsize, }, #[error("inner prefix too long, got ({prefix_len}) while the max length is ({max_len})")] InnerOpPrefixTooLong { prefix_len: usize, - max_len: InnerSpecMaxPrefixLen, + max_len: PositiveI32AsUsize, }, #[error("malformed inner op suffix ({0})")] InnerOpSuffixMalformed(usize), diff --git a/lib/ics23/src/proof_specs.rs b/lib/ics23/src/proof_specs.rs index 27b57beda5..51b18742c9 100644 --- a/lib/ics23/src/proof_specs.rs +++ b/lib/ics23/src/proof_specs.rs @@ -3,10 +3,7 @@ use std::borrow::Cow; use unionlabs::{ cosmos::ics23::{ hash_op::HashOp, - inner_spec::{ - InnerSpec, InnerSpecChild, InnerSpecChildSize, InnerSpecMaxPrefixLen, - InnerSpecMinPrefixLen, - }, + inner_spec::{InnerSpec, PositiveI32AsUsize}, leaf_op::LeafOp, length_op::LengthOp, proof_spec::ProofSpec, @@ -23,13 +20,13 @@ pub const IAVL_PROOF_SPEC: ProofSpec = ProofSpec { prefix: Cow::Borrowed(&[0]), }, inner_spec: InnerSpec { - child_order: Cow::Borrowed(promote!(&[InnerSpecChild]: &[ - result_unwrap!(InnerSpecChild::new(0)), - result_unwrap!(InnerSpecChild::new(1)), + child_order: Cow::Borrowed(promote!(&[PositiveI32AsUsize]: &[ + result_unwrap!(PositiveI32AsUsize::new(0)), + result_unwrap!(PositiveI32AsUsize::new(1)), ])), - child_size: result_unwrap!(InnerSpecChildSize::new(33)), - min_prefix_length: result_unwrap!(InnerSpecMinPrefixLen::new(4)), - max_prefix_length: result_unwrap!(InnerSpecMaxPrefixLen::new(12)), + child_size: result_unwrap!(PositiveI32AsUsize::new(33)), + min_prefix_length: result_unwrap!(PositiveI32AsUsize::new(4)), + max_prefix_length: result_unwrap!(PositiveI32AsUsize::new(12)), empty_child: Cow::Borrowed(&[]), hash: HashOp::Sha256, }, @@ -47,13 +44,13 @@ pub const TENDERMINT_PROOF_SPEC: ProofSpec = ProofSpec { prefix: Cow::Borrowed(&[0]), }, inner_spec: InnerSpec { - child_order: Cow::Borrowed(promote!(&[InnerSpecChild]: &[ - result_unwrap!(InnerSpecChild::new(0)), - result_unwrap!(InnerSpecChild::new(1)), + child_order: Cow::Borrowed(promote!(&[PositiveI32AsUsize]: &[ + result_unwrap!(PositiveI32AsUsize::new(0)), + result_unwrap!(PositiveI32AsUsize::new(1)), ])), - child_size: result_unwrap!(InnerSpecChildSize::new(32)), - min_prefix_length: result_unwrap!(InnerSpecMinPrefixLen::new(1)), - max_prefix_length: result_unwrap!(InnerSpecMaxPrefixLen::new(1)), + child_size: result_unwrap!(PositiveI32AsUsize::new(32)), + min_prefix_length: result_unwrap!(PositiveI32AsUsize::new(1)), + max_prefix_length: result_unwrap!(PositiveI32AsUsize::new(1)), empty_child: Cow::Borrowed(&[]), hash: HashOp::Sha256, }, diff --git a/lib/ics23/src/verify.rs b/lib/ics23/src/verify.rs index cfafb889c2..3546f2c6c3 100644 --- a/lib/ics23/src/verify.rs +++ b/lib/ics23/src/verify.rs @@ -4,7 +4,7 @@ use unionlabs::cosmos::ics23::{ existence_proof::ExistenceProof, hash_op::HashOp, inner_op::InnerOp, - inner_spec::{InnerSpec, InnerSpecChild}, + inner_spec::{InnerSpec, PositiveI32AsUsize}, non_existence_proof::NonExistenceProof, proof_spec::ProofSpec, }; @@ -68,7 +68,7 @@ pub enum NeighborSearchError { #[error("branch ({branch}) not found in ({order:?})")] BranchNotFoundInOrder { branch: usize, - order: Vec, + order: Vec, }, #[error("cannot find any valid spacing for this node")] CannotFindValidSpacing, @@ -332,7 +332,7 @@ fn get_padding( /// checks where the branch is in the order and returns /// the index of this branch -fn get_position(order: &[InnerSpecChild], branch: usize) -> Result { +fn get_position(order: &[PositiveI32AsUsize], branch: usize) -> Result { // NOTE: Reference implementation checks `branch < 0` as well as it uses signed integers if branch >= order.len() { return Err(NeighborSearchError::InvalidBranch { diff --git a/lib/ics23/test-suite/src/main.rs b/lib/ics23/test-suite/src/main.rs index c311fa4fd2..3baa2b92e0 100644 --- a/lib/ics23/test-suite/src/main.rs +++ b/lib/ics23/test-suite/src/main.rs @@ -2,8 +2,12 @@ use std::{collections::HashMap, fs, path::PathBuf}; use anyhow::bail; use clap::Parser; -use ics23::ops::{hash_op, inner_op, leaf_op}; +use ics23::{ + existence_proof, + ops::{hash_op, inner_op, leaf_op}, +}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use unionlabs::cosmos::ics23::hash_op::HashOp; #[derive(Parser)] struct App { @@ -16,6 +20,8 @@ fn main() -> anyhow::Result<()> { run_test_cases::(app.testdata_dir.join("TestLeafOpData.json"))?; run_test_cases::(app.testdata_dir.join("TestInnerOpData.json"))?; run_test_cases::(app.testdata_dir.join("TestDoHashData.json"))?; + run_test_cases::(app.testdata_dir.join("TestExistenceProofData.json"))?; + run_test_cases::(app.testdata_dir.join("TestCheckLeafData.json"))?; Ok(()) } @@ -24,8 +30,9 @@ fn run_test_cases(p: PathBuf) -> Result<(), anyhow::Error> { let json = read_json::>(p); for (case, t) in json { + eprint!("{case}... "); t.run()?; - println!("{case}: ok"); + eprintln!("ok"); } Ok(()) @@ -143,16 +150,148 @@ struct TestDoHashData { impl TestCase for TestDoHashData { fn run(self) -> anyhow::Result<()> { - let res = hash_op::do_hash(self.hash_op.try_into().unwrap(), self.preimage.as_bytes())?; - - if res != self.expected_hash { - bail!( - "Expected {} got {}", - serde_utils::to_hex(res), - serde_utils::to_hex(self.expected_hash) - ) + let do_hash = hash_op::do_hash(self.hash_op.try_into().unwrap(), self.preimage.as_bytes()); + + match do_hash { + Ok(res) => { + if res != self.expected_hash { + bail!( + "Expected {} got {}", + serde_utils::to_hex(res), + serde_utils::to_hex(self.expected_hash) + ) + } + } + Err(hash_op::HashError::UnsupportedOp( + hash_op @ (HashOp::Blake2b512 | HashOp::Blake2s256 | HashOp::Blake3), + )) => { + println!("unsupported hash op {hash_op}, skipping"); + } + Err(err) => bail!(err), + } + + Ok(()) + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct TestExistenceProofData { + proof: protos::cosmos::ics23::v1::ExistenceProof, + #[serde(with = "::serde_utils::base64_opt")] + expected: Option>, + is_err: bool, +} + +impl TestCase for TestExistenceProofData { + fn run(self) -> anyhow::Result<()> { + match self.proof.try_into() { + Ok(res) => match existence_proof::calculate_root(&res) { + Ok(ok) => { + if self.is_err { + bail!("expected error, but got none") + } + + let expected = self.expected.unwrap(); + if ok != expected { + bail!( + "bad result: {} vs {}", + serde_utils::to_hex(ok), + serde_utils::to_hex(expected) + ) + }; + } + Err(err) => { + if !self.is_err { + bail!("{err}") + } + } + }, + Err(err) => { + if !self.is_err { + bail!("{err:?}") + } + } + }; + + Ok(()) + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct TestCheckLeafData { + leaf: protos::cosmos::ics23::v1::LeafOp, + spec: protos::cosmos::ics23::v1::LeafOp, + is_err: bool, +} + +impl TestCase for TestCheckLeafData { + fn run(self) -> anyhow::Result<()> { + match ( + self.leaf.try_into(), + protos::cosmos::ics23::v1::ProofSpec { + leaf_spec: Some(self.spec), + ..Default::default() + } + .try_into(), + ) { + (Ok(leaf), Ok(spec)) => match leaf_op::check_against_spec(&leaf, &spec) { + Ok(()) => { + if self.is_err { + bail!("Expected error") + } + } + Err(err) => { + if !self.is_err { + bail!("Unexpected error: {err}") + } + } + }, + (Ok(_), Err(err)) => { + if !self.is_err { + bail!("Unexpected error (ProofSpec): {err:?}") + } + } + (Err(err), Ok(_)) => { + if !self.is_err { + bail!("Unexpected error: {err:?}") + } + } + (Err(err1), Err(err2)) => { + if !self.is_err { + bail!("Unexpected errors: {err1:?}, {err2:?}") + } + } } + // match self.proof.try_into() { + // Ok(res) => match existence_proof::calculate_root(&res) { + // Ok(ok) => { + // if self.is_err { + // bail!("expected error, but got none") + // } + + // let expected = self.expected.unwrap(); + // if ok != expected { + // bail!( + // "bad result: {} vs {}", + // serde_utils::to_hex(ok), + // serde_utils::to_hex(expected) + // ) + // }; + // } + // Err(err) => { + // if !self.is_err { + // bail!("{err}") + // } + // } + // }, + // Err(err) => { + // println!("invalid input: {err:?}"); + // } + // }; + Ok(()) } } diff --git a/lib/unionlabs/src/cosmos/ics23/hash_op.rs b/lib/unionlabs/src/cosmos/ics23/hash_op.rs index 00276e879d..266819f0ee 100644 --- a/lib/unionlabs/src/cosmos/ics23/hash_op.rs +++ b/lib/unionlabs/src/cosmos/ics23/hash_op.rs @@ -8,9 +8,12 @@ wrapper_enum! { NoHash = 0, Sha256 = 1, Sha512 = 2, - Keccak = 3, + Keccak256 = 3, Ripemd160 = 4, Bitcoin = 5, Sha512256 = 6, + Blake2b512 = 7, + Blake2s256 = 8, + Blake3 = 9, } } diff --git a/lib/unionlabs/src/cosmos/ics23/inner_spec.rs b/lib/unionlabs/src/cosmos/ics23/inner_spec.rs index 69e2155310..cf545aabd4 100644 --- a/lib/unionlabs/src/cosmos/ics23/inner_spec.rs +++ b/lib/unionlabs/src/cosmos/ics23/inner_spec.rs @@ -9,18 +9,15 @@ use crate::{ Proto, TypeUrl, }; -pub type InnerSpecChild = BoundedUsize<0, { i32::MAX as usize }>; -pub type InnerSpecMinPrefixLen = BoundedUsize<0, { i32::MAX as usize }>; -pub type InnerSpecMaxPrefixLen = BoundedUsize<0, { i32::MAX as usize }>; -pub type InnerSpecChildSize = BoundedUsize<1, { i32::MAX as usize }>; +pub type PositiveI32AsUsize = BoundedUsize<0, { i32::MAX as usize }>; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct InnerSpec { - pub child_order: Cow<'static, [InnerSpecChild]>, - pub child_size: InnerSpecChildSize, - pub min_prefix_length: InnerSpecMinPrefixLen, - pub max_prefix_length: InnerSpecMaxPrefixLen, + pub child_order: Cow<'static, [PositiveI32AsUsize]>, + pub child_size: PositiveI32AsUsize, + pub min_prefix_length: PositiveI32AsUsize, + pub max_prefix_length: PositiveI32AsUsize, #[serde(with = "::serde_utils::hex_string")] pub empty_child: Cow<'static, [u8]>, pub hash: HashOp, diff --git a/tools/rust-proto.nix b/tools/rust-proto.nix index 1cb8bf9306..d29bea4f80 100644 --- a/tools/rust-proto.nix +++ b/tools/rust-proto.nix @@ -156,7 +156,6 @@ in with attrs; { type_attribute = { - # "." = [ eth_abi ]; ".google.protobuf.Any" = [ serde eq ]; ".google.protobuf.Timestamp" = [ serde ]; ".google.protobuf.Duration" = [ serde eq ]; @@ -194,17 +193,25 @@ field_attribute = { ".ibc.core.client.v1.Height" = [ serde_default ]; + ".ibc.core.commitment.v1.MerkleRoot.hash" = [ jsonschema_str serde_base64 ]; + ".ibc.core.commitment.v1.MerklePrefix.key_prefix" = [ jsonschema_str serde_base64 ]; + ".ibc.lightclients.wasm.v1.ClientState.data" = [ serde_base64 ]; ".ibc.lightclients.wasm.v1.ClientState.checksum" = [ serde_base64 ]; + ".ibc.lightclients.wasm.v1.ConsensusState.data" = [ serde_base64 ]; + ".ibc.lightclients.wasm.v1.Header.data" = [ serde_base64 ]; + ".union.ibc.lightclients.ethereum.v1.SyncCommittee.aggregate_pubkey" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.SyncCommittee.pubkeys" = [ serde_inner_base64 ]; + ".union.ibc.lightclients.ethereum.v1.BeaconBlockHeader.parent_root" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.BeaconBlockHeader.state_root" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.BeaconBlockHeader.body_root" = [ serde_base64 ]; + ".union.ibc.lightclients.ethereum.v1.ExecutionPayloadHeader.parent_hash" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.ExecutionPayloadHeader.fee_recipient" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.ExecutionPayloadHeader.state_root" = [ serde_base64 ]; @@ -216,23 +223,40 @@ ".union.ibc.lightclients.ethereum.v1.ExecutionPayloadHeader.block_hash" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.ExecutionPayloadHeader.transactions_root" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.ExecutionPayloadHeader.withdrawals_root" = [ serde_base64 ]; + ".union.ibc.lightclients.ethereum.v1.LightClientHeader.execution_branch" = [ serde_inner_base64 ]; + ".union.ibc.lightclients.ethereum.v1.LightClientUpdate.next_sync_committee_branch" = [ serde_inner_base64 ]; ".union.ibc.lightclients.ethereum.v1.LightClientUpdate.finality_branch" = [ serde_inner_base64 ]; + ".union.ibc.lightclients.ethereum.v1.SyncAggregate.sync_committee_bits" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.SyncAggregate.sync_committee_signature" = [ serde_base64 ]; + ".union.ibc.lightclients.ethereum.v1.Proof.key" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.Proof.value" = [ serde_base64 ]; ".union.ibc.lightclients.ethereum.v1.Proof.proof" = [ serde_inner_base64 ]; + ".cosmos.ics23.v1.LeafOp.hash" = [ serde_default ]; ".cosmos.ics23.v1.LeafOp.prehash_key" = [ serde_default ]; ".cosmos.ics23.v1.LeafOp.prehash_value" = [ serde_default ]; + ".cosmos.ics23.v1.LeafOp.length" = [ serde_default ]; ".cosmos.ics23.v1.LeafOp.prefix" = [ jsonschema_str serde_default serde_base64 ]; + ".cosmos.ics23.v1.InnerOp.prefix" = [ serde_base64 serde_default jsonschema_str ]; ".cosmos.ics23.v1.InnerOp.suffix" = [ serde_base64 serde_default jsonschema_str ]; + ".cosmos.ics23.v1.ProofSpec.max_depth" = [ serde_default ]; ".cosmos.ics23.v1.ProofSpec.min_depth" = [ serde_default ]; + ".cosmos.ics23.v1.ProofSpec.prehash_key_before_comparison" = [ serde_default ]; + ".cosmos.ics23.v1.InnerSpec.empty_child" = [ serde_default jsonschema_str serde_base64 ]; + + ".cosmos.ics23.v1.ExistenceProof.key" = [ serde_base64 ]; + ".cosmos.ics23.v1.ExistenceProof.value" = [ serde_base64 ]; + ".cosmos.ics23.v1.ExistenceProof.path" = [ serde_default ]; + ".cosmos.ics23.v1.ExistenceProof.leaf" = [ serde_default ]; + + ".cosmos.ics23.v1.NonExistenceProof.value" = [ serde_base64 ]; }; };