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

ADR 003: Handler implementation #197

Merged
merged 29 commits into from
Aug 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2f51964
Add doc comments and error types to ICS 02 module
adizere Aug 3, 2020
b1d0091
Rename ics02_client::client module to ics02_client::raw
romac Aug 4, 2020
0f93646
Replace message traits with structs
romac Aug 3, 2020
c5fce26
Fix formatting error
romac Aug 5, 2020
64696e0
Add implementation specific error kind
romac Aug 5, 2020
5a045a6
Fixup client message structs definitions
romac Aug 5, 2020
6949ed6
Fix clippy warnings
romac Aug 5, 2020
414cfa9
Add basic handler definitions
romac Aug 5, 2020
605e62a
Add initial implementation of CreateClient handler
romac Aug 5, 2020
46cf57a
Add implementation of ClientDef for Tendermint
romac Aug 7, 2020
2014925
Re-use existing traits
romac Aug 7, 2020
d80274a
Add valid test for create_client handler
romac Aug 7, 2020
302b083
Add tests for case where client already exists
romac Aug 7, 2020
e335fa2
WIP: Update client handler
romac Aug 7, 2020
fb972a9
Add initial proposal for ADR 003
romac Aug 7, 2020
add693e
Rename file to adr-003-handler-implementation.md
romac Aug 10, 2020
9939b39
Replace "handler" with "message processor" in plain text
romac Aug 10, 2020
5b85658
Formatting
romac Aug 11, 2020
6b86998
Fix create client handler tests
romac Aug 11, 2020
b1c2f2d
Rename module handler to dispatch
romac Aug 11, 2020
375d61e
Formatting
romac Aug 11, 2020
8551bbc
Add sketch of update client processor
romac Aug 13, 2020
4a529c1
Move mocks into their own module
romac Aug 13, 2020
05d3dff
Remove genericity over client def in client submodule
romac Aug 13, 2020
9d23ef3
Lift chain specific data into an enum
romac Aug 14, 2020
56d0464
Update ADR to match new handling of chain-specific data
romac Aug 14, 2020
458d64b
Fix the connection specifics in ADR
ancazamfir Aug 17, 2020
b571616
Added Tendermint to the new "Any*" enums
ancazamfir Aug 19, 2020
08df7d3
Merge branch 'master' into romain/ics02-client-handlers
ancazamfir Aug 19, 2020
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
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