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

Connection proofs refactor #170

Merged
merged 23 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Remove crossing hellos logic from connection handshake. Breaking changes in
connection message types.
([#156](https://github.com/cosmos/ibc-rs/issues/156)).
2 changes: 2 additions & 0 deletions .changelog/unreleased/bug-fixes/168-consensus-height.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Connection consensus state proof verification now properly uses `consensus_height`
([#168](https://github.com/cosmos/ibc-rs/issues/168)).
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Refactor connection proofs
([#165](https://github.com/cosmos/ibc-rs/issues/165)).
5 changes: 0 additions & 5 deletions crates/ibc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,3 @@ modelator = "0.4.2"
sha2 = { version = "0.10.6" }
tendermint-rpc = { version = "=0.25.0", features = ["http-client", "websocket-client"] }
tendermint-testgen = { version = "=0.25.0" } # Needed for generating (synthetic) light blocks.

[[test]]
name = "mbt"
path = "tests/mbt.rs"
required-features = ["mocks"]
16 changes: 8 additions & 8 deletions crates/ibc/src/core/ics02_client/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ pub trait ClientState:
#[allow(clippy::too_many_arguments)]
fn verify_client_consensus_state(
&self,
height: Height,
prefix: &CommitmentPrefix,
proof_height: Height,
counterparty_prefix: &CommitmentPrefix,
proof: &CommitmentProofBytes,
root: &CommitmentRoot,
client_id: &ClientId,
counterparty_client_id: &ClientId,
consensus_height: Height,
expected_consensus_state: &dyn ConsensusState,
) -> Result<(), Error>;
Expand All @@ -114,11 +114,11 @@ pub trait ClientState:
#[allow(clippy::too_many_arguments)]
fn verify_connection_state(
&self,
height: Height,
prefix: &CommitmentPrefix,
proof_height: Height,
counterparty_prefix: &CommitmentPrefix,
proof: &CommitmentProofBytes,
root: &CommitmentRoot,
connection_id: &ConnectionId,
counterparty_connection_id: &ConnectionId,
expected_connection_end: &ConnectionEnd,
) -> Result<(), Error>;

Expand All @@ -139,8 +139,8 @@ pub trait ClientState:
#[allow(clippy::too_many_arguments)]
fn verify_client_full_state(
&self,
height: Height,
prefix: &CommitmentPrefix,
proof_height: Height,
counterparty_prefix: &CommitmentPrefix,
proof: &CommitmentProofBytes,
root: &CommitmentRoot,
client_id: &ClientId,
Expand Down
5 changes: 4 additions & 1 deletion crates/ibc/src/core/ics03_connection/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ use crate::core::ics02_client::client_state::ClientState;
use crate::core::ics02_client::consensus_state::ConsensusState;
use crate::core::ics03_connection::connection::ConnectionEnd;
use crate::core::ics03_connection::error::Error;
use crate::core::ics03_connection::handler::{ConnectionIdState, ConnectionResult};
use crate::core::ics03_connection::handler::ConnectionResult;
use crate::core::ics03_connection::version::{get_compatible_versions, pick_version, Version};
use crate::core::ics23_commitment::commitment::CommitmentPrefix;
use crate::core::ics24_host::identifier::{ClientId, ConnectionId};
use crate::prelude::*;
use crate::Height;
use ibc_proto::google::protobuf::Any;

use super::handler::ConnectionIdState;

/// A context supplying all the necessary read-only dependencies for processing any `ConnectionMsg`.
pub trait ConnectionReader {
/// Returns the ConnectionEnd for the given identifier `conn_id`.
Expand All @@ -28,6 +30,7 @@ pub trait ConnectionReader {
/// Returns the current height of the local chain.
fn host_current_height(&self) -> Height;

#[deprecated(since = "0.20.0")]
/// Returns the oldest height available on the local chain.
fn host_oldest_height(&self) -> Height;

Expand Down
5 changes: 2 additions & 3 deletions crates/ibc/src/core/ics03_connection/handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! This module implements the processing logic for ICS3 (connection open handshake) messages.
//! This module implements the processing logic for ICS3 (connection open
hu55a1n1 marked this conversation as resolved.
Show resolved Hide resolved
//! handshake) messages.

use crate::core::ics03_connection::connection::ConnectionEnd;
use crate::core::ics03_connection::context::ConnectionReader;
Expand All @@ -12,8 +13,6 @@ pub mod conn_open_confirm;
pub mod conn_open_init;
pub mod conn_open_try;

pub mod verify;

/// Defines the possible states of a connection identifier in a `ConnectionResult`.
#[derive(Clone, Debug)]
pub enum ConnectionIdState {
Expand Down
175 changes: 109 additions & 66 deletions crates/ibc/src/core/ics03_connection/handler/conn_open_ack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,92 +4,135 @@ use crate::core::ics03_connection::connection::{ConnectionEnd, Counterparty, Sta
use crate::core::ics03_connection::context::ConnectionReader;
use crate::core::ics03_connection::error::Error;
use crate::core::ics03_connection::events::Attributes;
use crate::core::ics03_connection::handler::verify::{
check_client_consensus_height, verify_proofs,
};
use crate::core::ics03_connection::handler::{ConnectionIdState, ConnectionResult};
use crate::core::ics03_connection::handler::ConnectionResult;
use crate::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck;
use crate::events::IbcEvent;
use crate::handler::{HandlerOutput, HandlerResult};
use crate::prelude::*;

use super::ConnectionIdState;

/// Per our convention, this message is processed on chain A.
pub(crate) fn process(
ctx: &dyn ConnectionReader,
ctx_a: &dyn ConnectionReader,
msg: MsgConnectionOpenAck,
) -> HandlerResult<ConnectionResult, Error> {
let mut output = HandlerOutput::builder();

// If a consensus proof is present, check that the consensus height (for
// client proof) in the message is not too advanced nor too old.
if let Some(consensus_height) = msg.consensus_height() {
check_client_consensus_height(ctx, consensus_height)?;
if msg.consensus_height_of_a_on_b > ctx_a.host_current_height() {
return Err(Error::invalid_consensus_height(
msg.consensus_height_of_a_on_b,
ctx_a.host_current_height(),
));
}

// Validate the connection end.
let mut conn_end = ctx.connection_end(&msg.connection_id)?;
// A connection end must be Init or TryOpen; otherwise we return an error.
let state_is_consistent = conn_end.state_matches(&State::Init)
&& conn_end.versions().contains(&msg.version)
|| conn_end.state_matches(&State::TryOpen)
&& conn_end.versions().get(0).eq(&Some(&msg.version));

if !state_is_consistent {
// Old connection end is in incorrect state, propagate the error.
return Err(Error::connection_mismatch(msg.connection_id));
}
///////////////////////////////////////////////////////////
// validate_self_client() verification goes here
// See [issue](https://github.com/cosmos/ibc-rs/issues/162)
///////////////////////////////////////////////////////////

// Set the connection ID of the counterparty
let prev_counterparty = conn_end.counterparty();
let counterparty = Counterparty::new(
prev_counterparty.client_id().clone(),
Some(msg.counterparty_connection_id.clone()),
prev_counterparty.prefix().clone(),
);
conn_end.set_state(State::Open);
conn_end.set_version(msg.version.clone());
conn_end.set_counterparty(counterparty);
let conn_end_on_a = ctx_a.connection_end(&msg.conn_id_on_a)?;
if !(conn_end_on_a.state_matches(&State::Init)
&& conn_end_on_a.versions().contains(&msg.version))
{
return Err(Error::connection_mismatch(msg.conn_id_on_a));
}

// Proof verification.
let expected_conn = {
// The counterparty is the local chain.
let counterparty = Counterparty::new(
conn_end.client_id().clone(), // The local client identifier.
Some(msg.connection_id.clone()), // This chain's connection id as known on counterparty.
ctx.commitment_prefix(), // Local commitment prefix.
);
{
let client_state_of_b_on_a = ctx_a.client_state(conn_end_on_a.client_id())?;
let consensus_state_of_b_on_a =
ctx_a.client_consensus_state(conn_end_on_a.client_id(), msg.proofs_height_on_b)?;

ConnectionEnd::new(
State::TryOpen,
conn_end.counterparty().client_id().clone(),
counterparty,
vec![msg.version.clone()],
conn_end.delay_period(),
)
};
let prefix_on_a = ctx_a.commitment_prefix();
let prefix_on_b = conn_end_on_a.counterparty().prefix();
let client_id_on_a = conn_end_on_a.client_id();
let client_id_on_b = conn_end_on_a.counterparty().client_id();

{
let conn_id_on_b = conn_end_on_a
.counterparty()
.connection_id()
.ok_or_else(Error::invalid_counterparty)?;
let expected_conn_end_on_b = ConnectionEnd::new(
State::TryOpen,
client_id_on_b.clone(),
Counterparty::new(
client_id_on_a.clone(),
Some(msg.conn_id_on_a.clone()),
prefix_on_a,
),
vec![msg.version.clone()],
conn_end_on_a.delay_period(),
);

client_state_of_b_on_a
.verify_connection_state(
msg.proofs_height_on_b,
prefix_on_b,
&msg.proof_conn_end_on_b,
consensus_state_of_b_on_a.root(),
conn_id_on_b,
&expected_conn_end_on_b,
)
.map_err(Error::verify_connection_state)?;
}

// 2. Pass the details to the verification function.
verify_proofs(
ctx,
msg.client_state,
msg.proofs.height(),
&conn_end,
&expected_conn,
&msg.proofs,
)?;

output.log("success: connection verification passed");

let result = ConnectionResult {
connection_id: msg.connection_id,
connection_id_state: ConnectionIdState::Reused,
connection_end: conn_end,
client_state_of_b_on_a
.verify_client_full_state(
msg.proofs_height_on_b,
prefix_on_b,
&msg.proof_client_state_of_a_on_b,
consensus_state_of_b_on_a.root(),
client_id_on_b,
msg.client_state_of_a_on_b,
)
.map_err(|e| {
Error::client_state_verification_failure(conn_end_on_a.client_id().clone(), e)
})?;

let expected_consensus_state_of_a_on_b =
ctx_a.host_consensus_state(msg.consensus_height_of_a_on_b)?;
client_state_of_b_on_a
.verify_client_consensus_state(
msg.proofs_height_on_b,
prefix_on_b,
&msg.proof_consensus_state_of_a_on_b,
consensus_state_of_b_on_a.root(),
conn_end_on_a.counterparty().client_id(),
msg.consensus_height_of_a_on_b,
expected_consensus_state_of_a_on_b.as_ref(),
)
.map_err(|e| Error::consensus_state_verification_failure(msg.proofs_height_on_b, e))?;
}

// Success
let result = {
let new_conn_end_on_a = {
let mut counterparty = conn_end_on_a.counterparty().clone();
counterparty.connection_id = Some(msg.conn_id_on_b.clone());

let mut new_conn_end_on_a = conn_end_on_a;
new_conn_end_on_a.set_state(State::Open);
new_conn_end_on_a.set_version(msg.version.clone());
new_conn_end_on_a.set_counterparty(counterparty);
new_conn_end_on_a
};

ConnectionResult {
connection_id: msg.conn_id_on_a,
connection_id_state: ConnectionIdState::Reused,
connection_end: new_conn_end_on_a,
}
};

let event_attributes = Attributes {
connection_id: Some(result.connection_id.clone()),
..Default::default()
};

output.emit(IbcEvent::OpenAckConnection(event_attributes.into()));
output.log("success: conn_open_ack verification passed");

Ok(output.with_result(result))
}
Expand Down Expand Up @@ -126,12 +169,12 @@ mod tests {

let msg_ack =
MsgConnectionOpenAck::try_from(get_dummy_raw_msg_conn_open_ack(10, 10)).unwrap();
let conn_id = msg_ack.connection_id.clone();
let counterparty_conn_id = msg_ack.counterparty_connection_id.clone();
let conn_id = msg_ack.conn_id_on_a.clone();
let counterparty_conn_id = msg_ack.conn_id_on_b.clone();

// Client parameters -- identifier and correct height (matching the proof height)
let client_id = ClientId::from_str("mock_clientid").unwrap();
let proof_height = msg_ack.proofs.height();
let proof_height = msg_ack.proofs_height_on_b;

// Parametrize the host chain to have a height at least as recent as the
// the height of the proofs in the Ack msg.
Expand All @@ -150,7 +193,7 @@ mod tests {
client_id.clone(),
Counterparty::new(
client_id.clone(),
Some(msg_ack.counterparty_connection_id.clone()),
Some(msg_ack.conn_id_on_b.clone()),
CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(),
),
vec![msg_ack.version.clone()],
Expand Down
Loading