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

ICS3 connection handshake protocol (message processing logic) #188

Merged
merged 39 commits into from
Sep 3, 2020
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9f42758
First processing logic for ICS 03 (#160).
adizere Jul 29, 2020
cb49076
Merge branch 'master' into adi/ics03_core
adizere Jul 29, 2020
d45dbeb
Merge branch 'master' into adi/ics03_core
adizere Jul 30, 2020
faeebcd
Fix for temporary pick_version().
adizere Jul 30, 2020
7c66d9f
First attempt at the process_try_msg function.
adizere Jul 30, 2020
c799254
Sketch for proof verification in process_try_msg. Removed Connection*…
adizere Jul 30, 2020
84d26bc
Added logic for the Try message.
adizere Jul 31, 2020
645659f
Logic for processing ICS3 confirm msg. Cosmetic improvements
adizere Jul 31, 2020
eb7d56f
Adapting code to new architecture based on contexts.
adizere Jul 31, 2020
8f9e4d4
Merge branch 'master' into adi/ics03_core
adizere Jul 31, 2020
3c5fa93
Refactored, new terminology, added events.
adizere Aug 3, 2020
b03fbd3
WIP: Adding tests
adizere Aug 3, 2020
9a4bf0f
Higher-level interface for the context.
adizere Aug 5, 2020
c345037
One chunk of proof verification is done
adizere Aug 5, 2020
1c5bf1d
Implementation of ics3 verify_proofs done.
adizere Aug 6, 2020
e0bd419
Addressing Romain's concerns.
adizere Aug 6, 2020
7b927f1
Merge branch 'master' into adi/ics03_core
adizere Aug 7, 2020
f1b3d26
Tests for conn open init message processing.
adizere Aug 7, 2020
c63616b
Fixes for integration tests: PartialEq for identifiers.
adizere Aug 7, 2020
118746f
Merge branch 'master' into adi/ics03_core
ancazamfir Aug 22, 2020
06140d0
More changes cf adr003 and code reorg
ancazamfir Aug 22, 2020
e72333a
Move info to new context and context_mock modules
ancazamfir Aug 23, 2020
ee621a5
migrate conn-open-init to new infra
ancazamfir Aug 24, 2020
18eaffa
Refine client and connection mock context
ancazamfir Aug 24, 2020
9e29bd9
Fix conn_open_try handler
ancazamfir Aug 24, 2020
cfba83b
Create new mock_client
ancazamfir Aug 24, 2020
df6c2b4
remove mocks.rs
ancazamfir Aug 24, 2020
6b56737
Add global chain context
ancazamfir Aug 26, 2020
17deff1
Uncomment tests
ancazamfir Aug 26, 2020
a039783
Cleanup
ancazamfir Aug 27, 2020
d68daed
Started to fill client verification functions.
ancazamfir Aug 31, 2020
f563534
Add try_from for connection messages and remove new()
ancazamfir Sep 2, 2020
98738cd
Add test for try dispatch
ancazamfir Sep 2, 2020
a4b0068
Some changes to mock context setup
ancazamfir Sep 2, 2020
c0fbbc8
Some cleanup
ancazamfir Sep 2, 2020
a01c667
Review comments
ancazamfir Sep 3, 2020
ee1fd7e
Mark mocks as `#[cfg(test)]
romac Sep 3, 2020
fd55dee
Fix clippy warnings
romac Sep 3, 2020
0fc9e3a
Remove use of clone in a Debug impl
romac Sep 3, 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
1 change: 1 addition & 0 deletions modules/src/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub struct AccAddress(Vec<u8>;
adizere marked this conversation as resolved.
Show resolved Hide resolved
30 changes: 30 additions & 0 deletions modules/src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use crate::ics02_client::client_def::AnyConsensusState;
use crate::mock_client::header::MockHeader;
use crate::mock_client::state::MockConsensusState;
use serde_derive::{Deserialize, Serialize};
use tendermint::block::Height;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SelfHeader {
ancazamfir marked this conversation as resolved.
Show resolved Hide resolved
Mock(MockHeader),
//Tendermint(tendermint::header::Header),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct HistoricalInfo {
pub header: SelfHeader,
}

impl From<MockHeader> for AnyConsensusState {
fn from(h: MockHeader) -> Self {
AnyConsensusState::Mock(MockConsensusState(h))
}
}

pub trait ChainReader {
fn self_historical_info(&self, height: Height) -> Option<&HistoricalInfo>;
}

pub trait ChainKeeper {
fn store_historical_info(&mut self, height: Height, info: HistoricalInfo);
}
142 changes: 142 additions & 0 deletions modules/src/context_mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use crate::context::{ChainKeeper, ChainReader, HistoricalInfo, SelfHeader};
use crate::mock_client::header::MockHeader;
use serde_derive::{Deserialize, Serialize};
use std::error::Error;
use tendermint::block::Height;

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct MockChainContext {
pub max_size: usize,
pub latest: Height,
pub history: Vec<HistoricalInfo>,
}

impl MockChainContext {
pub fn new(max_size: usize, n: Height) -> Self {
Self {
max_size,
latest: n,
history: (0..n.value())
.map(|i| HistoricalInfo {
header: SelfHeader::Mock(MockHeader(Height(i).increment())),
})
.collect(),
}
}

pub fn max_size(&self) -> usize {
self.max_size
}

/// Used for testing
romac marked this conversation as resolved.
Show resolved Hide resolved
pub fn populate(&mut self, hs: Vec<u64>) {
for h in hs {
self.store_historical_info(
Height(h),
HistoricalInfo {
header: SelfHeader::Mock(MockHeader(Height(h))),
},
);
}
}

/// Used for testing
pub fn validate(&self) -> Result<(), Box<dyn Error>> {
// check that the number of entries is not higher than max_size
if self.history.len() > self.max_size {
return Err("too many entries".to_string().into());
}

// check latest is properly updated with highest header height
let SelfHeader::Mock(lh) = self.history[self.history.len() - 1].header;
if lh.height() != self.latest {
return Err("latest height is not updated".to_string().into());
}

// check that all headers are in sequential order
for i in 1..self.history.len() {
let SelfHeader::Mock(ph) = self.history[i - 1].header;
let SelfHeader::Mock(h) = self.history[i].header;
if ph.height().increment() != h.height() {
return Err("headers in history not sequential".to_string().into());
}
}
Ok(())
}
}

impl ChainReader for MockChainContext {
fn self_historical_info(&self, height: Height) -> Option<&HistoricalInfo> {
let l = height.value() as usize;
let h = self.latest.value() as usize;

if l <= h - self.max_size {
// header with height not in the history
None
} else {
Some(&self.history[h - l])
}
}
}

impl ChainKeeper for MockChainContext {
fn store_historical_info(&mut self, height: Height, info: HistoricalInfo) {
if height != self.latest.increment() {
return;
}
let mut history = self.history.clone();
if history.len() >= self.max_size {
history.rotate_left(1);
history[self.max_size - 1] = info;
} else {
history.push(info);
}
//history.insert(height, info);
self.history = history;
self.latest = height;
}
}

#[cfg(test)]
mod tests {
use crate::context_mock::MockChainContext;
use tendermint::block::Height;

#[test]
fn test_store_historical_info() {
pub struct Test {
name: String,
ctx: MockChainContext,
args: Vec<u64>,
}

impl Test {
pub fn apply(&mut self, hs: Vec<u64>) {
self.ctx.populate(hs);
}
}

let tests: Vec<Test> = vec![
Test {
name: "Add no prune".to_string(),
ctx: MockChainContext::new(3, Height(0)),
args: [1].to_vec(),
},
Test {
name: "Add with prune".to_string(),
ctx: MockChainContext::new(3, Height(2)),
args: [3, 4].to_vec(),
},
Test {
name: "Attempt to add non sequential headers".to_string(),
ctx: MockChainContext::new(3, Height(2)),
args: [3, 5, 7].to_vec(),
},
];

for mut test in tests {
test.apply(test.args.clone());
assert!(test.ctx.validate().is_ok());
}
}
}
1 change: 1 addition & 0 deletions modules/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use tendermint::block;

use tendermint_rpc::event_listener::{ResultEvent, TMEventData};

/// Events created by the IBC component of a chain, destined for a relayer.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum IBCEvent {
NewBlock(NewBlock),
Expand Down
4 changes: 2 additions & 2 deletions modules/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub enum EventType {

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

impl Event {
Expand Down
114 changes: 102 additions & 12 deletions modules/src/ics02_client/client_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ 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::ics23_commitment::commitment::{CommitmentPrefix, CommitmentProof, CommitmentRoot};
use crate::Height;

use crate::ics02_client::mocks;
use crate::ics03_connection::connection::ConnectionEnd;
use crate::ics07_tendermint as tendermint;
use crate::ics07_tendermint::client_def::TendermintClient;
use crate::ics24_host::identifier::{ClientId, ConnectionId};
use crate::mock_client::client_def::MockClient;
use crate::mock_client::header::MockHeader;
use crate::mock_client::state::{MockClientState, MockConsensusState};

pub trait ClientDef: Clone {
type Header: Header;
Expand All @@ -19,7 +23,7 @@ pub trait ClientDef: Clone {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] // TODO: Add Eq
#[allow(clippy::large_enum_variant)]
pub enum AnyHeader {
Mock(mocks::MockHeader),
Mock(MockHeader),
romac marked this conversation as resolved.
Show resolved Hide resolved
Tendermint(tendermint::header::Header),
}

Expand All @@ -41,10 +45,35 @@ impl Header for AnyHeader {

#[derive(Clone, Debug, PartialEq)]
pub enum AnyClientState {
Mock(mocks::MockClientState),
Mock(MockClientState),
romac marked this conversation as resolved.
Show resolved Hide resolved
Tendermint(crate::ics07_tendermint::client_state::ClientState),
}

impl AnyClientState {
pub fn check_header_and_update_state(
&self,
header: AnyHeader,
) -> Result<(AnyClientState, AnyConsensusState), Box<dyn std::error::Error>> {
match self {
AnyClientState::Tendermint(tm_state) => {
let (new_state, new_consensus) = tm_state.check_header_and_update_state(header)?;
Ok((
AnyClientState::Tendermint(new_state),
AnyConsensusState::Tendermint(new_consensus),
))
}
AnyClientState::Mock(mock_state) => {
romac marked this conversation as resolved.
Show resolved Hide resolved
let (new_state, new_consensus) =
mock_state.check_header_and_update_state(header)?;
Ok((
AnyClientState::Mock(new_state),
AnyConsensusState::Mock(new_consensus),
))
}
}
}
}

impl ClientState for AnyClientState {
fn chain_id(&self) -> String {
todo!()
Expand All @@ -54,28 +83,89 @@ impl ClientState for AnyClientState {
todo!()
}

fn get_latest_height(&self) -> Height {
fn latest_height(&self) -> Height {
match self {
AnyClientState::Tendermint(tm_state) => tm_state.get_latest_height(),
AnyClientState::Mock(mock_state) => mock_state.get_latest_height(),
AnyClientState::Tendermint(tm_state) => tm_state.latest_height(),
AnyClientState::Mock(mock_state) => mock_state.latest_height(),
romac marked this conversation as resolved.
Show resolved Hide resolved
}
}

fn is_frozen(&self) -> bool {
todo!()
match self {
AnyClientState::Tendermint(tm_state) => tm_state.is_frozen(),
AnyClientState::Mock(mock_state) => mock_state.is_frozen(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this variant is only used in tests, we should annotate this branch/case with #[cfg(test)].

}
}

// fn check_header_and_update_state(
// &self,
// header: &dyn Header,
// ) -> Result<(Box<dyn ClientState>, Box<dyn ConsensusState>), Box<dyn std::error::Error>> {
// match self {
// AnyClientState::Tendermint(tm_state) => tm_state.check_header_and_update_state(header),
// AnyClientState::Mock(mock_state) => mock_state.check_header_and_update_state(header),
// }
// }

fn verify_client_consensus_state(
&self,
_root: &CommitmentRoot,
height: Height,
prefix: &CommitmentPrefix,
proof: &CommitmentProof,
client_id: &ClientId,
consensus_height: Height,
expected_consensus_state: &dyn ConsensusState,
) -> Result<(), Box<dyn std::error::Error>> {
todo!()
match self {
AnyClientState::Tendermint(tm_state) => tm_state.verify_client_consensus_state(
height,
prefix,
proof,
client_id,
consensus_height,
expected_consensus_state,
),
AnyClientState::Mock(mock_state) => mock_state.verify_client_consensus_state(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this variant is only used in tests, we should annotate this branch/case with #[cfg(test)].

height,
prefix,
proof,
client_id,
consensus_height,
expected_consensus_state,
),
}
}

fn verify_connection_state(
&self,
height: Height,
prefix: &CommitmentPrefix,
proof: &CommitmentProof,
connection_id: &ConnectionId,
expected_connection_end: &ConnectionEnd,
) -> Result<(), Box<dyn std::error::Error>> {
match self {
AnyClientState::Tendermint(tm_state) => tm_state.verify_connection_state(
height,
prefix,
proof,
connection_id,
expected_connection_end,
),
AnyClientState::Mock(mock_state) => mock_state.verify_connection_state(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this variant is only used in tests, we should annotate this branch/case with #[cfg(test)].

height,
prefix,
proof,
connection_id,
expected_connection_end,
),
}
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum AnyConsensusState {
Mock(mocks::MockConsensusState),
Mock(MockConsensusState),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this variant is only used in tests, we should annotate it with #[cfg(test)].

Tendermint(crate::ics07_tendermint::consensus_state::ConsensusState),
}

Expand All @@ -99,7 +189,7 @@ impl ConsensusState for AnyConsensusState {

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AnyClient {
Mock(mocks::MockClient),
Mock(MockClient),
romac marked this conversation as resolved.
Show resolved Hide resolved
Tendermint(TendermintClient),
}

Expand Down
2 changes: 2 additions & 0 deletions modules/src/ics02_client/client_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ use serde_derive::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum ClientType {
Tendermint = 1,
Mock,
romac marked this conversation as resolved.
Show resolved Hide resolved
}

impl ClientType {
/// Yields the identifier of this client type as a string
pub fn as_string(&self) -> &'static str {
match self {
Self::Tendermint => "tendermint",
Self::Mock => "mock",
romac marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Expand Down
Loading