Skip to content
18 changes: 18 additions & 0 deletions ibc-eureka-core/ics02-client/context/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ibc_eureka_core_client_types::Height;
use ibc_eureka_core_commitment_types::commitment::CommitmentPrefix;
use ibc_eureka_core_host_types::error::HostError;
use ibc_eureka_core_host_types::identifiers::ClientId;
use ibc_eureka_core_host_types::path::{ClientConsensusStatePath, ClientStatePath};
Expand Down Expand Up @@ -39,6 +40,12 @@ pub trait ClientValidationContext: Sized {
client_id: &ClientId,
height: &Height,
) -> Result<(Timestamp, Height), HostError>;

/// Returns the client identifier and the commitment prefix of the counterparty client.
fn counterparty_meta(
&self,
client_id: &ClientId,
) -> Result<Option<(ClientId, CommitmentPrefix)>, HostError>;
}

/// Defines the methods that all client `ExecutionContext`s (precisely the
Expand Down Expand Up @@ -98,6 +105,17 @@ pub trait ClientExecutionContext:
///
/// Note that this timestamp is determined by the host.
fn delete_update_meta(&mut self, client_id: ClientId, height: Height) -> Result<(), HostError>;

/// Store the client identifier and the commitment prefix of the counterparty client.
fn store_counterparty_meta(
&self,
client_id: &ClientId,
counterparty_client_id: &ClientId,
counterparty_prefix: &CommitmentPrefix,
) -> Result<(), HostError>;

/// Delete the client identifier and the commitment prefix of the counterparty client.
fn delete_counterparty_meta(&self, client_id: &ClientId) -> Result<(), HostError>;
}

/// An optional trait that extends the client validation context capabilities by
Expand Down
1 change: 1 addition & 0 deletions ibc-eureka-core/ics02-client/src/handler/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! This module implements the processing logic for ICS2 (client abstractions and functions) msgs.

pub mod create_client;
pub mod provide_counterparty;
pub mod recover_client;
pub mod update_client;
pub mod upgrade_client;
57 changes: 57 additions & 0 deletions ibc-eureka-core/ics02-client/src/handler/provide_counterparty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//! Protocol logic specific to processing ICS2 messages of type `MsgProvideCouterparty`.

use ibc_eureka_core_client_context::prelude::*;
use ibc_eureka_core_client_types::error::ClientError;
use ibc_eureka_core_client_types::msgs::MsgProvideCouterparty;
use ibc_eureka_core_host::{ExecutionContext, ValidationContext};
use ibc_primitives::prelude::*;

pub fn validate<Ctx>(ctx: &Ctx, msg: MsgProvideCouterparty) -> Result<(), ClientError>
where
Ctx: ValidationContext,
{
let MsgProvideCouterparty {
client_id, signer, ..
} = &msg;

ctx.validate_message_signer(signer)?;

let client_val_ctx = ctx.get_client_validation_context();

// Read client state from the host chain store. The client should already exist.
let client_state = client_val_ctx.client_state(client_id)?;

client_state
.status(client_val_ctx, client_id)?
.verify_is_active()?;

if client_val_ctx.counterparty_meta(client_id)?.is_some() {
return Err(ClientError::ClientSpecific {
description: "counterparty is already provided".into(),
});
}

Ok(())
}

pub fn execute<Ctx>(ctx: &mut Ctx, msg: MsgProvideCouterparty) -> Result<(), ClientError>
where
Ctx: ExecutionContext,
{
let MsgProvideCouterparty {
client_id,
counterparty_client_id,
counterparty_commitment_prefix,
..
} = &msg;

let client_exec_ctx = ctx.get_client_execution_context();

client_exec_ctx.store_counterparty_meta(
client_id,
counterparty_client_id,
counterparty_commitment_prefix,
)?;

Ok(())
}
3 changes: 3 additions & 0 deletions ibc-eureka-core/ics02-client/types/src/msgs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ use ibc_proto::google::protobuf::Any;

