diff --git a/CHANGELOG.md b/CHANGELOG.md index edc404ee5d..3d4c1f4a25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,66 +2,69 @@ ## Unreleased Changes -### FEATURES +> [high level summary] -- +### FEATURES - [ibc] - - + - [nothing yet] - [ibc-relayer] - Listen to channel close initialization event and perform the close handshake ([#560]) - [ibc-relayer-cli] - - + - [nothing yet] ### IMPROVEMENTS -- - - [ibc] - - + - [nothing yet] - [ibc-relayer] - - + - [nothing yet] - [ibc-relayer-cli] - - + - [nothing yet] -### BUG FIXES: +### BUG FIXES - [ibc] - - + - [nothing yet] - [ibc-relayer] - - + - [nothing yet] - [ibc-relayer-cli] - - + - [nothing yet] -### BREAKING CHANGES: +### BREAKING CHANGES - [ibc] - Implementation of the `ChanOpenAck`, `ChanOpenConfirm`, `ChanCloseInit`, and `ChanCloseConfirm` handlers ([#316]) + - Remove dependency on `tendermint-rpc` ([#624]) - [ibc-relayer] - - + - [nothing yet] - [ibc-relayer-cli] + - [nothing yet] +### BUG FIXES -### BUG FIXES: - -- `[ibc]` +- [ibc] - Fix panic in conn open try when no connection id is provided ([#626]) -- `[ibc-relayer-cli]` +- [ibc-relayer] + - [nothing yet] + +- [ibc-relayer-cli] - Fix wrong acks sent with `tx raw packet-ack` in a 3-chain setup ([#614]) [#316]: https://github.com/informalsystems/ibc-rs/issues/316 [#560]: https://github.com/informalsystems/ibc-rs/issues/560 [#614]: https://github.com/informalsystems/ibc-rs/issues/614 +[#624]: https://github.com/informalsystems/ibc-rs/issues/624 [#626]: https://github.com/informalsystems/ibc-rs/issues/626 ## v0.1.0 @@ -72,9 +75,9 @@ Noteworthy changes in this release include: -- The binary in the `ibc-relayer-cli` crate was given the name Hermes. +- The binary in the `ibc-relayer-cli` crate was given the name Hermes. - We published a comprehensive guide for Hermes at [hermes.informal.systems](https://hermes.informal.systems). -- Major improvements to user experience, in particular at CLI level: JSON output, +- Major improvements to user experience, in particular at CLI level: JSON output, configurable log output level, dedicated channel handshake command, as well as overall improvements to error display and output. @@ -183,7 +186,7 @@ the latest cosmos proto versions from `v0.40.0-rc5` (sometimes called 'stargate- - Update to cosmos-sdk IBC proto version `v0.40.0-rc5` ([#451]) - [ibc-relayer] - + - [ibc-relayer-cli] - Packet CLIs for recv_packet ([#443]) - Packet CLIs for acknowledging packets ([#468]) @@ -192,7 +195,7 @@ the latest cosmos proto versions from `v0.40.0-rc5` (sometimes called 'stargate- - [ibc-relayer] - Mock chain (implementing IBC handlers) and integration against CLI ([#158]) - Relayer tests for client update (ping pong) against MockChain ([#381]) - - Relayer refactor to improve testing and add semantic dependencies ([#447]) + - Relayer refactor to improve testing and add semantic dependencies ([#447]) [#158]: https://github.com/informalsystems/ibc-rs/issues/158 [#379]: https://github.com/informalsystems/ibc-rs/issues/379 @@ -208,7 +211,7 @@ the latest cosmos proto versions from `v0.40.0-rc5` (sometimes called 'stargate- This release focuses on implementing relayer and relayer-cli functionality towards a full v0 implementation. We now have the full-stack implementation for supporting client creation & updates, as well as connection- and channel handshakes. -We also consolidated our TLA+ specs into an "IBC Core TLA+ specification," and added ICS 020 spec. +We also consolidated our TLA+ specs into an "IBC Core TLA+ specification," and added ICS 020 spec. Special thanks to external contributors for this release: @CharlyCst ([#347], [#419]). @@ -317,7 +320,7 @@ Additional highlights: ### BREAKING CHANGES: - [ibc-relayer] & [ibc] Alignment with ecosystem updates: - - Compatibility with the latest protobuf (Gaia stargate-3 and stargate-4) ([#191], [#272], [#273], [#278]) + - Compatibility with the latest protobuf (Gaia stargate-3 and stargate-4) ([#191], [#272], [#273], [#278]) - Adaptations to tendermint 0.17 ([#286], [#293], [#300], [#302], [#308]) - [ibc-relayer] UX improvement: Remove proof option from client connections command ([#205]) @@ -336,7 +339,7 @@ Additional highlights: - [ibc] Implemented the `DomainType` trait for IBC proto structures ([#245], [#249]). - [ibc] & [ibc-proto] Several improvements to message processors, among which ([#218]): - ICS03 connection handshake protocol initial implementation and tests ([#160]) - - Add capability to decode from protobuf Any* type into Tendermint and Mock client states + - Add capability to decode from protobuf Any* type into Tendermint and Mock client states - Cleanup Any* client wrappers related code - Migrate handlers to newer protobuf definitions ([#226]) - Extend client context mock ([#221]) @@ -476,7 +479,7 @@ the latest state of development towards the Cosmos-SDK Stargate release. [\#174](https://github.com/informalsystems/ibc-rs/pull/174), [\#155](https://github.com/informalsystems/ibc-rs/pull/155) - [repo] Moved relayer/cli to relayer-cli, relayer/relay to relayer. [\#183](https://github.com/informalsystems/ibc-rs/pull/183) - + ### FEATURES: - [ibc-relayer] Query connections given client id. [\#169](https://github.com/informalsystems/ibc-rs/pull/169) @@ -486,20 +489,20 @@ the latest state of development towards the Cosmos-SDK Stargate release. ### IMPROVEMENTS: -- [ci] Framework (scripts and Github Actions) for integration testing the relayer queries against +- [ci] Framework (scripts and Github Actions) for integration testing the relayer queries against the Cosmos-SDK's `simd` binary with prepopulated IBC state in the genesis [\#140](https://github.com/informalsystems/ibc-rs/pull/140), [\#184](https://github.com/informalsystems/ibc-rs/pull/184) - [ibc-relayer|ibc] Implemented better Raw type handling. [\#156](https://github.com/informalsystems/ibc-rs/pull/156) - [repo] Add rust-toolchain file. [\#154](https://github.com/informalsystems/ibc-rs/pull/154) - + ### BUG FIXES: - [ibc] Fixed the identifiers limits according to updated ics spec. [\#189](https://github.com/informalsystems/ibc-rs/pull/189) - [ibc/relayer] Remove some warnings triggered during compilation due to dependency specification. [\#132](https://github.com/informalsystems/ibc-rs/pull/132) - [ibc] Fix nightly runs. [\#161](https://github.com/informalsystems/ibc-rs/pull/161) - [repo] Fix for incomplete licence terms. [\#153](https://github.com/informalsystems/ibc-rs/pull/153) - + ## 0.0.1 *July 1st, 2020* diff --git a/modules/Cargo.toml b/modules/Cargo.toml index 1a4a2c29b9..e30594b7d4 100644 --- a/modules/Cargo.toml +++ b/modules/Cargo.toml @@ -42,10 +42,6 @@ subtle-encoding = "0.5" [dependencies.tendermint] version = "=0.18.0" -[dependencies.tendermint-rpc] -version = "=0.18.0" -features = ["http-client", "websocket-client"] - [dependencies.tendermint-proto] version = "=0.18.0" @@ -55,4 +51,5 @@ optional = true [dev-dependencies] tokio = { version = "1.0", features = ["macros"] } +tendermint-rpc = { version = "=0.18.0", features = ["http-client", "websocket-client"] } tendermint-testgen = { version = "=0.18.0" } # Needed for generating (synthetic) light blocks. diff --git a/modules/src/application/ics20_fungible_token_transfer/events.rs b/modules/src/application/ics20_fungible_token_transfer/events.rs index 6198587c3b..0414686406 100644 --- a/modules/src/application/ics20_fungible_token_transfer/events.rs +++ b/modules/src/application/ics20_fungible_token_transfer/events.rs @@ -1,8 +1,7 @@ -use crate::events::{IBCEvent, RawObject}; -use crate::make_event; -use anomaly::BoxError; use serde_derive::{Deserialize, Serialize}; -use std::convert::TryFrom; + +use crate::events::IBCEvent; +use crate::make_event; // TODO - extract attributes make_event!(Timeout, "timeout"); diff --git a/modules/src/events.rs b/modules/src/events.rs index 15dc5937f6..0331528b23 100644 --- a/modules/src/events.rs +++ b/modules/src/events.rs @@ -1,3 +1,11 @@ +use std::collections::HashMap; +use std::convert::TryFrom; + +use anomaly::BoxError; +use serde_derive::{Deserialize, Serialize}; + +use tendermint::block::Height; + use crate::application::ics20_fungible_token_transfer::events as TransferEvents; use crate::ics02_client::events as ClientEvents; use crate::ics02_client::events::NewBlock; @@ -5,16 +13,6 @@ use crate::ics03_connection::events as ConnectionEvents; use crate::ics04_channel::events as ChannelEvents; use crate::Height as ICSHeight; -use tendermint_rpc::event::{Event as RpcEvent, EventData as RpcEventData}; - -use anomaly::BoxError; -use serde_derive::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::convert::{TryFrom, TryInto}; -use tendermint::block::Height; - -use tracing::warn; - /// Events types #[derive(Debug, Clone, Deserialize, Serialize)] pub enum IBCEventType { @@ -166,160 +164,18 @@ pub fn extract_events( Err("Incorrect Event Type".into()) } -//Takes events in the form -// "events":{ -// "connection_open_init.client_id":["testclient","testclientsec"], -// "connection_open_init.connection_id":["ancaconnonetest","ancaconnonetestsec"], -// "connection_open_init.counterparty_client_id":["testclientsec","testclientsecsec"], -// "create_client.client_id":["testclientthird"], -// "create_client.client_type":["tendermint"], -// "message.action":["connection_open_init","create_client","connection_open_init"], -// "message.module":["ibc_connection","ibc_client",ibc_connection"], -// "message.sender":["cosmos187xxg4yfkypl05cqylucezpjvycj24nurvm8p9","cosmos187xxg4yfkypl05cqylucezpjvycj24nurvm8p9","cosmos187xxg4yfkypl05cqylucezpjvycj24nurvm8p9","cosmos187xxg4yfkypl05cqylucezpjvycj24nurvm8p9"],"tm.event":["Tx"],"transfer.amount":["5000stake"],"transfer.recipient":["cosmos17xpfvakm2amg962yls6f84z3kell8c5lserqta"],"tx.hash":["A9E18AE3909F22232F8DBDB1C48F2FECB260A308A2D157E8832E901D45950605"], -// "tx.height":["35"] -// and returns: -// [{"connection_open_init", 0}, {"create_client", 0}, {"connection_open_init", 1}] -// where the number in each entry is the index in the matching events that should be used to build the event -// e.g. for the last "connection_open_init" in the result -// "connection_open_init.client_id" -> "testclientsec" -// "connection_open_init.connection_id" -> "ancaconnonetestsec", -// "connection_open_init.counterparty_client_id" -> "testclientsec","testclientsecsec", -fn extract_helper(events: &HashMap>) -> Result, String> { - let message_action = events.get("message.action").ok_or("Incorrect Event Type")?; - let mut val_indeces = HashMap::new(); - let mut result = Vec::new(); - - for action_string in message_action { - let idx = val_indeces - .entry(action_string.clone()) - .or_insert_with(|| 0); - result.push((action_string.clone(), *idx)); - *val_indeces.get_mut(action_string.as_str()).unwrap() += 1; - } - Ok(result) -} - -pub fn get_all_events(result: RpcEvent) -> Result, String> { - let mut vals: Vec<(Height, IBCEvent)> = vec![]; - - match &result.data { - RpcEventData::NewBlock { block, .. } => { - let block = block.as_ref().ok_or("missing block")?; - vals.push(( - block.header.height, - NewBlock::new(block.header.height).into(), - )); - } - - RpcEventData::Tx { .. } => { - let events = &result.events.ok_or("missing events")?; - let height_raw = events.get("tx.height").ok_or("tx.height")?[0] - .parse::() - .map_err(|e| e.to_string())?; - let height: Height = height_raw - .try_into() - .map_err(|_| "height parsing overflow")?; - - let actions_and_indices = extract_helper(&events)?; - for action in actions_and_indices { - match build_event(RawObject::new( - height, - action.0, - action.1 as usize, - events.clone(), - )) { - Ok(event) => vals.push((height, event)), - Err(e) => warn!("error while building event {}", e.to_string()), - } - } - } - _ => {} - } - - Ok(vals) -} - -pub fn build_event(mut object: RawObject) -> Result { - match object.action.as_str() { - // Client events - "create_client" => Ok(IBCEvent::from(ClientEvents::CreateClient::try_from( - object, - )?)), - "update_client" => Ok(IBCEvent::from(ClientEvents::UpdateClient::try_from( - object, - )?)), - - // Connection events - "connection_open_init" => Ok(IBCEvent::from(ConnectionEvents::OpenInit::try_from( - object, - )?)), - "connection_open_try" => Ok(IBCEvent::from(ConnectionEvents::OpenTry::try_from(object)?)), - "connection_open_ack" => Ok(IBCEvent::from(ConnectionEvents::OpenAck::try_from(object)?)), - "connection_open_confirm" => Ok(IBCEvent::from(ConnectionEvents::OpenConfirm::try_from( - object, - )?)), - - // Channel events - "channel_open_init" => Ok(IBCEvent::from(ChannelEvents::OpenInit::try_from(object)?)), - "channel_open_try" => Ok(IBCEvent::from(ChannelEvents::OpenTry::try_from(object)?)), - "channel_open_ack" => Ok(IBCEvent::from(ChannelEvents::OpenAck::try_from(object)?)), - "channel_open_confirm" => Ok(IBCEvent::from(ChannelEvents::OpenConfirm::try_from( - object, - )?)), - "channel_close_init" => Ok(IBCEvent::from(ChannelEvents::CloseInit::try_from(object)?)), - "channel_close_confirm" => Ok(IBCEvent::from(ChannelEvents::CloseConfirm::try_from( - object, - )?)), - - // Packet events - // Note: There is no message.action "send_packet", the only one we can hook into is the - // module's action, "transfer" being the only one in IBC1.0. However the attributes - // are all prefixed with "send_packet" therefore the overwrite here - // TODO: This need to be sorted out - "transfer" => { - object.action = "send_packet".to_string(); - Ok(IBCEvent::from(ChannelEvents::SendPacket::try_from(object)?)) - } - // Same here - // TODO: sort this out - "recv_packet" => { - object.action = "write_acknowledgement".to_string(); - Ok(IBCEvent::from( - ChannelEvents::WriteAcknowledgement::try_from(object)?, - )) - } - "write_acknowledgement" => Ok(IBCEvent::from( - ChannelEvents::WriteAcknowledgement::try_from(object)?, - )), - "acknowledge_packet" => Ok(IBCEvent::from(ChannelEvents::AcknowledgePacket::try_from( - object, - )?)), - "timeout_packet" => Ok(IBCEvent::from(ChannelEvents::TimeoutPacket::try_from( - object, - )?)), - - "timeout_on_close_packet" => { - object.action = "timeout_packet".to_string(); - Ok(IBCEvent::from( - ChannelEvents::TimeoutOnClosePacket::try_from(object)?, - )) - } - - unknown_event => Err(unknown_event.into()), - } -} - #[macro_export] macro_rules! make_event { ($a:ident, $b:literal) => { #[derive(Debug, Deserialize, Serialize, Clone)] pub struct $a { - pub data: std::collections::HashMap>, + pub data: ::std::collections::HashMap>, } - impl TryFrom for $a { - type Error = BoxError; - fn try_from(result: RawObject) -> Result { - match crate::events::extract_events(&result.events, $b) { + impl ::std::convert::TryFrom<$crate::events::RawObject> for $a { + type Error = ::anomaly::BoxError; + + fn try_from(result: $crate::events::RawObject) -> Result { + match $crate::events::extract_events(&result.events, $b) { Ok(()) => Ok($a { data: result.events.clone(), }), diff --git a/relayer/src/event.rs b/relayer/src/event.rs index f9f9338640..c5f87abc85 100644 --- a/relayer/src/event.rs +++ b/relayer/src/event.rs @@ -1,2 +1,3 @@ pub mod bus; pub mod monitor; +pub mod rpc; diff --git a/relayer/src/event/monitor.rs b/relayer/src/event/monitor.rs index f8999452aa..057af675b1 100644 --- a/relayer/src/event/monitor.rs +++ b/relayer/src/event/monitor.rs @@ -169,7 +169,7 @@ impl EventMonitor { let event = self.rt.block_on(self.subscriptions.next()); match event { - Some(Ok(event)) => match ibc::events::get_all_events(event.clone()) { + Some(Ok(event)) => match crate::event::rpc::get_all_events(event.clone()) { Ok(ibc_events) => { let events_by_height = ibc_events.into_iter().into_group_map(); diff --git a/relayer/src/event/rpc.rs b/relayer/src/event/rpc.rs new file mode 100644 index 0000000000..2bc5a923a6 --- /dev/null +++ b/relayer/src/event/rpc.rs @@ -0,0 +1,214 @@ +use std::{ + collections::HashMap, + convert::{TryFrom, TryInto}, +}; + +use anomaly::BoxError; +use tracing::warn; + +use tendermint::block::Height; +use tendermint_rpc::event::{Event as RpcEvent, EventData as RpcEventData}; + +use ibc::{ + events::{IBCEvent, RawObject}, + ics02_client::events as ClientEvents, + ics03_connection::events as ConnectionEvents, + ics04_channel::events as ChannelEvents, +}; + +pub fn get_all_events(result: RpcEvent) -> Result, String> { + let mut vals: Vec<(Height, IBCEvent)> = vec![]; + + match &result.data { + RpcEventData::NewBlock { block, .. } => { + let block = block.as_ref().ok_or("missing block")?; + vals.push(( + block.header.height, + ClientEvents::NewBlock::new(block.header.height).into(), + )); + } + + RpcEventData::Tx { .. } => { + let events = &result.events.ok_or("missing events")?; + let height_raw = events.get("tx.height").ok_or("tx.height")?[0] + .parse::() + .map_err(|e| e.to_string())?; + + let height: Height = height_raw + .try_into() + .map_err(|_| "height parsing overflow")?; + + let actions_and_indices = extract_helper(&events)?; + for action in actions_and_indices { + match build_event(RawObject::new( + height, + action.0, + action.1 as usize, + events.clone(), + )) { + Ok(event) => vals.push((height, event)), + Err(e) => warn!("error while building event {}", e.to_string()), + } + } + } + _ => {} + } + + Ok(vals) +} + +pub fn build_event(mut object: RawObject) -> Result { + match object.action.as_str() { + // Client events + "create_client" => Ok(IBCEvent::from(ClientEvents::CreateClient::try_from( + object, + )?)), + "update_client" => Ok(IBCEvent::from(ClientEvents::UpdateClient::try_from( + object, + )?)), + + // Connection events + "connection_open_init" => Ok(IBCEvent::from(ConnectionEvents::OpenInit::try_from( + object, + )?)), + "connection_open_try" => Ok(IBCEvent::from(ConnectionEvents::OpenTry::try_from(object)?)), + "connection_open_ack" => Ok(IBCEvent::from(ConnectionEvents::OpenAck::try_from(object)?)), + "connection_open_confirm" => Ok(IBCEvent::from(ConnectionEvents::OpenConfirm::try_from( + object, + )?)), + + // Channel events + "channel_open_init" => Ok(IBCEvent::from(ChannelEvents::OpenInit::try_from(object)?)), + "channel_open_try" => Ok(IBCEvent::from(ChannelEvents::OpenTry::try_from(object)?)), + "channel_open_ack" => Ok(IBCEvent::from(ChannelEvents::OpenAck::try_from(object)?)), + "channel_open_confirm" => Ok(IBCEvent::from(ChannelEvents::OpenConfirm::try_from( + object, + )?)), + "channel_close_init" => Ok(IBCEvent::from(ChannelEvents::CloseInit::try_from(object)?)), + "channel_close_confirm" => Ok(IBCEvent::from(ChannelEvents::CloseConfirm::try_from( + object, + )?)), + + // Packet events + // Note: There is no message.action "send_packet", the only one we can hook into is the + // module's action, "transfer" being the only one in IBC1.0. However the attributes + // are all prefixed with "send_packet" therefore the overwrite here + // TODO: This need to be sorted out + "transfer" => { + object.action = "send_packet".to_string(); + Ok(IBCEvent::from(ChannelEvents::SendPacket::try_from(object)?)) + } + // Same here + // TODO: sort this out + "recv_packet" => { + object.action = "write_acknowledgement".to_string(); + Ok(IBCEvent::from( + ChannelEvents::WriteAcknowledgement::try_from(object)?, + )) + } + "write_acknowledgement" => Ok(IBCEvent::from( + ChannelEvents::WriteAcknowledgement::try_from(object)?, + )), + "acknowledge_packet" => Ok(IBCEvent::from(ChannelEvents::AcknowledgePacket::try_from( + object, + )?)), + "timeout_packet" => Ok(IBCEvent::from(ChannelEvents::TimeoutPacket::try_from( + object, + )?)), + + _ => Err("Incorrect Event Type".into()), + } +} + +/// Takes events in the form +/// +/// ```json +/// { +/// "events": { +/// "connection_open_init.client_id": [ +/// "testclient", +/// "testclientsec" +/// ], +/// "connection_open_init.connection_id": [ +/// "ancaconnonetest", +/// "ancaconnonetestsec" +/// ], +/// "connection_open_init.counterparty_client_id": [ +/// "testclientsec", +/// "testclientsecsec" +/// ], +/// "create_client.client_id": [ +/// "testclientthird" +/// ], +/// "create_client.client_type": [ +/// "tendermint" +/// ], +/// "message.action": [ +/// "connection_open_init", +/// "create_client", +/// "connection_open_init" +/// ], +/// "message.module": [ +/// "ibc_connection", +/// "ibc_client", +/// "ibc_connection" +/// ], +/// "message.sender": [ +/// "cosmos187xxg4yfkypl05cqylucezpjvycj24nurvm8p9", +/// "cosmos187xxg4yfkypl05cqylucezpjvycj24nurvm8p9", +/// "cosmos187xxg4yfkypl05cqylucezpjvycj24nurvm8p9", +/// "cosmos187xxg4yfkypl05cqylucezpjvycj24nurvm8p9" +/// ], +/// "tm.event": [ +/// "Tx" +/// ], +/// "transfer.amount": [ +/// "5000stake" +/// ], +/// "transfer.recipient": [ +/// "cosmos17xpfvakm2amg962yls6f84z3kell8c5lserqta" +/// ], +/// "tx.hash": [ +/// "A9E18AE3909F22232F8DBDB1C48F2FECB260A308A2D157E8832E901D45950605" +/// ], +/// "tx.height": [ +/// "35" +/// ] +/// } +/// } +/// ``` +/// +/// and returns: +/// +/// ```rust +/// vec![ +/// ("connection_open_init", 0), +/// ("create_client", 0), +/// ("connection_open_init", 1), +/// ]; +/// ``` +/// +/// where the number in each entry is the index in the matching events that should be used to build the event. +/// +/// e.g. for the last "connection_open_init" in the result +/// +/// ```text +/// "connection_open_init.client_id" -> "testclientsec" +/// "connection_open_init.connection_id" -> "ancaconnonetestsec", +/// "connection_open_init.counterparty_client_id" -> "testclientsec", "testclientsecsec", +/// ``` +fn extract_helper(events: &HashMap>) -> Result, String> { + let actions = events.get("message.action").ok_or("Incorrect Event Type")?; + + let mut val_indices = HashMap::new(); + let mut result = Vec::with_capacity(actions.len()); + + for action in actions { + let idx = val_indices.entry(action.clone()).or_insert_with(|| 0); + result.push((action.clone(), *idx)); + + *val_indices.get_mut(action.as_str()).unwrap() += 1; + } + + Ok(result) +}