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

Mock testing for ICS 18 (relayer) update client messages #270

Merged
merged 10 commits into from
Sep 30, 2020
10 changes: 6 additions & 4 deletions modules/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use crate::ics07_tendermint;
use serde_derive::{Deserialize, Serialize};
use tendermint::block::Height;

use crate::ics02_client::client_def::AnyConsensusState;
use crate::ics02_client::client_def::AnyHeader;

#[cfg(test)]
use {
crate::ics02_client::client_def::AnyConsensusState, crate::mock_client::header::MockHeader,
crate::mock_client::state::MockConsensusState,
};
use {crate::mock_client::header::MockHeader, crate::mock_client::state::MockConsensusState};

#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SelfChainType {
Expand Down Expand Up @@ -37,6 +37,8 @@ impl From<MockHeader> for AnyConsensusState {
pub trait ChainReader {
fn chain_type(&self) -> SelfChainType;
fn self_historical_info(&self, height: Height) -> Option<HistoricalInfo>;
fn header(&self, height: Height) -> Option<AnyHeader>;
fn fetch_self_consensus_state(&self, height: Height) -> Option<AnyConsensusState>;
}

pub trait ChainKeeper {
Expand Down
96 changes: 70 additions & 26 deletions modules/src/context_mock.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
use crate::context::{ChainKeeper, ChainReader, HistoricalInfo, SelfChainType, SelfHeader};
use crate::ics02_client::client_def::{AnyConsensusState, AnyHeader};
use crate::mock_client::header::MockHeader;
use serde_derive::{Deserialize, Serialize};
use std::cmp::min;
use std::error::Error;
use tendermint::block::Height;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
pub struct MockChainContext {
/// Maximum size of the history
pub max_size: usize,
/// Highest height of the headers in the history
pub latest: Height,
/// A list of `max_size` headers ordered by height
pub history: Vec<MockHeader>,
}

impl MockChainContext {
pub fn new(max_size: usize, n: Height) -> Self {
/// Creates a new mock chain with max_size number of headers up to height h
pub fn new(max_size: usize, h: Height) -> Self {
// number of headers to store, if h is 0 nothing is stored
let n = min(max_size as u64, h.value());
Self {
max_size,
latest: n,
history: (0..n.value())
.map(|i| MockHeader(Height(i).increment()))
latest: h,
history: (0..n)
.rev()
.map(|i| MockHeader(Height(h.value() - i)))
.collect(),
}
}
Expand All @@ -38,29 +47,41 @@ impl MockChainContext {
}
}

pub fn add_header(&mut self, h: u64) {
let mut new_h = h;
if h == 0 {
new_h = u64::from(self.latest.increment());
}
self.store_historical_info(
Height(new_h),
HistoricalInfo {
header: SelfHeader::Mock(MockHeader(Height(new_h))),
},
);
}

pub fn validate(&self) -> Result<(), Box<dyn Error>> {
// TODO
// 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());
}

// get the highers header
let lh = self.history[self.history.len() - 1];
// check latest is properly updated with highest header height
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 ph = self.history[i - 1];
let h = self.history[i];
if ph.height().increment() != h.height() {
return Err("headers in history not sequential".to_string().into());
}
}
Ok(())
// // 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(())
}
}

Expand All @@ -78,10 +99,28 @@ impl ChainReader for MockChainContext {
None
} else {
Some(HistoricalInfo {
header: SelfHeader::Mock(self.history[h - l]),
header: SelfHeader::Mock(self.history[self.max_size + l - h - 1]),
})
}
}

fn header(&self, height: Height) -> Option<AnyHeader> {
let hi = self.self_historical_info(height)?.header;
match hi {
#[cfg(test)]
SelfHeader::Mock(h) => Some(h.into()),
_ => None,
}
}

fn fetch_self_consensus_state(&self, height: Height) -> Option<AnyConsensusState> {
let hi = self.self_historical_info(height)?.header;
match hi {
#[cfg(test)]
SelfHeader::Mock(h) => Some(h.into()),
_ => None,
}
}
}

