Skip to content

Commit

Permalink
ADR 003: Handler implementation (#197)
Browse files Browse the repository at this point in the history
* Add doc comments and error types to ICS 02 module

* Rename ics02_client::client module to ics02_client::raw

* Replace message traits with structs

* Fix formatting error

* Add implementation specific error kind

* Fixup client message structs definitions

* Fix clippy warnings

* Add basic handler definitions

* Add initial implementation of CreateClient handler

* Add implementation of ClientDef for Tendermint

* Re-use existing traits

* Add valid test for create_client handler

* Add tests for case where client already exists

* WIP: Update client handler

* Add initial proposal for ADR 003

* Rename file to adr-003-handler-implementation.md

* Replace "handler" with "message processor" in plain text

* Formatting

* Fix create client handler tests

* Rename module handler to dispatch

* Formatting

* Add sketch of update client processor

* Move mocks into their own module

* Remove genericity over client def in client submodule

* Lift chain specific data into an enum

* Update ADR to match new handling of chain-specific data

* Fix the connection specifics in ADR

* Added Tendermint to the new "Any*" enums

Added client state to MsgCreateAnyClient, result, keeper, etc

Test for concrete tendermint MsgCreateClient

Updated TM client and consensus states, MsgCreateClient

Extract client and consensus states from MsgCreateClient for TM

Extract consensus state from MsgUpdateClient for TM

Co-authored-by: Adi Seredinschi <[email protected]>
Co-authored-by: Anca Zamfir <[email protected]>
  • Loading branch information
3 people authored Aug 19, 2020
1 parent cd6c0a0 commit 788c36b
Show file tree
Hide file tree
Showing 33 changed files with 1,744 additions and 168 deletions.
679 changes: 679 additions & 0 deletions docs/architecture/adr-003-handler-implementation.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions modules/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ serde_json = "1"
tracing = "0.1.13"
prost = "0.6.1"
bytes = "0.5.5"
dyn-clonable = "0.9.0"

[dev-dependencies]
tokio = { version = "0.2", features = ["macros"] }
Expand Down
95 changes: 95 additions & 0 deletions modules/src/handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use std::marker::PhantomData;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Attribute {
key: String,
value: String,
}

impl Attribute {
pub fn new(key: String, value: String) -> Self {
Self { key, value }
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EventType {
Message,
Custom(String),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Event {
tpe: EventType,
attributes: Vec<Attribute>,
}

impl Event {
pub fn new(tpe: EventType, attrs: Vec<(String, String)>) -> Self {
Self {
tpe,
attributes: attrs
.into_iter()
.map(|(k, v)| Attribute::new(k, v))
.collect(),
}
}
}

pub type HandlerResult<T, E> = Result<HandlerOutput<T>, E>;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HandlerOutput<T> {
pub result: T,
pub log: Vec<String>,
pub events: Vec<Event>,
}

impl<T> HandlerOutput<T> {
pub fn builder() -> HandlerOutputBuilder<T> {
HandlerOutputBuilder::new()
}
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct HandlerOutputBuilder<T> {
log: Vec<String>,
events: Vec<Event>,
marker: PhantomData<T>,
}

impl<T> HandlerOutputBuilder<T> {
pub fn new() -> Self {
Self {
log: vec![],
events: vec![],
marker: PhantomData,
}
}

pub fn with_log(mut self, log: impl Into<Vec<String>>) -> Self {
self.log.append(&mut log.into());
self
}

pub fn log(&mut self, log: impl Into<String>) {
self.log.push(log.into());
}

pub fn with_events(mut self, events: impl Into<Vec<Event>>) -> Self {
self.events.append(&mut events.into());
self
}

pub fn emit(&mut self, event: impl Into<Event>) {
self.events.push(event.into());
}

pub fn with_result(self, result: T) -> HandlerOutput<T> {
HandlerOutput {
result,
log: self.log,
events: self.events,
}
}
}
110 changes: 110 additions & 0 deletions modules/src/ics02_client/client_def.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use serde_derive::{Deserialize, Serialize};

use crate::ics02_client::client_type::ClientType;
use crate::ics02_client::header::Header;
use crate::ics02_client::state::{ClientState, ConsensusState};
use crate::ics23_commitment::CommitmentRoot;
use crate::Height;

use crate::ics02_client::mocks;
use crate::ics07_tendermint as tendermint;
use crate::ics07_tendermint::client_def::TendermintClient;

pub trait ClientDef: Clone {
type Header: Header;
type ClientState: ClientState;
type ConsensusState: ConsensusState;
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] // TODO: Add Eq
#[allow(clippy::large_enum_variant)]
pub enum AnyHeader {
Mock(mocks::MockHeader),
Tendermint(tendermint::header::Header),
}

impl Header for AnyHeader {
fn client_type(&self) -> ClientType {
match self {
Self::Mock(header) => header.client_type(),
Self::Tendermint(header) => header.client_type(),
}
}

fn height(&self) -> Height {
match self {
Self::Mock(header) => header.height(),
Self::Tendermint(header) => header.height(),
}
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum AnyClientState {
Mock(mocks::MockClientState),
Tendermint(crate::ics07_tendermint::client_state::ClientState),
}

impl ClientState for AnyClientState {
fn chain_id(&self) -> String {
todo!()
}

fn client_type(&self) -> ClientType {
todo!()
}

fn get_latest_height(&self) -> Height {
match self {
AnyClientState::Tendermint(tm_state) => tm_state.get_latest_height(),
AnyClientState::Mock(mock_state) => mock_state.get_latest_height(),
}
}

fn is_frozen(&self) -> bool {
todo!()
}

fn verify_client_consensus_state(
&self,
_root: &CommitmentRoot,
) -> Result<(), Box<dyn std::error::Error>> {
todo!()
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum AnyConsensusState {
Mock(mocks::MockConsensusState),
Tendermint(crate::ics07_tendermint::consensus_state::ConsensusState),
}

impl ConsensusState for AnyConsensusState {
fn client_type(&self) -> ClientType {
todo!()
}

fn height(&self) -> Height {
todo!()
}

fn root(&self) -> &CommitmentRoot {
todo!()
}

fn validate_basic(&self) -> Result<(), Box<dyn std::error::Error>> {
todo!()
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AnyClient {
Mock(mocks::MockClient),
Tendermint(TendermintClient),
}

impl ClientDef for AnyClient {
type Header = AnyHeader;
type ClientState = AnyClientState;
type ConsensusState = AnyConsensusState;
}
6 changes: 3 additions & 3 deletions modules/src/ics02_client/client_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ mod tests {
let client_type = ClientType::from_str("tendermint");

match client_type {
Ok(ClientType::Tendermint) => assert!(true),
Err(_) => assert!(false, "parse failed"),
Ok(ClientType::Tendermint) => (),
_ => panic!("parse failed"),
}
}

Expand All @@ -52,7 +52,7 @@ mod tests {
format!("{}", err),
"unknown client type: some-random-client-type"
),
_ => assert!(false, "parse didn't fail"),
_ => panic!("parse didn't fail"),
}
}
}
17 changes: 16 additions & 1 deletion modules/src/ics02_client/error.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
use anomaly::{BoxError, Context};
use thiserror::Error;

use crate::ics24_host::identifier::ClientId;
use crate::Height;

pub type Error = anomaly::Error<Kind>;

#[derive(Clone, Debug, Error)]
#[derive(Clone, Debug, Error, PartialEq, Eq)]
pub enum Kind {
#[error("unknown client type")]
UnknownClientType,

#[error("client already exists: {0}")]
ClientAlreadyExists(ClientId),

#[error("client not found: {0}")]
ClientNotFound(ClientId),

#[error("consensus state not found at: {0} at height {1}")]
ConsensusStateNotFound(ClientId, Height),

#[error("implementation specific")]
ImplementationSpecific,
}

impl Kind {
Expand Down
5 changes: 5 additions & 0 deletions modules/src/ics02_client/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use tendermint::block;

/// NewBlock event signals the committing & execution of a new block.
// TODO - find a better place for NewBlock
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct NewBlock {
Expand All @@ -27,6 +28,7 @@ impl From<NewBlock> for IBCEvent {
}
}

/// CreateClient event signals the creation of a new on-chain client (IBC client).
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct CreateClient {
pub height: block::Height,
Expand All @@ -51,6 +53,7 @@ impl From<CreateClient> for IBCEvent {
}
}

/// UpdateClient event signals a recent update of an on-chain client (IBC Client).
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct UpdateClient {
pub height: block::Height,
Expand All @@ -75,6 +78,8 @@ impl From<UpdateClient> for IBCEvent {
}
}

/// ClientMisbehavior event signals the update of an on-chain client (IBC Client) with evidence of
/// misbehavior.
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ClientMisbehavior {
pub height: block::Height,
Expand Down
102 changes: 102 additions & 0 deletions modules/src/ics02_client/handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#![allow(unused_imports)]

use crate::handler::{Event, EventType, HandlerOutput};
use crate::ics02_client::client_def::{AnyClient, AnyClientState, AnyConsensusState, ClientDef};
use crate::ics02_client::client_type::ClientType;
use crate::ics02_client::error::Error;
use crate::ics02_client::msgs::{MsgCreateAnyClient, MsgUpdateAnyClient};
use crate::ics02_client::state::{ClientState, ConsensusState};
use crate::ics24_host::identifier::ClientId;

use crate::Height;

pub mod create_client;
pub mod update_client;

pub trait ClientReader {
fn client_type(&self, client_id: &ClientId) -> Option<ClientType>;
fn client_state(&self, client_id: &ClientId) -> Option<AnyClientState>;
fn consensus_state(&self, client_id: &ClientId, height: Height) -> Option<AnyConsensusState>;
}

pub trait ClientKeeper {
fn store_client_type(
&mut self,
client_id: ClientId,
client_type: ClientType,
) -> Result<(), Error>;

fn store_client_state(
&mut self,
client_id: ClientId,
client_state: AnyClientState,
) -> Result<(), Error>;

fn store_consensus_state(
&mut self,
client_id: ClientId,
consensus_state: AnyConsensusState,
) -> Result<(), Error>;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ClientEvent {
ClientCreated(ClientId),
ClientUpdated(ClientId),
}

impl From<ClientEvent> for Event {
fn from(ce: ClientEvent) -> Event {
match ce {
ClientEvent::ClientCreated(client_id) => Event::new(
EventType::Custom("ClientCreated".to_string()),
vec![("client_id".to_string(), client_id.to_string())],
),
ClientEvent::ClientUpdated(client_id) => Event::new(
EventType::Custom("ClientUpdated".to_string()),
vec![("client_id".to_string(), client_id.to_string())],
),
}
}
}

pub enum ClientMsg<CD: ClientDef> {
CreateClient(MsgCreateAnyClient<CD>),
UpdateClient(MsgUpdateAnyClient<CD>),
}

pub fn dispatch<Ctx>(ctx: &mut Ctx, msg: ClientMsg<AnyClient>) -> Result<HandlerOutput<()>, Error>
where
Ctx: ClientReader + ClientKeeper,
{
match msg {
ClientMsg::CreateClient(msg) => {
let HandlerOutput {
result,
log,
events,
} = create_client::process(ctx, msg)?;

create_client::keep(ctx, result)?;

Ok(HandlerOutput::builder()
.with_log(log)
.with_events(events)
.with_result(()))
}
ClientMsg::UpdateClient(msg) => {
let HandlerOutput {
result,
log,
events,
} = update_client::process(ctx, msg)?;

update_client::keep(ctx, result)?;

Ok(HandlerOutput::builder()
.with_log(log)
.with_events(events)
.with_result(()))
}
}
}
Loading

0 comments on commit 788c36b

Please sign in to comment.