Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cross-Chain Queries (ICS-031) refactoring #2915

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
0aa3852
feat: basic structure for cross-chain-query packet
SangyunOck Nov 19, 2022
090d64e
fix: replace thiserror to flexerror
SangyunOck Nov 19, 2022
56cc63c
feat: cross chain query request & response handle
SangyunOck Nov 19, 2022
a5019ce
feat: collect events related to cross chain query packet object
SangyunOck Nov 19, 2022
31b6079
feat: filter cross chain query not to be relayed to queried chain
SangyunOck Nov 19, 2022
855e2a3
feat: cross chain query worker draft
SangyunOck Nov 19, 2022
e9e7bdd
feat: cross chain query handle implement
SangyunOck Nov 19, 2022
c17c727
feat: add counterparty chain for cross chain query object
SangyunOck Nov 23, 2022
a73b4ca
feat: merkle proof for rpc query
SangyunOck Nov 23, 2022
4fdf61f
fix: use tendermint merkle proof
SangyunOck Nov 23, 2022
4942bd9
fix: queried chain
SangyunOck Nov 25, 2022
9028d0b
chore: cross chain query response height into i64
SangyunOck Nov 25, 2022
f585efd
fix: i64 parse
SangyunOck Nov 25, 2022
7167860
feat: response icq to querying chain
SangyunOck Nov 26, 2022
a798c03
TODO: update consensus state before sending query response
Nov 29, 2022
d89e372
error: merkle proof
SangyunOck Nov 30, 2022
a62e791
feat: icq response for stride
SangyunOck Dec 1, 2022
13fd9d5
chore: remove e2e skeleton code
SangyunOck Dec 1, 2022
a9ff531
chore: cross chain query related comments update
SangyunOck Dec 2, 2022
5702c90
feat: use tendermint-rs prost built proofops
SangyunOck Dec 2, 2022
6a04a4c
chore: replace type_url for stride
SangyunOck Dec 2, 2022
5714f38
fix: clippy
SangyunOck Dec 5, 2022
759fb5c
fix: conflict
SangyunOck Dec 5, 2022
8df6a03
refactor: update to master
SangyunOck Dec 5, 2022
0d40f10
refactor: update to master
SangyunOck Dec 5, 2022
7b6ca3b
fix: clippy warnings
SangyunOck Dec 5, 2022
e0a2b2d
chore: fmt
SangyunOck Dec 5, 2022
1748ef9
Merge branch 'master' into refactoring/ics-031-crosschain-queries
SangyunOck Dec 6, 2022
136201a
refactor: handle error when parsing icq events from abci event
SangyunOck Dec 7, 2022
8458817
refactor: elaborate errors
SangyunOck Dec 7, 2022
23e9b1e
feat: inc metric for counting
SangyunOck Dec 7, 2022
96591ec
fix: filter ccq object
SangyunOck Dec 7, 2022
1e8e5dc
fix: handle empty cross chain query response
SangyunOck Dec 7, 2022
4805f9b
Merge branch 'master' into refactoring/ics-031-crosschain-queries
SangyunOck Dec 7, 2022
2b894b5
Merge branch 'refactoring/ics-031-crosschain-queries' of github.com:v…
SangyunOck Dec 7, 2022
588a82f
Merge remote-tracking branch 'hermes/master' into refactoring/ics-031…
SangyunOck Dec 15, 2022
5c3637d
feat: change not found element message for events
SangyunOck Dec 15, 2022
2be623d
fix: add ibc query to all tendermint queries vec
SangyunOck Dec 15, 2022
3ae48a9
Merge remote-tracking branch 'hermes/master' into refactoring/ics-031…
SangyunOck Dec 17, 2022
23d9918
feat: use stride-patched ibc-proto-rs
SangyunOck Dec 17, 2022
5344cf3
fix: remove unnecessary clone
SangyunOck Dec 19, 2022
11c57c4
chore: clarify interchain query error
SangyunOck Dec 19, 2022
715ad85
docs: unclog changelog
SangyunOck Dec 20, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Implementation of ics-031 cross-chain-query
([#2915](https://github.com/informalsystems/hermes/pull/2915))
3 changes: 1 addition & 2 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ members = [
# tendermint-light-client = { git = "https://github.com/informalsystems/tendermint-rs", branch = "v0.23.x" }
# tendermint-light-client-verifier = { git = "https://github.com/informalsystems/tendermint-rs", branch = "v0.23.x" }
# tendermint-testgen = { git = "https://github.com/informalsystems/tendermint-rs", branch = "v0.23.x" }
ibc-proto = { git = "https://github.com/cosmos/ibc-proto-rs", branch = "main" }
45 changes: 45 additions & 0 deletions crates/relayer-types/src/applications/ics31_icq/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::core::ics24_host::error::ValidationError as Ics24ValidationError;
use tendermint::error::Error as TendermintError;

use crate::prelude::*;
use flex_error::define_error;

define_error! {
Error {
Parse
| _ | { "Failed to parse content" },

Event
{ event: String }
| e | { format!("Event attribute not found: {}", e.event) },

Ics24
{ error: Ics24ValidationError }
| e | { format!("ics24 validation error: {:?}", e.error) },

Tendermint
{ error: TendermintError }
| e | { format!("Tendermint error: {:?}", e.error) },

Query
| _ | { "Failed to query data" },

Proof
| _ | { "Proof not found" },

ProtoEncode
| _ | { "Failed to encode interchain query Protobuf" },
}
}

impl From<Ics24ValidationError> for Error {
fn from(e: Ics24ValidationError) -> Self {
Self::ics24(e)
}
}

impl From<TendermintError> for Error {
fn from(e: TendermintError) -> Self {
Self::tendermint(e)
}
}
158 changes: 158 additions & 0 deletions crates/relayer-types/src/applications/ics31_icq/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use crate::core::ics24_host::identifier::{ChainId, ConnectionId};
use crate::events::IbcEvent;
use crate::prelude::*;

use super::error::Error;

use core::str::FromStr;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use tendermint::{abci, block::Height};
const EVENT_TYPE_PREFIX: &str = "query_request";

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct CrossChainQueryPacket {
pub module: String,
pub action: String,
pub query_id: String,
pub chain_id: ChainId,
pub connection_id: ConnectionId,
pub query_type: String,
pub height: Height,
pub request: String,
}

fn find_value<'a>(key: &str, entries: &'a [abci::EventAttribute]) -> Result<&'a str, Error> {
entries
.iter()
.find_map(|entry| {
if entry.key == key {
Some(entry.value.as_str())
} else {
None
}
})
.ok_or_else(|| Error::event(format!("attribute not found for key: {}", key)))
}

fn new_attr(key: &str, value: &str) -> abci::EventAttribute {
abci::EventAttribute {
key: String::from(key),
value: String::from(value),
index: true,
}
}

impl From<CrossChainQueryPacket> for abci::Event {
fn from(packet: CrossChainQueryPacket) -> Self {
let attributes: Vec<abci::EventAttribute> = vec![
new_attr("module", packet.module.as_str()),
new_attr("action", packet.action.as_str()),
new_attr("query_id", packet.query_id.as_str()),
new_attr("chain_id", packet.chain_id.as_str()),
new_attr("connection_id", packet.connection_id.as_str()),
new_attr("type", &packet.query_type.to_string()),
new_attr("request", packet.request.as_str()),
new_attr("height", &packet.height.to_string()),
];

abci::Event {
kind: String::from("message"),
attributes,
}
}
}

impl<'a> TryFrom<&'a [abci::EventAttribute]> for CrossChainQueryPacket {
type Error = Error;

fn try_from(entries: &'a [abci::EventAttribute]) -> Result<Self, Error> {
let module = find_value("module", entries)?.to_string();
let action = find_value("action", entries)?.to_string();
let query_id = find_value("query_id", entries)?.to_string();
let chain_id_str = find_value("chain_id", entries)?;
let connection_id_str = find_value("connection_id", entries)?;
let query_type = find_value("type", entries)?.to_string();
let request = find_value("request", entries)?.to_string();
let height_str = find_value("height", entries)?;

let chain_id = ChainId::from_string(chain_id_str);
let connection_id = ConnectionId::from_str(connection_id_str)?;
let height = Height::from_str(height_str)?;

Ok(Self {
module,
action,
query_id,
chain_id,
connection_id,
query_type,
height,
request,
})
}
}

fn fetch_first_element_from_events(
block_events: &BTreeMap<String, Vec<String>>,
key: &str,
) -> Result<String, Error> {
let res = block_events
.get(key)
.ok_or_else(|| Error::event(format!("attribute not found for key: {}", key)))?
.get(0)
.ok_or_else(|| {
Error::event(format!(
"element at position 0, of attribute with key `{}`, not found",
key
))
})?;

Ok(res.clone())
}

impl CrossChainQueryPacket {
pub fn extract_query_event(
block_events: &BTreeMap<String, Vec<String>>,
) -> Result<IbcEvent, Error> {
let chain_id_str = fetch_first_element_from_events(
block_events,
&format!("{}.{}", EVENT_TYPE_PREFIX, "chain_id"),
)?;
let connection_id_str = fetch_first_element_from_events(
block_events,
&format!("{}.{}", EVENT_TYPE_PREFIX, "connection_id"),
)?;
let query_type = fetch_first_element_from_events(
block_events,
&format!("{}.{}", EVENT_TYPE_PREFIX, "type"),
)?;
let height_str = fetch_first_element_from_events(
block_events,
&format!("{}.{}", EVENT_TYPE_PREFIX, "height"),
)?;

Ok(IbcEvent::CrossChainQueryPacket(CrossChainQueryPacket {
module: fetch_first_element_from_events(
block_events,
&format!("{}.{}", EVENT_TYPE_PREFIX, "module"),
)?,
action: fetch_first_element_from_events(
block_events,
&format!("{}.{}", EVENT_TYPE_PREFIX, "action"),
)?,
query_id: fetch_first_element_from_events(
block_events,
&format!("{}.{}", EVENT_TYPE_PREFIX, "query_id"),
)?,
chain_id: ChainId::from_string(&chain_id_str),
connection_id: ConnectionId::from_str(&connection_id_str)?,
query_type,
height: Height::from_str(&height_str)?,
request: fetch_first_element_from_events(
block_events,
&format!("{}.{}", EVENT_TYPE_PREFIX, "request"),
)?,
}))
}
}
3 changes: 3 additions & 0 deletions crates/relayer-types/src/applications/ics31_icq/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod error;
pub mod events;
pub mod response;
75 changes: 75 additions & 0 deletions crates/relayer-types/src/applications/ics31_icq/response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::applications::ics31_icq::error::Error;
use crate::signer::Signer;

use ibc_proto::google::protobuf::Any;
use ibc_proto::stride::interchainquery::v1::MsgSubmitQueryResponse;
use prost::Message;
use std::prelude::v1::*;
use std::vec;
use tendermint::merkle::proof::ProofOps as TendermintProofOps;
use tendermint_proto::crypto::{ProofOp, ProofOps};

pub const TYPE_URL: &str = "/stride.interchainquery.v1.MsgSubmitQueryResponse";

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CrossChainQueryResponse {
pub chain_id: String,
pub query_id: String,
pub result: Vec<u8>,
pub height: i64,
pub proof: TendermintProofOps,
}

fn into_proof_ops(merkle_proof: TendermintProofOps) -> ProofOps {
ProofOps {
ops: merkle_proof
.ops
.into_iter()
.map(|o| ProofOp {
r#type: o.field_type,
key: o.key,
data: o.data,
})
.collect(),
}
}

impl CrossChainQueryResponse {
pub fn new(
chain_id: String,
query_id: String,
result: Vec<u8>,
height: i64,
proof: TendermintProofOps,
) -> Self {
Self {
chain_id,
query_id,
result,
height,
proof,
}
}

pub fn try_to_any(&self, signer: Signer) -> Result<Any, Error> {
let mut encoded = vec![];

let msg_submit_cross_chain_query_result = MsgSubmitQueryResponse {
chain_id: self.chain_id.to_string(),
query_id: self.query_id.to_string(),
result: self.result.clone(),
proof_ops: Some(into_proof_ops(self.proof.clone())),
height: self.height,
from_address: signer.as_ref().to_string(),
};

msg_submit_cross_chain_query_result
.encode(&mut encoded)
.map_err(|_| Error::proto_encode())?;

Ok(Any {
type_url: TYPE_URL.to_string(),
value: encoded,
})
}
}
1 change: 1 addition & 0 deletions crates/relayer-types/src/applications/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Various packet encoding semantics which underpin the various types of transactions.

pub mod ics29_fee;
pub mod ics31_icq;
pub mod transfer;
Loading