impl ChainKeeper for MockChainContext {
Expand Down Expand Up @@ -133,6 +172,11 @@ mod tests {
ctx: MockChainContext::new(3, Height(2)),
args: [3, 4].to_vec(),
},
Test {
name: "Add with initial prune".to_string(),
ctx: MockChainContext::new(3, Height(10)),
args: [11].to_vec(),
},
Test {
name: "Attempt to add non sequential headers".to_string(),
ctx: MockChainContext::new(3, Height(2)),
Expand Down
7 changes: 6 additions & 1 deletion modules/src/ics02_client/client_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,12 @@ impl ConsensusState for AnyConsensusState {
}

fn height(&self) -> Height {
todo!()
match self {
AnyConsensusState::Tendermint(cs) => cs.height(),

#[cfg(test)]
AnyConsensusState::Mock(cs) => cs.height(),
}
}

fn root(&self) -> &CommitmentRoot {
Expand Down
71 changes: 61 additions & 10 deletions modules/src/ics02_client/context_mock.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::context_mock::MockChainContext;
use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState};
use crate::ics02_client::client_type::ClientType;
use crate::ics02_client::context::{ClientKeeper, ClientReader};
use crate::ics02_client::error::Error;
use crate::ics02_client::error::{Error, Kind};
use crate::ics02_client::state::ConsensusState;
use crate::ics24_host::identifier::ClientId;
use crate::mock_client::header::MockHeader;
use crate::mock_client::state::{MockClientRecord, MockClientState, MockConsensusState};
Expand All @@ -15,13 +17,31 @@ use tendermint::block::Height;
/// the ICS02 handlers (create, update client) and other dependent ICS handlers (e.g., ICS03).
#[derive(Clone, Debug)]
pub struct MockClientContext {
pub chain_context: MockChainContext,
/// The set of all clients, indexed by their id.
pub clients: HashMap<ClientId, MockClientRecord>,
}

impl MockClientContext {
pub fn new(chain_height: u64, max_history_size: usize) -> Self {
MockClientContext {
chain_context: MockChainContext::new(max_history_size, Height(chain_height)),
clients: Default::default(),
}
}
pub fn chain_context(&self) -> &MockChainContext {
&self.chain_context
}

pub fn set_chain_context(&mut self, chain_context: MockChainContext) {
self.chain_context = chain_context
}
}

impl Default for MockClientContext {
fn default() -> Self {
MockClientContext {
chain_context: Default::default(),
clients: Default::default(),
}
}
Expand Down Expand Up @@ -101,25 +121,56 @@ impl ClientReader for MockClientContext {
impl ClientKeeper for MockClientContext {
fn store_client_type(
&mut self,
_client_id: ClientId,
_client_type: ClientType,
client_id: ClientId,
client_type: ClientType,
) -> Result<(), Error> {
todo!()
let mut client_record = self.clients.entry(client_id).or_insert(MockClientRecord {
client_type,
consensus_states: Default::default(),
client_state: Default::default(),
});

client_record.client_type = client_type;
Ok(())
}

fn store_client_state(
&mut self,
_client_id: ClientId,
_client_state: AnyClientState,
client_id: ClientId,
client_state: AnyClientState,
) -> Result<(), Error> {
todo!()
match client_state {
AnyClientState::Mock(client_state) => {
let mut client_record = self.clients.entry(client_id).or_insert(MockClientRecord {
client_type: ClientType::Mock,
consensus_states: Default::default(),
client_state,
});
client_record.client_state = client_state;
Ok(())
}
_ => Err(Kind::BadClientState.into()),
}
}

fn store_consensus_state(
&mut self,
_client_id: ClientId,
_consensus_state: AnyConsensusState,
client_id: ClientId,
consensus_state: AnyConsensusState,
) -> Result<(), Error> {
todo!()
match consensus_state {
AnyConsensusState::Mock(consensus_state) => {
let client_record = self.clients.entry(client_id).or_insert(MockClientRecord {
client_type: ClientType::Mock,
consensus_states: Default::default(),
client_state: Default::default(),
});
client_record
.consensus_states
.insert(consensus_state.height(), consensus_state);
Ok(())
}
_ => Err(Kind::BadClientState.into()),
}
}
}
3 changes: 3 additions & 0 deletions modules/src/ics02_client/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ pub enum Kind {

#[error("mismatch between client and arguments types, expected: {0:?}")]
ClientArgsTypeMismatch(ClientType),

#[error("bad client state (expected mock client)")]
BadClientState,
}

impl Kind {
Expand Down
1 change: 1 addition & 0 deletions modules/src/ics02_client/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub enum ClientEvent {
ClientUpdated(ClientId),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ClientResult {
CreateResult(CreateClientResult),
UpdateResult(UpdateClientResult),
Expand Down
2 changes: 1 addition & 1 deletion modules/src/ics02_client/handler/create_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::ics02_client::handler::ClientEvent;
use crate::ics02_client::msgs::MsgCreateAnyClient;
use crate::ics24_host::identifier::ClientId;

#[derive(Debug)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CreateClientResult {
pub client_id: ClientId,
pub client_type: ClientType,
Expand Down
10 changes: 7 additions & 3 deletions modules/src/ics02_client/handler/update_client.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::handler::{HandlerOutput, HandlerResult};
use crate::ics02_client::client_def::{AnyClient, AnyClientState, AnyConsensusState, ClientDef};
use crate::ics02_client::context::ClientReader;
use crate::ics02_client::context::{ClientKeeper, ClientReader};
use crate::ics02_client::error::{Error, Kind};
use crate::ics02_client::handler::ClientEvent;
use crate::ics02_client::handler::{ClientEvent, ClientResult};

use crate::ics02_client::msgs::MsgUpdateAnyClient;
use crate::ics02_client::state::ClientState;
use crate::ics24_host::identifier::ClientId;

#[derive(Debug)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpdateClientResult {
pub client_id: ClientId,
pub client_state: AnyClientState,
Expand Down Expand Up @@ -57,6 +57,10 @@ pub fn process(
}))
}

pub fn keep(keeper: &mut dyn ClientKeeper, result: UpdateClientResult) -> Result<(), Error> {
keeper.store_client_result(ClientResult::UpdateResult(result))
}

#[cfg(test)]
mod tests {
use tendermint::block::Height;
Expand Down
Loading