mod create_client;
mod misbehaviour;
mod provide_counterparty;
mod recover_client;
mod update_client;
mod upgrade_client;

pub use create_client::*;
pub use misbehaviour::*;
pub use provide_counterparty::*;
pub use recover_client::*;
pub use update_client::*;
pub use upgrade_client::*;
Expand All @@ -33,6 +35,7 @@ pub enum ClientMsg {
Misbehaviour(MsgSubmitMisbehaviour),
UpgradeClient(MsgUpgradeClient),
RecoverClient(MsgRecoverClient),
ProvideCounterparty(MsgProvideCouterparty),
}

pub enum MsgUpdateOrMisbehaviour {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! Definition of domain type message `MsgProvideCouterparty`.

use ibc_eureka_core_commitment_types::commitment::CommitmentPrefix;
use ibc_eureka_core_host_types::identifiers::ClientId;
use ibc_primitives::prelude::*;
use ibc_primitives::Signer;

pub const _PROVIDE_COUNTERPARTY_TYPE_URL: &str = "/ibc.core.client.v1.MsgProvideCouterparty";

/// A type of message that links an on-chain (IBC) client to its counterparty.
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MsgProvideCouterparty {
pub client_id: ClientId,
pub counterparty_client_id: ClientId,
pub counterparty_commitment_prefix: CommitmentPrefix,
pub signer: Signer,
}

impl MsgProvideCouterparty {
pub fn new(
client_id: ClientId,
counterparty_client_id: ClientId,
counterparty_commitment_prefix: CommitmentPrefix,
signer: Signer,
) -> Self {
MsgProvideCouterparty {
client_id,
counterparty_client_id,
counterparty_commitment_prefix,
signer,
}
}
}
4 changes: 3 additions & 1 deletion ibc-eureka-core/ics04-channel/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use ibc_eureka_core_client::context::prelude::*;
use ibc_eureka_core_handler_types::events::IbcEvent;
use ibc_eureka_core_host::types::error::HostError;
use ibc_eureka_core_host::types::identifiers::Sequence;
use ibc_eureka_core_host::types::path::{CommitmentPath, SeqSendPath};
use ibc_eureka_core_host::types::path::{
CommitmentPathV2 as CommitmentPath, SeqSendPathV2 as SeqSendPath,
};
use ibc_eureka_core_host::{ExecutionContext, ValidationContext};
use ibc_primitives::prelude::*;

Expand Down
67 changes: 47 additions & 20 deletions ibc-eureka-core/ics04-channel/src/handler/acknowledgement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use ibc_eureka_core_channel_types::events::AcknowledgePacket;
use ibc_eureka_core_channel_types::msgs::MsgAcknowledgement;
use ibc_eureka_core_client::context::prelude::*;
use ibc_eureka_core_handler_types::events::{IbcEvent, MessageEvent};
use ibc_eureka_core_host::types::path::{AckPath, ClientConsensusStatePath, CommitmentPath, Path};
use ibc_eureka_core_host::types::identifiers::ClientId;
use ibc_eureka_core_host::types::path::{
AckPathV2 as AckPath, ClientConsensusStatePath, CommitmentPathV2 as CommitmentPath, Path,
};
use ibc_eureka_core_host::{ExecutionContext, ValidationContext};
use ibc_eureka_core_router::module::Module;
use ibc_primitives::prelude::*;
Expand All @@ -33,18 +36,21 @@ where
ExecCtx: ExecutionContext,
{
let packet = &msg.packet;
let payload = &packet.payloads[0];

let (_, port_id_on_a) = &payload.header.source_port;
let channel_id_on_a = &packet.header.target_client_on_source;
let channel_target_client_on_source = &packet.header.target_client_on_source;
let channel_source_client_on_target = &packet.header.source_client_on_target;
let seq_on_a = &packet.header.seq_on_a;

// In all cases, this event is emitted
let event = IbcEvent::AcknowledgePacket(AcknowledgePacket::new(packet.clone()));
ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?;
ctx_a.emit_ibc_event(event)?;

let commitment_path_on_a = CommitmentPath::new(port_id_on_a, channel_id_on_a, *seq_on_a);
let commitment_path_on_a = CommitmentPath::new(
channel_source_client_on_target.as_ref(),
channel_target_client_on_source.as_ref(),
seq_on_a,
);

// check if we're in the NO-OP case
if ctx_a.get_packet_commitment(&commitment_path_on_a).is_err() {
Expand Down Expand Up @@ -92,14 +98,16 @@ where
let packet = &msg.packet;
let payload = &packet.payloads[0];

let (prefix_on_a, port_id_on_a) = &payload.header.source_port;
let channel_id_on_a = &packet.header.target_client_on_source;
let (_, port_id_on_b) = &payload.header.target_port;
let channel_id_on_b = &packet.header.source_client_on_target;
let channel_target_client_on_source = &packet.header.target_client_on_source;
let channel_source_client_on_target = &packet.header.source_client_on_target;
let seq_on_a = &packet.header.seq_on_a;
let data = &payload.data;

let commitment_path_on_a = CommitmentPath::new(port_id_on_a, channel_id_on_a, *seq_on_a);
let commitment_path_on_a = CommitmentPath::new(
channel_source_client_on_target.as_ref(),
channel_target_client_on_source.as_ref(),
seq_on_a,
);

// Verify packet commitment
let Ok(commitment_on_a) = ctx_a.get_packet_commitment(&commitment_path_on_a) else {
Expand All @@ -126,34 +134,53 @@ where
// Verify proofs
{
// TODO(rano): avoid a vs b confusion
let client_id_on_a = channel_id_on_b.as_ref();
let id_target_client_on_source = channel_target_client_on_source.as_ref();
let id_source_client_on_target: &ClientId = channel_source_client_on_target.as_ref();

let client_val_ctx_a = ctx_a.get_client_validation_context();

let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?;
let (stored_id_source_client_on_target, target_prefix) = client_val_ctx_a
.counterparty_meta(id_target_client_on_source)?
.ok_or(ChannelError::MissingCounterparty)?;

client_state_of_b_on_a
.status(ctx_a.get_client_validation_context(), client_id_on_a)?
if &stored_id_source_client_on_target != id_source_client_on_target {
return Err(ChannelError::MismatchCounterparty {
expected: stored_id_source_client_on_target.clone(),
actual: id_source_client_on_target.clone(),
});
}

let target_client_on_source = client_val_ctx_a.client_state(id_target_client_on_source)?;

target_client_on_source
.status(
ctx_a.get_client_validation_context(),
id_target_client_on_source,
)?
.verify_is_active()?;

client_state_of_b_on_a.validate_proof_height(msg.proof_height_on_b)?;
target_client_on_source.validate_proof_height(msg.proof_height_on_b)?;

let client_cons_state_path_on_a = ClientConsensusStatePath::new(
client_id_on_a.clone(),
id_target_client_on_source.clone(),
msg.proof_height_on_b.revision_number(),
msg.proof_height_on_b.revision_height(),
);
let consensus_state_of_b_on_a =
client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?;
let ack_commitment = compute_ack_commitment(&msg.acknowledgement);
let ack_path_on_b = AckPath::new(port_id_on_b, channel_id_on_b, *seq_on_a);
let ack_path_on_b = AckPath::new(
channel_source_client_on_target.as_ref(),
channel_target_client_on_source.as_ref(),
seq_on_a,
);

// Verify the proof for the packet against the chain store.
client_state_of_b_on_a.verify_membership(
prefix_on_a,
target_client_on_source.verify_membership(
&target_prefix,
&msg.proof_acked_on_b,
consensus_state_of_b_on_a.root(),
Path::Ack(ack_path_on_b),
Path::AckV2(ack_path_on_b),
ack_commitment.into_vec(),
)?;
}
Expand Down
Loading