Verify merkle proofs (#32)
This commit is contained in:
commit
991b45a210
26 changed files with 1419 additions and 48 deletions
2
utils/cw-proof/.gitignore
vendored
Normal file
2
utils/cw-proof/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Cargo.lock
|
||||||
|
|
18
utils/cw-proof/Cargo.toml
Normal file
18
utils/cw-proof/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "cw-proof"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.1.8", features = ["derive"] }
|
||||||
|
cosmrs = { version = "0.15.0", default-features = false }
|
||||||
|
displaydoc = { version = "0.2.4", default-features = false }
|
||||||
|
ics23 = { version = "0.11.0", default-features = false, features = ["host-functions"] }
|
||||||
|
prost = { version = "0.12.3", default-features = false }
|
||||||
|
serde = { version = "1.0.189", features = ["derive"] }
|
||||||
|
serde_with = { version = "3.4.0", default-features = false, features = ["hex", "macros"] }
|
||||||
|
tendermint = { version = "0.34.0", default-features = false }
|
||||||
|
tendermint-rpc = { version = "0.34.0", default-features = false, features = ["http-client"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex = { version = "0.4.3", default-features = false }
|
15
utils/cw-proof/src/error.rs
Normal file
15
utils/cw-proof/src/error.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
use displaydoc::Display;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Display)]
|
||||||
|
pub enum ProofError {
|
||||||
|
/// failed to decode commitment proof
|
||||||
|
CommitmentProofDecodingFailed,
|
||||||
|
/// empty merkle root
|
||||||
|
EmptyMerkleRoot,
|
||||||
|
/// empty verified value
|
||||||
|
EmptyVerifiedValue,
|
||||||
|
/// invalid merkle proof
|
||||||
|
InvalidMerkleProof,
|
||||||
|
/// proof verification failed
|
||||||
|
VerificationFailure,
|
||||||
|
}
|
20
utils/cw-proof/src/lib.rs
Normal file
20
utils/cw-proof/src/lib.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#![no_std]
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![warn(
|
||||||
|
clippy::checked_conversions,
|
||||||
|
clippy::panic,
|
||||||
|
clippy::panic_in_result_fn,
|
||||||
|
clippy::unwrap_used,
|
||||||
|
trivial_casts,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
rust_2018_idioms,
|
||||||
|
unused_lifetimes,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_qualifications
|
||||||
|
)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod proof;
|
||||||
|
pub mod verifier;
|
115
utils/cw-proof/src/proof/cw.rs
Normal file
115
utils/cw-proof/src/proof/cw.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
use alloc::{boxed::Box, vec::Vec};
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use displaydoc::Display;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_with::{hex::Hex, serde_as};
|
||||||
|
use tendermint::merkle::proof::ProofOps;
|
||||||
|
use tendermint_rpc::endpoint::abci_query::AbciQuery;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::ProofError,
|
||||||
|
proof::{
|
||||||
|
convert_tm_to_ics_merkle_proof,
|
||||||
|
key::{IntoKeys, PrefixedKey},
|
||||||
|
prefix::PrefixWasm,
|
||||||
|
Proof,
|
||||||
|
},
|
||||||
|
verifier::cw::CwVerifier,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CwProof<K = Vec<u8>, V = Vec<u8>> {
|
||||||
|
proof: ProofOps,
|
||||||
|
// TODO(hu55a1n1): replace `K` with `CwAbciKey`
|
||||||
|
key: PrefixedKey<PrefixWasm, K>,
|
||||||
|
value: V,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ABCI query response doesn't contain proof
|
||||||
|
#[derive(Clone, Debug, Display)]
|
||||||
|
pub struct ErrorWithoutProof;
|
||||||
|
|
||||||
|
impl TryFrom<AbciQuery> for CwProof {
|
||||||
|
type Error = ErrorWithoutProof;
|
||||||
|
|
||||||
|
fn try_from(query: AbciQuery) -> Result<Self, Self::Error> {
|
||||||
|
RawCwProof::try_from(query).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> Proof for CwProof<K, V>
|
||||||
|
where
|
||||||
|
K: Clone + Into<Vec<u8>>,
|
||||||
|
V: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
type Key = K;
|
||||||
|
type Value = V;
|
||||||
|
type ProofOps = ProofOps;
|
||||||
|
|
||||||
|
fn verify(&self, root: Vec<u8>) -> Result<(), ProofError> {
|
||||||
|
fn into_array_of_size_2<T: Debug>(v: Vec<T>) -> Result<[T; 2], ProofError> {
|
||||||
|
let boxed_slice = v.into_boxed_slice();
|
||||||
|
let boxed_array: Box<[T; 2]> = boxed_slice.try_into().expect("todo");
|
||||||
|
Ok(*boxed_array)
|
||||||
|
}
|
||||||
|
|
||||||
|
let Self { proof, key, value } = self;
|
||||||
|
let proofs = convert_tm_to_ics_merkle_proof(proof)?;
|
||||||
|
|
||||||
|
let cw_verifier = CwVerifier::default();
|
||||||
|
cw_verifier.verify(
|
||||||
|
&into_array_of_size_2(proofs)?,
|
||||||
|
&root,
|
||||||
|
&into_array_of_size_2(key.clone().into_keys())?,
|
||||||
|
value.as_ref(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct RawCwProof {
|
||||||
|
#[serde_as(as = "Hex")]
|
||||||
|
key: Vec<u8>,
|
||||||
|
#[serde_as(as = "Hex")]
|
||||||
|
value: Vec<u8>,
|
||||||
|
proof: ProofOps,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RawCwProof> for CwProof {
|
||||||
|
fn from(RawCwProof { key, value, proof }: RawCwProof) -> Self {
|
||||||
|
Self {
|
||||||
|
proof,
|
||||||
|
key: PrefixedKey::new(key),
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CwProof> for RawCwProof {
|
||||||
|
fn from(CwProof { proof, key, value }: CwProof) -> Self {
|
||||||
|
Self {
|
||||||
|
key: key.into_keys().pop().expect("empty key"),
|
||||||
|
value,
|
||||||
|
proof,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<AbciQuery> for RawCwProof {
|
||||||
|
type Error = ErrorWithoutProof;
|
||||||
|
|
||||||
|
fn try_from(query: AbciQuery) -> Result<Self, Self::Error> {
|
||||||
|
let AbciQuery {
|
||||||
|
key, value, proof, ..
|
||||||
|
} = query;
|
||||||
|
let Some(proof) = proof else {
|
||||||
|
return Err(ErrorWithoutProof);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self { proof, key, value })
|
||||||
|
}
|
||||||
|
}
|
125
utils/cw-proof/src/proof/key.rs
Normal file
125
utils/cw-proof/src/proof/key.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
use alloc::{
|
||||||
|
string::{String, ToString},
|
||||||
|
vec,
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use cosmrs::AccountId;
|
||||||
|
|
||||||
|
use crate::proof::prefix::ConstPrefix;
|
||||||
|
|
||||||
|
const CONTRACT_STORE_PREFIX: u8 = 0x03;
|
||||||
|
|
||||||
|
pub trait IntoKeys {
|
||||||
|
fn into_keys(self) -> Vec<Vec<u8>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K> IntoKeys for K
|
||||||
|
where
|
||||||
|
K: Into<Vec<u8>>,
|
||||||
|
{
|
||||||
|
fn into_keys(self) -> Vec<Vec<u8>> {
|
||||||
|
vec![self.into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PrefixedKey<P, K> {
|
||||||
|
key: K,
|
||||||
|
prefix: PhantomData<P>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, K> PrefixedKey<P, K> {
|
||||||
|
pub fn new(key: K) -> Self {
|
||||||
|
Self {
|
||||||
|
key,
|
||||||
|
prefix: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, K> IntoKeys for PrefixedKey<P, K>
|
||||||
|
where
|
||||||
|
K: Into<Vec<u8>>,
|
||||||
|
P: ConstPrefix,
|
||||||
|
{
|
||||||
|
fn into_keys(self) -> Vec<Vec<u8>> {
|
||||||
|
vec![P::PREFIX.to_string().into_bytes(), self.key.into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum CwAbciKey {
|
||||||
|
Item {
|
||||||
|
contract_address: AccountId,
|
||||||
|
storage_key: String,
|
||||||
|
},
|
||||||
|
Map {
|
||||||
|
contract_address: AccountId,
|
||||||
|
storage_key: String,
|
||||||
|
storage_namespace: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CwAbciKey {
|
||||||
|
pub fn new(
|
||||||
|
contract_address: AccountId,
|
||||||
|
storage_key: String,
|
||||||
|
storage_namespace: Option<String>,
|
||||||
|
) -> Self {
|
||||||
|
if let Some(storage_namespace) = storage_namespace {
|
||||||
|
Self::Map {
|
||||||
|
contract_address,
|
||||||
|
storage_key,
|
||||||
|
storage_namespace,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::Item {
|
||||||
|
contract_address,
|
||||||
|
storage_key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_tuple(self) -> (AccountId, String, Option<String>) {
|
||||||
|
match self {
|
||||||
|
CwAbciKey::Item {
|
||||||
|
contract_address,
|
||||||
|
storage_key,
|
||||||
|
} => (contract_address, storage_key, None),
|
||||||
|
CwAbciKey::Map {
|
||||||
|
contract_address,
|
||||||
|
storage_key,
|
||||||
|
storage_namespace,
|
||||||
|
} => (contract_address, storage_key, Some(storage_namespace)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from cw-storage-plus
|
||||||
|
fn encode_length(namespace: &[u8]) -> [u8; 2] {
|
||||||
|
assert!(
|
||||||
|
namespace.len() <= 0xFFFF,
|
||||||
|
"only supports namespaces up to length 0xFFFF"
|
||||||
|
);
|
||||||
|
|
||||||
|
let length_bytes = (namespace.len() as u32).to_be_bytes();
|
||||||
|
[length_bytes[2], length_bytes[3]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CwAbciKey> for Vec<u8> {
|
||||||
|
fn from(value: CwAbciKey) -> Self {
|
||||||
|
let (contract_address, storage_key, storage_namespace) = value.into_tuple();
|
||||||
|
|
||||||
|
let mut data = vec![CONTRACT_STORE_PREFIX];
|
||||||
|
data.append(&mut contract_address.to_bytes());
|
||||||
|
if let Some(namespace) = storage_namespace {
|
||||||
|
data.extend_from_slice(&CwAbciKey::encode_length(namespace.as_bytes()));
|
||||||
|
data.append(&mut namespace.into_bytes());
|
||||||
|
}
|
||||||
|
data.append(&mut storage_key.into_bytes());
|
||||||
|
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
36
utils/cw-proof/src/proof/mod.rs
Normal file
36
utils/cw-proof/src/proof/mod.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
use ics23::CommitmentProof;
|
||||||
|
use tendermint::merkle::proof::ProofOps;
|
||||||
|
|
||||||
|
use crate::error::ProofError;
|
||||||
|
|
||||||
|
pub mod cw;
|
||||||
|
pub mod key;
|
||||||
|
pub mod prefix;
|
||||||
|
|
||||||
|
// Copied from hermes
|
||||||
|
pub fn convert_tm_to_ics_merkle_proof(
|
||||||
|
tm_proof: &ProofOps,
|
||||||
|
) -> Result<Vec<CommitmentProof>, ProofError> {
|
||||||
|
let mut proofs = Vec::new();
|
||||||
|
|
||||||
|
for op in &tm_proof.ops {
|
||||||
|
let mut parsed = CommitmentProof { proof: None };
|
||||||
|
|
||||||
|
prost::Message::merge(&mut parsed, op.data.as_slice())
|
||||||
|
.map_err(|_| ProofError::CommitmentProofDecodingFailed)?;
|
||||||
|
|
||||||
|
proofs.push(parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(proofs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Proof {
|
||||||
|
type Key;
|
||||||
|
type Value;
|
||||||
|
type ProofOps;
|
||||||
|
|
||||||
|
fn verify(&self, root: Vec<u8>) -> Result<(), ProofError>;
|
||||||
|
}
|
10
utils/cw-proof/src/proof/prefix.rs
Normal file
10
utils/cw-proof/src/proof/prefix.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
pub trait ConstPrefix {
|
||||||
|
const PREFIX: &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PrefixWasm;
|
||||||
|
|
||||||
|
impl ConstPrefix for PrefixWasm {
|
||||||
|
const PREFIX: &'static str = "wasm";
|
||||||
|
}
|
42
utils/cw-proof/src/verifier/cw.rs
Normal file
42
utils/cw-proof/src/verifier/cw.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use alloc::borrow::Cow;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
use ics23::CommitmentProof;
|
||||||
|
|
||||||
|
use crate::error::ProofError;
|
||||||
|
use crate::verifier::{ics23::Ics23MembershipVerifier, multi::MultiVerifier, Verifier};
|
||||||
|
|
||||||
|
type Key = Vec<u8>;
|
||||||
|
type Value<'a> = Cow<'a, [u8]>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CwVerifier<'a>(MultiVerifier<Ics23MembershipVerifier<Key, Value<'a>>, 2>);
|
||||||
|
|
||||||
|
impl CwVerifier<'_> {
|
||||||
|
pub fn verify(
|
||||||
|
&self,
|
||||||
|
proofs: &[CommitmentProof; 2],
|
||||||
|
root: &Vec<u8>,
|
||||||
|
keys: &[Vec<u8>; 2],
|
||||||
|
value: &[u8],
|
||||||
|
) -> Result<(), ProofError> {
|
||||||
|
if root.is_empty() {
|
||||||
|
return Err(ProofError::EmptyMerkleRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.0
|
||||||
|
.verify_against_root(proofs, keys, &Cow::Borrowed(value), root)?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(ProofError::VerificationFailure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CwVerifier<'_> {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mv = MultiVerifier::new([
|
||||||
|
Ics23MembershipVerifier::new(ics23::iavl_spec()),
|
||||||
|
Ics23MembershipVerifier::new(ics23::tendermint_spec()),
|
||||||
|
]);
|
||||||
|
Self(mv)
|
||||||
|
}
|
||||||
|
}
|
67
utils/cw-proof/src/verifier/ics23.rs
Normal file
67
utils/cw-proof/src/verifier/ics23.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use ics23::{
|
||||||
|
calculate_existence_root, commitment_proof::Proof, verify_membership, CommitmentProof,
|
||||||
|
ProofSpec,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::error::ProofError;
|
||||||
|
use crate::verifier::Verifier;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Ics23MembershipVerifier<K, V> {
|
||||||
|
spec: ProofSpec,
|
||||||
|
_phantom: PhantomData<(K, V)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> Ics23MembershipVerifier<K, V> {
|
||||||
|
pub fn new(spec: ProofSpec) -> Self {
|
||||||
|
Self {
|
||||||
|
spec,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> Verifier for Ics23MembershipVerifier<K, V>
|
||||||
|
where
|
||||||
|
K: AsRef<[u8]>,
|
||||||
|
V: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
type Proof = CommitmentProof;
|
||||||
|
type Root = Vec<u8>;
|
||||||
|
type Key = K;
|
||||||
|
type Value = V;
|
||||||
|
type Error = ProofError;
|
||||||
|
|
||||||
|
fn verify(
|
||||||
|
&self,
|
||||||
|
commitment_proof: &Self::Proof,
|
||||||
|
key: &Self::Key,
|
||||||
|
value: &Self::Value,
|
||||||
|
) -> Result<Self::Root, Self::Error> {
|
||||||
|
if value.as_ref().is_empty() {
|
||||||
|
return Err(ProofError::EmptyVerifiedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(Proof::Exist(existence_proof)) = &commitment_proof.proof else {
|
||||||
|
return Err(ProofError::InvalidMerkleProof);
|
||||||
|
};
|
||||||
|
|
||||||
|
let root = calculate_existence_root::<ics23::HostFunctionsManager>(existence_proof)
|
||||||
|
.map_err(|_| ProofError::InvalidMerkleProof)?;
|
||||||
|
|
||||||
|
if !verify_membership::<ics23::HostFunctionsManager>(
|
||||||
|
commitment_proof,
|
||||||
|
&self.spec,
|
||||||
|
&root,
|
||||||
|
key.as_ref(),
|
||||||
|
value.as_ref(),
|
||||||
|
) {
|
||||||
|
return Err(ProofError::VerificationFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(root)
|
||||||
|
}
|
||||||
|
}
|
29
utils/cw-proof/src/verifier/mod.rs
Normal file
29
utils/cw-proof/src/verifier/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
pub mod cw;
|
||||||
|
pub mod ics23;
|
||||||
|
pub mod multi;
|
||||||
|
|
||||||
|
trait Verifier {
|
||||||
|
type Proof;
|
||||||
|
type Root: Eq;
|
||||||
|
type Key;
|
||||||
|
type Value;
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
fn verify(
|
||||||
|
&self,
|
||||||
|
proof: &Self::Proof,
|
||||||
|
key: &Self::Key,
|
||||||
|
value: &Self::Value,
|
||||||
|
) -> Result<Self::Root, Self::Error>;
|
||||||
|
|
||||||
|
fn verify_against_root(
|
||||||
|
&self,
|
||||||
|
proof: &Self::Proof,
|
||||||
|
key: &Self::Key,
|
||||||
|
value: &Self::Value,
|
||||||
|
root: &Self::Root,
|
||||||
|
) -> Result<bool, Self::Error> {
|
||||||
|
let found_root = self.verify(proof, key, value)?;
|
||||||
|
Ok(root == &found_root)
|
||||||
|
}
|
||||||
|
}
|
47
utils/cw-proof/src/verifier/multi.rs
Normal file
47
utils/cw-proof/src/verifier/multi.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use crate::verifier::Verifier;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct MultiVerifier<V, const N: usize> {
|
||||||
|
verifiers: [V; N],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, const N: usize> MultiVerifier<V, N> {
|
||||||
|
pub fn new(verifiers: [V; N]) -> Self {
|
||||||
|
assert!(N > 0, "cannot create empty multi-verifiers");
|
||||||
|
Self { verifiers }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, const N: usize> Verifier for MultiVerifier<V, N>
|
||||||
|
where
|
||||||
|
V: Verifier,
|
||||||
|
V::Root: Into<V::Value> + Clone,
|
||||||
|
V::Value: Clone,
|
||||||
|
{
|
||||||
|
type Proof = [V::Proof; N];
|
||||||
|
type Root = V::Root;
|
||||||
|
type Key = [V::Key; N];
|
||||||
|
type Value = V::Value;
|
||||||
|
type Error = V::Error;
|
||||||
|
|
||||||
|
fn verify(
|
||||||
|
&self,
|
||||||
|
proofs: &Self::Proof,
|
||||||
|
keys: &Self::Key,
|
||||||
|
value: &Self::Value,
|
||||||
|
) -> Result<Self::Root, Self::Error> {
|
||||||
|
let mut root = None;
|
||||||
|
let mut value: V::Value = value.clone();
|
||||||
|
|
||||||
|
for (idx, verifier) in self.verifiers.iter().enumerate() {
|
||||||
|
let proof = &proofs[idx];
|
||||||
|
let key = &keys[N - idx - 1];
|
||||||
|
let sub_root = verifier.verify(proof, key, &value)?;
|
||||||
|
|
||||||
|
value = sub_root.clone().into();
|
||||||
|
root = Some(sub_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(root.expect("MultiVerifier cannot be instantiated with 0 verifiers"))
|
||||||
|
}
|
||||||
|
}
|
141
utils/cw-prover/Cargo.lock
generated
141
utils/cw-prover/Cargo.lock
generated
|
@ -206,6 +206,16 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.4.11"
|
version = "4.4.11"
|
||||||
|
@ -349,17 +359,70 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cw-proof"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"cosmrs",
|
||||||
|
"displaydoc",
|
||||||
|
"ics23",
|
||||||
|
"prost",
|
||||||
|
"serde",
|
||||||
|
"serde_with",
|
||||||
|
"tendermint",
|
||||||
|
"tendermint-rpc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cw-prover"
|
name = "cw-prover"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"cosmrs",
|
"cosmrs",
|
||||||
|
"cw-proof",
|
||||||
|
"hex",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tendermint",
|
||||||
"tendermint-rpc",
|
"tendermint-rpc",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn 2.0.41",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.41",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
version = "0.7.8"
|
version = "0.7.8"
|
||||||
|
@ -400,6 +463,17 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "displaydoc"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.41",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecdsa"
|
name = "ecdsa"
|
||||||
version = "0.16.9"
|
version = "0.16.9"
|
||||||
|
@ -748,6 +822,27 @@ dependencies = [
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ics23"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "661e2d6f79952a65bc92b1c81f639ebd37228dae6ff412a5aba7d474bdc4b957"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bytes",
|
||||||
|
"hex",
|
||||||
|
"prost",
|
||||||
|
"ripemd",
|
||||||
|
"sha2 0.10.8",
|
||||||
|
"sha3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -816,6 +911,15 @@ dependencies = [
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keccak"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940"
|
||||||
|
dependencies = [
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.151"
|
version = "0.2.151"
|
||||||
|
@ -1373,6 +1477,33 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"chrono",
|
||||||
|
"hex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_with_macros",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.41",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.9.9"
|
version = "0.9.9"
|
||||||
|
@ -1397,6 +1528,16 @@ dependencies = [
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha3"
|
||||||
|
version = "0.10.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||||
|
dependencies = [
|
||||||
|
"digest 0.10.7",
|
||||||
|
"keccak",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
|
|
|
@ -8,6 +8,11 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.1.8", features = ["derive"] }
|
clap = { version = "4.1.8", features = ["derive"] }
|
||||||
cosmrs = "0.15.0"
|
cosmrs = "0.15.0"
|
||||||
|
cw-proof = {path = "../cw-proof"}
|
||||||
|
serde_json = "1.0.108"
|
||||||
|
tendermint = "0.34.0"
|
||||||
tendermint-rpc = { version = "0.34.0", features = ["http-client"] }
|
tendermint-rpc = { version = "0.34.0", features = ["http-client"] }
|
||||||
tokio = { version = "1.26.0", features = ["full"] }
|
tokio = { version = "1.26.0", features = ["full"] }
|
||||||
serde_json = "1.0.108"
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex = "0.4.3"
|
||||||
|
|
|
@ -14,11 +14,22 @@
|
||||||
unused_qualifications
|
unused_qualifications
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use std::error::Error;
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
fmt::Debug,
|
||||||
|
fs::File,
|
||||||
|
io::{BufWriter, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use cosmrs::AccountId;
|
use cosmrs::AccountId;
|
||||||
use tendermint_rpc::{client::HttpClient as TmRpcClient, Client, HttpClientUrl};
|
use cw_proof::proof::cw::RawCwProof;
|
||||||
|
use cw_proof::proof::{cw::CwProof, key::CwAbciKey, Proof};
|
||||||
|
use tendermint::{block::Height, AppHash};
|
||||||
|
use tendermint_rpc::{
|
||||||
|
client::HttpClient as TmRpcClient, endpoint::status::Response, Client, HttpClientUrl,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
|
@ -42,11 +53,19 @@ enum Command {
|
||||||
/// Storage key of the state item for which proofs must be retrieved
|
/// Storage key of the state item for which proofs must be retrieved
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
storage_key: String,
|
storage_key: String,
|
||||||
|
|
||||||
|
/// Storage namespace of the state item for which proofs must be retrieved
|
||||||
|
/// (only makes sense when dealing with maps)
|
||||||
|
#[clap(long)]
|
||||||
|
storage_namespace: Option<String>,
|
||||||
|
|
||||||
|
/// Output file to store merkle proof
|
||||||
|
#[clap(long)]
|
||||||
|
proof_file: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const WASM_STORE_KEY: &str = "/store/wasm/key";
|
const WASM_STORE_KEY: &str = "/store/wasm/key";
|
||||||
const CONTRACT_STORE_PREFIX: u8 = 0x03;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
@ -57,27 +76,141 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
rpc_url,
|
rpc_url,
|
||||||
contract_address,
|
contract_address,
|
||||||
storage_key,
|
storage_key,
|
||||||
|
storage_namespace,
|
||||||
|
proof_file,
|
||||||
} => {
|
} => {
|
||||||
let path = WASM_STORE_KEY.to_owned();
|
|
||||||
let data = {
|
|
||||||
let mut data = vec![CONTRACT_STORE_PREFIX];
|
|
||||||
data.append(&mut contract_address.to_bytes());
|
|
||||||
data.append(&mut storage_key.into_bytes());
|
|
||||||
data
|
|
||||||
};
|
|
||||||
|
|
||||||
let client = TmRpcClient::builder(rpc_url).build()?;
|
let client = TmRpcClient::builder(rpc_url).build()?;
|
||||||
let latest_height = client.status().await?.sync_info.latest_block_height;
|
let status = client.status().await?;
|
||||||
|
let (proof_height, latest_app_hash) = latest_proof_height_hash(status);
|
||||||
|
|
||||||
|
let path = WASM_STORE_KEY.to_owned();
|
||||||
|
let data = CwAbciKey::new(contract_address, storage_key, storage_namespace);
|
||||||
let result = client
|
let result = client
|
||||||
.abci_query(Some(path), data, Some(latest_height), true)
|
.abci_query(Some(path), data, Some(proof_height), true)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
println!(
|
let proof: CwProof = result.clone().try_into().map_err(into_string)?;
|
||||||
"{}",
|
proof
|
||||||
serde_json::to_string(&result).expect("infallible serializer")
|
.verify(latest_app_hash.clone().into())
|
||||||
);
|
.map_err(into_string)?;
|
||||||
|
|
||||||
|
println!("{}", String::from_utf8(result.value.clone())?);
|
||||||
|
|
||||||
|
if let Some(proof_file) = proof_file {
|
||||||
|
write_proof_to_file(proof_file, proof.into())?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_string<E: ToString>(e: E) -> String {
|
||||||
|
e.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn latest_proof_height_hash(status: Response) -> (Height, AppHash) {
|
||||||
|
let proof_height = {
|
||||||
|
let latest_height = status.sync_info.latest_block_height;
|
||||||
|
(latest_height.value() - 1)
|
||||||
|
.try_into()
|
||||||
|
.expect("infallible conversion")
|
||||||
|
};
|
||||||
|
let latest_app_hash = status.sync_info.latest_app_hash;
|
||||||
|
|
||||||
|
(proof_height, latest_app_hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_proof_to_file(proof_file: PathBuf, proof: RawCwProof) -> Result<(), Box<dyn Error>> {
|
||||||
|
let file = File::create(proof_file)?;
|
||||||
|
let mut writer = BufWriter::new(file);
|
||||||
|
serde_json::to_writer(&mut writer, &proof)?;
|
||||||
|
writer.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use cw_proof::{proof::cw::RawCwProof, proof::Proof};
|
||||||
|
use tendermint_rpc::endpoint::abci_query::AbciQuery;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_query_item() {
|
||||||
|
let abci_query_response = r#"{
|
||||||
|
"code": 0,
|
||||||
|
"log": "",
|
||||||
|
"info": "",
|
||||||
|
"index": "0",
|
||||||
|
"key": "A63kpfWAOkOYNcY2OVqNZI3uV7L8kNmNwX+ohxWbaWOLc2d4c3RhdGU=",
|
||||||
|
"value": "eyJjb21wdXRlX21yZW5jbGF2ZSI6ImRjNDNmOGM0MmQ4ZTVmNTJjOGJiZDY4ZjQyNjI0MjE1M2YwYmUxMDYzMGZmOGNjYTI1NTEyOWEzY2EwM2QyNzMiLCJrZXlfbWFuYWdlcl9tcmVuY2xhdmUiOiIxY2YyZTUyOTExNDEwZmJmM2YxOTkwNTZhOThkNTg3OTVhNTU5YTJlODAwOTMzZjdmY2QxM2QwNDg0NjIyNzFjIiwidGNiX2luZm8iOiIzMTIzODc2In0=",
|
||||||
|
"proof": {
|
||||||
|
"ops": [
|
||||||
|
{
|
||||||
|
"field_type": "ics23:iavl",
|
||||||
|
"key": "A63kpfWAOkOYNcY2OVqNZI3uV7L8kNmNwX+ohxWbaWOLc2d4c3RhdGU=",
|
||||||
|
"data": "CrgDCikDreSl9YA6Q5g1xjY5Wo1kje5XsvyQ2Y3Bf6iHFZtpY4tzZ3hzdGF0ZRLIAXsiY29tcHV0ZV9tcmVuY2xhdmUiOiJkYzQzZjhjNDJkOGU1ZjUyYzhiYmQ2OGY0MjYyNDIxNTNmMGJlMTA2MzBmZjhjY2EyNTUxMjlhM2NhMDNkMjczIiwia2V5X21hbmFnZXJfbXJlbmNsYXZlIjoiMWNmMmU1MjkxMTQxMGZiZjNmMTk5MDU2YTk4ZDU4Nzk1YTU1OWEyZTgwMDkzM2Y3ZmNkMTNkMDQ4NDYyMjcxYyIsInRjYl9pbmZvIjoiMzEyMzg3NiJ9GgwIARgBIAEqBAACmAEiKggBEiYCBJgBIFclzyzP2y2LTcBhP0IxBhvnlMJiEFCsDEMUQ9dM5dvYICIsCAESBQQGmAEgGiEgfUSWe0VMFTsxkzDuMQNE05aSzdRTTvkWzZXkfplWUbEiKggBEiYGDJBnIEkK+nmGmXpOfREXvfonLrK4mEZx1XF4DgJp86QIVF1EICIsCAESBQgakGcgGiEgBl/NSR16eG1vDenJA6GEEJ9xcQv9Bwxv8wyhAL5JLwE="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field_type": "ics23:simple",
|
||||||
|
"key": "d2FzbQ==",
|
||||||
|
"data": "CqgBCgR3YXNtEiDYWxn2B9M/eGP18Gwl3zgWZkT7Yn/iFlcS0THfmfcfDBoJCAEYASABKgEAIiUIARIhAWLU8PgnJ/EMp4BYvtTN9MX/rS70dNQ3ZAzrJLssrLjRIiUIARIhAcFEiiCwgvh2CwGJrnfnBCvuNl9u4BgngCVVKihSxYahIiUIARIhAVPQq6npMIxTVF19htERZGPpp0TZZaNLGho3+Y1oBFLg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"height": "7355",
|
||||||
|
"codespace": ""
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let abci_query: AbciQuery = serde_json::from_str(abci_query_response)
|
||||||
|
.expect("deserialization failure for hardcoded response");
|
||||||
|
let proof: RawCwProof = abci_query
|
||||||
|
.try_into()
|
||||||
|
.expect("hardcoded response does not include proof");
|
||||||
|
let root = "25a8b485e0ff095f7b60a1aab837d65756c9a4cdc216bae7ba9c59b3fb28fbec";
|
||||||
|
|
||||||
|
proof
|
||||||
|
.verify(hex::decode(root).expect("invalid hex"))
|
||||||
|
.expect("");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_query_map() {
|
||||||
|
let abci_query_response = r#"{
|
||||||
|
"code": 0,
|
||||||
|
"log": "",
|
||||||
|
"info": "",
|
||||||
|
"index": "0",
|
||||||
|
"key": "A63kpfWAOkOYNcY2OVqNZI3uV7L8kNmNwX+ohxWbaWOLAAhyZXF1ZXN0czQyNWQ4N2Y4NjIwZTFkZWRlZWU3MDU5MGNjNTViMTY0YjhmMDE0ODBlZTU5ZTBiMWRhMzU0MzZhMmY3YzI3Nzc=",
|
||||||
|
"value": "eyJqb2luX2NvbXB1dGVfbm9kZSI6WyIwM0U2N0VGMDkyMTM2MzMwNzRGQjRGQkYzMzg2NDNGNEYwQzU3NEVENjBFRjExRDAzNDIyRUVCMDZGQTM4QzhGM0YiLCJ3YXNtMTBuNGRzbGp5eWZwMmsyaHk2ZTh2dWM5cnkzMnB4MmVnd3Q1ZTBtIl19",
|
||||||
|
"proof": {
|
||||||
|
"ops": [
|
||||||
|
{
|
||||||
|
"field_type": "ics23:iavl",
|
||||||
|
"key": "A63kpfWAOkOYNcY2OVqNZI3uV7L8kNmNwX+ohxWbaWOLAAhyZXF1ZXN0czQyNWQ4N2Y4NjIwZTFkZWRlZWU3MDU5MGNjNTViMTY0YjhmMDE0ODBlZTU5ZTBiMWRhMzU0MzZhMmY3YzI3Nzc=",
|
||||||
|
"data": "CrwDCmsDreSl9YA6Q5g1xjY5Wo1kje5XsvyQ2Y3Bf6iHFZtpY4sACHJlcXVlc3RzNDI1ZDg3Zjg2MjBlMWRlZGVlZTcwNTkwY2M1NWIxNjRiOGYwMTQ4MGVlNTllMGIxZGEzNTQzNmEyZjdjMjc3NxKKAXsiam9pbl9jb21wdXRlX25vZGUiOlsiMDNFNjdFRjA5MjEzNjMzMDc0RkI0RkJGMzM4NjQzRjRGMEM1NzRFRDYwRUYxMUQwMzQyMkVFQjA2RkEzOEM4RjNGIiwid2FzbTEwbjRkc2xqeXlmcDJrMmh5NmU4dnVjOXJ5MzJweDJlZ3d0NWUwbSJdfRoMCAEYASABKgQAApBnIioIARImAgSQZyDcejBg60yYaDEvKvExWQf9XKfIaNU/Amt6hqCn7y+CSiAiKggBEiYEBpBnICanihuey/DZHbttCL13YV1SMnCD6D6J2zssxb7sqwrlICIsCAESBQYMkGcgGiEg+89wQcyopgtcMvQ2ceLVOsi6b3IcMYCR2UZrrqAV1xsiLAgBEgUIGpBnIBohIAZfzUkdenhtbw3pyQOhhBCfcXEL/QcMb/MMoQC+SS8B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field_type": "ics23:simple",
|
||||||
|
"key": "d2FzbQ==",
|
||||||
|
"data": "CqgBCgR3YXNtEiDYWxn2B9M/eGP18Gwl3zgWZkT7Yn/iFlcS0THfmfcfDBoJCAEYASABKgEAIiUIARIhAWLU8PgnJ/EMp4BYvtTN9MX/rS70dNQ3ZAzrJLssrLjRIiUIARIhARcbvq+IA7uFZQ37EHO4TUVW33UPw2gl4PnFAPf/w+LDIiUIARIhAZIp1f1XIqpz3QSNX3F9i7IGdc8DHeSpBJ/Qhg3httiR"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"height": "7589",
|
||||||
|
"codespace": ""
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let abci_query: AbciQuery = serde_json::from_str(abci_query_response)
|
||||||
|
.expect("deserialization failure for hardcoded response");
|
||||||
|
let proof: RawCwProof = abci_query
|
||||||
|
.try_into()
|
||||||
|
.expect("hardcoded response does not include proof");
|
||||||
|
let root = "632612de75657f50bbb769157bf0ef8dd417409b367b0204bbda4529ab2b2d4f";
|
||||||
|
|
||||||
|
proof
|
||||||
|
.verify(hex::decode(root).expect("invalid hex"))
|
||||||
|
.expect("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
348
utils/tm-prover/Cargo.lock
generated
348
utils/tm-prover/Cargo.lock
generated
|
@ -112,6 +112,12 @@ dependencies = [
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base16ct"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.5"
|
version = "0.21.5"
|
||||||
|
@ -124,6 +130,22 @@ version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bip32"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164"
|
||||||
|
dependencies = [
|
||||||
|
"bs58",
|
||||||
|
"hmac",
|
||||||
|
"k256",
|
||||||
|
"rand_core",
|
||||||
|
"ripemd",
|
||||||
|
"sha2 0.10.8",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -148,6 +170,15 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bs58"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896"
|
||||||
|
dependencies = [
|
||||||
|
"sha2 0.10.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.14.0"
|
version = "3.14.0"
|
||||||
|
@ -190,6 +221,16 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.4.7"
|
version = "4.4.7"
|
||||||
|
@ -296,6 +337,37 @@ version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cosmos-sdk-proto"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237"
|
||||||
|
dependencies = [
|
||||||
|
"prost",
|
||||||
|
"prost-types",
|
||||||
|
"tendermint-proto",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cosmrs"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47126f5364df9387b9d8559dcef62e99010e1d4098f39eb3f7ee4b5c254e40ea"
|
||||||
|
dependencies = [
|
||||||
|
"bip32",
|
||||||
|
"cosmos-sdk-proto",
|
||||||
|
"ecdsa",
|
||||||
|
"eyre",
|
||||||
|
"k256",
|
||||||
|
"rand_core",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"signature",
|
||||||
|
"subtle-encoding",
|
||||||
|
"tendermint",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
@ -326,6 +398,18 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-bigint"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -349,6 +433,56 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cw-proof"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"cosmrs",
|
||||||
|
"displaydoc",
|
||||||
|
"ics23",
|
||||||
|
"prost",
|
||||||
|
"serde",
|
||||||
|
"serde_with",
|
||||||
|
"tendermint",
|
||||||
|
"tendermint-rpc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn 2.0.39",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.39",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
version = "0.7.8"
|
version = "0.7.8"
|
||||||
|
@ -395,7 +529,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer 0.10.4",
|
"block-buffer 0.10.4",
|
||||||
|
"const-oid",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "displaydoc"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.39",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecdsa"
|
||||||
|
version = "0.16.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"digest 0.10.7",
|
||||||
|
"elliptic-curve",
|
||||||
|
"rfc6979",
|
||||||
|
"signature",
|
||||||
|
"spki",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -427,6 +588,25 @@ version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "elliptic-curve"
|
||||||
|
version = "0.13.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"crypto-bigint",
|
||||||
|
"digest 0.10.7",
|
||||||
|
"ff",
|
||||||
|
"generic-array",
|
||||||
|
"group",
|
||||||
|
"pkcs8",
|
||||||
|
"rand_core",
|
||||||
|
"sec1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.33"
|
version = "0.8.33"
|
||||||
|
@ -446,6 +626,16 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ff"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flex-error"
|
name = "flex-error"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
|
@ -568,6 +758,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -589,6 +780,17 @@ version = "0.28.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "group"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
|
@ -638,6 +840,15 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest 0.10.7",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
|
@ -710,6 +921,27 @@ dependencies = [
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ics23"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "661e2d6f79952a65bc92b1c81f639ebd37228dae6ff412a5aba7d474bdc4b957"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bytes",
|
||||||
|
"hex",
|
||||||
|
"prost",
|
||||||
|
"ripemd",
|
||||||
|
"sha2 0.10.8",
|
||||||
|
"sha3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -766,6 +998,27 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "k256"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"sha2 0.10.8",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keccak"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940"
|
||||||
|
dependencies = [
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -1041,9 +1294,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost"
|
name = "prost"
|
||||||
version = "0.12.1"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d"
|
checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"prost-derive",
|
"prost-derive",
|
||||||
|
@ -1051,9 +1304,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-derive"
|
name = "prost-derive"
|
||||||
version = "0.12.1"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
|
checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
@ -1085,6 +1338,9 @@ name = "rand_core"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
|
@ -1179,6 +1435,16 @@ dependencies = [
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfc6979"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||||
|
dependencies = [
|
||||||
|
"hmac",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.5"
|
version = "0.17.5"
|
||||||
|
@ -1193,6 +1459,15 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ripemd"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f"
|
||||||
|
dependencies = [
|
||||||
|
"digest 0.10.7",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
|
@ -1282,6 +1557,20 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sec1"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"der",
|
||||||
|
"generic-array",
|
||||||
|
"pkcs8",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.9.2"
|
version = "2.9.2"
|
||||||
|
@ -1384,6 +1673,33 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"chrono",
|
||||||
|
"hex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_with_macros",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.39",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.9.9"
|
version = "0.9.9"
|
||||||
|
@ -1408,6 +1724,16 @@ dependencies = [
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha3"
|
||||||
|
version = "0.10.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||||
|
dependencies = [
|
||||||
|
"digest 0.10.7",
|
||||||
|
"keccak",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
@ -1431,6 +1757,10 @@ name = "signature"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
|
checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
|
||||||
|
dependencies = [
|
||||||
|
"digest 0.10.7",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
|
@ -1571,10 +1901,12 @@ dependencies = [
|
||||||
"ed25519-consensus",
|
"ed25519-consensus",
|
||||||
"flex-error",
|
"flex-error",
|
||||||
"futures",
|
"futures",
|
||||||
|
"k256",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"prost",
|
"prost",
|
||||||
"prost-types",
|
"prost-types",
|
||||||
|
"ripemd",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -1793,8 +2125,12 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
|
"cosmrs",
|
||||||
|
"cw-proof",
|
||||||
"futures",
|
"futures",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_with",
|
||||||
"tendermint",
|
"tendermint",
|
||||||
"tendermint-light-client",
|
"tendermint-light-client",
|
||||||
"tendermint-light-client-detector",
|
"tendermint-light-client-detector",
|
||||||
|
@ -2228,9 +2564,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
|
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zeroize_derive",
|
"zeroize_derive",
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,15 +6,17 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { version = "4.1.8", features = ["derive"] }
|
||||||
|
color-eyre = "0.6.2"
|
||||||
|
cosmrs = "0.15.0"
|
||||||
|
cw-proof = { path = "../cw-proof" }
|
||||||
|
futures = "0.3.27"
|
||||||
|
serde = { version = "1.0.189", features = ["derive"] }
|
||||||
|
serde_json = "1.0.94"
|
||||||
tendermint = "0.34.0"
|
tendermint = "0.34.0"
|
||||||
tendermint-rpc = { version = "0.34.0", features = ["http-client"] }
|
tendermint-rpc = { version = "0.34.0", features = ["http-client"] }
|
||||||
tendermint-light-client = "0.34.0"
|
tendermint-light-client = "0.34.0"
|
||||||
tendermint-light-client-detector = "0.34.0"
|
tendermint-light-client-detector = "0.34.0"
|
||||||
|
|
||||||
clap = { version = "4.1.8", features = ["derive"] }
|
|
||||||
color-eyre = "0.6.2"
|
|
||||||
futures = "0.3.27"
|
|
||||||
serde_json = "1.0.94"
|
|
||||||
tokio = { version = "1.26.0", features = ["full"] }
|
tokio = { version = "1.26.0", features = ["full"] }
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||||
|
|
|
@ -7,10 +7,11 @@ block height and trusted height/hash.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run -- --chain-id osmosis-1 \
|
cargo run -- --chain-id osmosis-1 \
|
||||||
--primary https://rpc.osmosis.zone \
|
--primary "http://127.0.0.1:26657" \
|
||||||
--witnesses https://rpc.osmosis.zone \
|
--witnesses "http://127.0.0.1:26657" \
|
||||||
--trusted-height 12230413 \
|
--trusted-height 400 \
|
||||||
--trusted-hash D3742DD1573436AF972472135A24B31D5ACE9A2C04791A76196F875955B90F1D \
|
--trusted-hash "DEA1738C2AEE72E935E39CE6EB8765B8782B791038789AC2FEA446526FDE8638" \
|
||||||
--height 12230423 \
|
--contract-address "wasm17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9jfksztgw5uh69wac2pgsm0v070" \
|
||||||
|
--storage-key "requests" \
|
||||||
--trace-file light-client-proof.json
|
--trace-file light-client-proof.json
|
||||||
```
|
```
|
||||||
|
|
1
utils/tm-prover/light-client-proof.json
Normal file
1
utils/tm-prover/light-client-proof.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -20,7 +20,12 @@ use color_eyre::{
|
||||||
eyre::{eyre, Result},
|
eyre::{eyre, Result},
|
||||||
Report,
|
Report,
|
||||||
};
|
};
|
||||||
|
use cosmrs::AccountId;
|
||||||
|
use cw_proof::error::ProofError;
|
||||||
|
use cw_proof::proof::cw::RawCwProof;
|
||||||
|
use cw_proof::proof::{cw::CwProof, key::CwAbciKey, Proof};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use tendermint::{crypto::default::Sha256, evidence::Evidence};
|
use tendermint::{crypto::default::Sha256, evidence::Evidence};
|
||||||
use tendermint_light_client::{
|
use tendermint_light_client::{
|
||||||
builder::LightClientBuilder,
|
builder::LightClientBuilder,
|
||||||
|
@ -29,10 +34,12 @@ use tendermint_light_client::{
|
||||||
types::{Hash, Height, LightBlock, TrustThreshold},
|
types::{Hash, Height, LightBlock, TrustThreshold},
|
||||||
};
|
};
|
||||||
use tendermint_light_client_detector::{detect_divergence, Error, Provider, Trace};
|
use tendermint_light_client_detector::{detect_divergence, Error, Provider, Trace};
|
||||||
use tendermint_rpc::{Client, HttpClient, HttpClientUrl};
|
use tendermint_rpc::{client::HttpClient, Client, HttpClientUrl};
|
||||||
use tracing::{error, info, metadata::LevelFilter};
|
use tracing::{error, info, metadata::LevelFilter};
|
||||||
use tracing_subscriber::{util::SubscriberInitExt, EnvFilter};
|
use tracing_subscriber::{util::SubscriberInitExt, EnvFilter};
|
||||||
|
|
||||||
|
const WASM_STORE_KEY: &str = "/store/wasm/key";
|
||||||
|
|
||||||
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
|
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
|
||||||
if let Some((l, r)) = s.split_once('/') {
|
if let Some((l, r)) = s.split_once('/') {
|
||||||
TrustThreshold::new(l.parse()?, r.parse()?).map_err(Into::into)
|
TrustThreshold::new(l.parse()?, r.parse()?).map_err(Into::into)
|
||||||
|
@ -74,6 +81,12 @@ impl Verbosity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
struct ProofOutput {
|
||||||
|
light_client_proof: Vec<LightBlock>,
|
||||||
|
merkle_proof: RawCwProof,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
|
@ -82,7 +95,7 @@ struct Cli {
|
||||||
chain_id: String,
|
chain_id: String,
|
||||||
|
|
||||||
/// Primary RPC address
|
/// Primary RPC address
|
||||||
#[clap(long)]
|
#[clap(long, default_value = "http://127.0.0.1:26657")]
|
||||||
primary: HttpClientUrl,
|
primary: HttpClientUrl,
|
||||||
|
|
||||||
/// Comma-separated list of witnesses RPC addresses
|
/// Comma-separated list of witnesses RPC addresses
|
||||||
|
@ -97,10 +110,6 @@ struct Cli {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
trusted_hash: Hash,
|
trusted_hash: Hash,
|
||||||
|
|
||||||
/// Height of the header to verify
|
|
||||||
#[clap(long)]
|
|
||||||
height: Option<Height>,
|
|
||||||
|
|
||||||
/// Trust threshold
|
/// Trust threshold
|
||||||
#[clap(long, value_parser = parse_trust_threshold, default_value_t = TrustThreshold::TWO_THIRDS)]
|
#[clap(long, value_parser = parse_trust_threshold, default_value_t = TrustThreshold::TWO_THIRDS)]
|
||||||
trust_threshold: TrustThreshold,
|
trust_threshold: TrustThreshold,
|
||||||
|
@ -124,6 +133,14 @@ struct Cli {
|
||||||
/// Increase verbosity
|
/// Increase verbosity
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
verbose: Verbosity,
|
verbose: Verbosity,
|
||||||
|
|
||||||
|
/// Address of the CosmWasm contract
|
||||||
|
#[clap(long)]
|
||||||
|
contract_address: AccountId,
|
||||||
|
|
||||||
|
/// Storage key of the state item for which proofs must be retrieved
|
||||||
|
#[clap(long)]
|
||||||
|
storage_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -150,23 +167,31 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
let mut primary = make_provider(
|
let mut primary = make_provider(
|
||||||
&args.chain_id,
|
&args.chain_id,
|
||||||
args.primary,
|
args.primary.clone(),
|
||||||
args.trusted_height,
|
args.trusted_height,
|
||||||
args.trusted_hash,
|
args.trusted_hash,
|
||||||
options,
|
options,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let client = HttpClient::builder(args.primary.clone()).build()?;
|
||||||
|
|
||||||
let trusted_block = primary
|
let trusted_block = primary
|
||||||
.latest_trusted()
|
.latest_trusted()
|
||||||
.ok_or_else(|| eyre!("No trusted state found for primary"))?;
|
.ok_or_else(|| eyre!("No trusted state found for primary"))?;
|
||||||
|
|
||||||
let primary_block = if let Some(target_height) = args.height {
|
let primary_block = {
|
||||||
info!("Verifying to height {} on primary...", target_height);
|
|
||||||
primary.verify_to_height(target_height)
|
|
||||||
} else {
|
|
||||||
info!("Verifying to latest height on primary...");
|
info!("Verifying to latest height on primary...");
|
||||||
primary.verify_to_highest()
|
|
||||||
|
let status = client.status().await?;
|
||||||
|
let proof_height = {
|
||||||
|
let latest_height = status.sync_info.latest_block_height;
|
||||||
|
(latest_height.value() - 1)
|
||||||
|
.try_into()
|
||||||
|
.expect("infallible conversion")
|
||||||
|
};
|
||||||
|
|
||||||
|
primary.verify_to_height(proof_height)
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
info!("Verified to height {} on primary", primary_block.height());
|
info!("Verified to height {} on primary", primary_block.height());
|
||||||
|
@ -197,14 +222,36 @@ async fn main() -> Result<()> {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let status = client.status().await?;
|
||||||
|
let (proof_height, latest_app_hash) =
|
||||||
|
(primary_block.height(), status.sync_info.latest_app_hash);
|
||||||
|
|
||||||
|
let path = WASM_STORE_KEY.to_owned();
|
||||||
|
let data = CwAbciKey::new(args.contract_address, args.storage_key, None);
|
||||||
|
let result = client
|
||||||
|
.abci_query(Some(path), data, Some(proof_height), true)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let proof: CwProof = result
|
||||||
|
.clone()
|
||||||
|
.try_into()
|
||||||
|
.expect("result should contain proof");
|
||||||
|
proof
|
||||||
|
.verify(latest_app_hash.clone().into())
|
||||||
|
.map_err(|e: ProofError| eyre!(e))?;
|
||||||
|
|
||||||
if let Some(trace_file) = args.trace_file {
|
if let Some(trace_file) = args.trace_file {
|
||||||
write_trace_to_file(trace_file, primary_trace).await?;
|
let output = ProofOutput {
|
||||||
|
light_client_proof: primary_trace,
|
||||||
|
merkle_proof: proof.into(),
|
||||||
|
};
|
||||||
|
write_proof_to_file(trace_file, output).await?;
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_trace_to_file(trace_file: PathBuf, output: Vec<LightBlock>) -> Result<()> {
|
async fn write_proof_to_file(trace_file: PathBuf, output: ProofOutput) -> Result<()> {
|
||||||
info!("Writing proof to output file ({})", trace_file.display());
|
info!("Writing proof to output file ({})", trace_file.display());
|
||||||
|
|
||||||
let file = File::create(trace_file)?;
|
let file = File::create(trace_file)?;
|
||||||
|
|
2
utils/tm-stateless-verifier/.gitignore
vendored
Normal file
2
utils/tm-stateless-verifier/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Cargo.lock
|
||||||
|
|
9
utils/tm-stateless-verifier/Cargo.toml
Normal file
9
utils/tm-stateless-verifier/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "tm-stateless-verifier"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
displaydoc = { version = "0.2.4", default-features = false }
|
||||||
|
tendermint = { version = "=0.34.0", default-features = false }
|
||||||
|
tendermint-light-client = { version = "=0.34.0", default-features = false, features = ["rust-crypto"] }
|
34
utils/tm-stateless-verifier/src/error.rs
Normal file
34
utils/tm-stateless-verifier/src/error.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
|
use displaydoc::Display;
|
||||||
|
use tendermint::{block::Height, Hash};
|
||||||
|
use tendermint_light_client::{
|
||||||
|
builder::error::Error as TmBuilderError, errors::Error as LightClientError,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Display)]
|
||||||
|
pub enum Error {
|
||||||
|
/// empty trace
|
||||||
|
EmptyTrace,
|
||||||
|
/// first block in trace does not match trusted (expected {expected:?}, found {found:?})
|
||||||
|
FirstTraceBlockNotTrusted {
|
||||||
|
expected: (Height, Hash),
|
||||||
|
found: (Height, Hash),
|
||||||
|
},
|
||||||
|
/// verification failure (`{0}`)
|
||||||
|
VerificationFailure(Box<LightClientError>),
|
||||||
|
/// failed to build light client (`{0}`)
|
||||||
|
LightClientBuildFailure(Box<TmBuilderError>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LightClientError> for Error {
|
||||||
|
fn from(e: LightClientError) -> Self {
|
||||||
|
Error::VerificationFailure(Box::new(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TmBuilderError> for Error {
|
||||||
|
fn from(e: TmBuilderError) -> Self {
|
||||||
|
Error::LightClientBuildFailure(Box::new(e))
|
||||||
|
}
|
||||||
|
}
|
24
utils/tm-stateless-verifier/src/lib.rs
Normal file
24
utils/tm-stateless-verifier/src/lib.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#![no_std]
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![warn(
|
||||||
|
clippy::checked_conversions,
|
||||||
|
clippy::panic,
|
||||||
|
clippy::panic_in_result_fn,
|
||||||
|
clippy::unwrap_used,
|
||||||
|
trivial_casts,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
rust_2018_idioms,
|
||||||
|
unused_lifetimes,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_qualifications
|
||||||
|
)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
mod null_io;
|
||||||
|
mod provider;
|
||||||
|
|
||||||
|
pub use error::Error;
|
||||||
|
pub use provider::make_provider;
|
||||||
|
pub use provider::StatelessProvider;
|
13
utils/tm-stateless-verifier/src/null_io.rs
Normal file
13
utils/tm-stateless-verifier/src/null_io.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use tendermint_light_client::{
|
||||||
|
components::io::{AtHeight, Io, IoError},
|
||||||
|
types::LightBlock,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct NullIo;
|
||||||
|
|
||||||
|
impl Io for NullIo {
|
||||||
|
fn fetch_light_block(&self, _height: AtHeight) -> Result<LightBlock, IoError> {
|
||||||
|
unimplemented!("stateless verification does NOT need access to Io")
|
||||||
|
}
|
||||||
|
}
|
97
utils/tm-stateless-verifier/src/provider.rs
Normal file
97
utils/tm-stateless-verifier/src/provider.rs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::string::{String, ToString};
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
use tendermint::Hash;
|
||||||
|
use tendermint_light_client::{
|
||||||
|
builder::LightClientBuilder,
|
||||||
|
components::{clock::SystemClock, scheduler},
|
||||||
|
instance::Instance,
|
||||||
|
light_client::Options,
|
||||||
|
predicates::ProdPredicates,
|
||||||
|
store::{memory::MemoryStore, LightStore},
|
||||||
|
types::{Height, LightBlock, Status},
|
||||||
|
verifier::ProdVerifier,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
use crate::null_io::NullIo;
|
||||||
|
|
||||||
|
/// A interface over a stateless light client instance.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StatelessProvider {
|
||||||
|
#[allow(unused)]
|
||||||
|
chain_id: String,
|
||||||
|
instance: Instance,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatelessProvider {
|
||||||
|
pub fn new(chain_id: String, instance: Instance) -> Self {
|
||||||
|
Self { chain_id, instance }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_to_height(&mut self, height: Height) -> Result<LightBlock, Error> {
|
||||||
|
self.instance
|
||||||
|
.light_client
|
||||||
|
.verify_to_target(height, &mut self.instance.state)
|
||||||
|
.map_err(Into::<Error>::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_provider(
|
||||||
|
chain_id: &str,
|
||||||
|
trusted_height: Height,
|
||||||
|
trusted_hash: Hash,
|
||||||
|
trace: Vec<LightBlock>,
|
||||||
|
options: Options,
|
||||||
|
) -> Result<StatelessProvider, Error> {
|
||||||
|
// Make sure the trace is not empty and that the first light block corresponds to trusted
|
||||||
|
verify_trace_against_trusted(&trace, trusted_height, trusted_hash)?;
|
||||||
|
|
||||||
|
let mut light_store = Box::new(MemoryStore::new());
|
||||||
|
|
||||||
|
for light_block in &trace {
|
||||||
|
light_store.insert(light_block.clone(), Status::Unverified);
|
||||||
|
}
|
||||||
|
|
||||||
|
let node_id = trace[0].provider;
|
||||||
|
|
||||||
|
let instance = LightClientBuilder::custom(
|
||||||
|
node_id,
|
||||||
|
options,
|
||||||
|
light_store,
|
||||||
|
Box::new(NullIo {}),
|
||||||
|
Box::new(SystemClock),
|
||||||
|
#[allow(clippy::box_default)]
|
||||||
|
Box::new(ProdVerifier::default()),
|
||||||
|
Box::new(scheduler::basic_bisecting_schedule),
|
||||||
|
Box::new(ProdPredicates),
|
||||||
|
)
|
||||||
|
.trust_light_block(trace[0].clone())
|
||||||
|
.map_err(Into::<Error>::into)?
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Ok(StatelessProvider::new(chain_id.to_string(), instance))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_trace_against_trusted(
|
||||||
|
trace: &[LightBlock],
|
||||||
|
trusted_height: Height,
|
||||||
|
trusted_hash: Hash,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let Some(first_block) = trace.first() else {
|
||||||
|
return Err(Error::EmptyTrace);
|
||||||
|
};
|
||||||
|
|
||||||
|
let first_height = first_block.signed_header.header.height;
|
||||||
|
let first_hash = first_block.signed_header.header.hash();
|
||||||
|
|
||||||
|
if first_height != trusted_height || first_hash != trusted_hash {
|
||||||
|
return Err(Error::FirstTraceBlockNotTrusted {
|
||||||
|
expected: (first_height, first_hash),
|
||||||
|
found: (trusted_height, trusted_hash),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in a new issue