ibc_core_channel/handler/
send_packet.rs

1use ibc_core_channel_types::channel::Counterparty;
2use ibc_core_channel_types::commitment::compute_packet_commitment;
3use ibc_core_channel_types::error::ChannelError;
4use ibc_core_channel_types::events::SendPacket;
5use ibc_core_channel_types::packet::Packet;
6use ibc_core_client::context::prelude::*;
7use ibc_core_handler_types::events::{IbcEvent, MessageEvent};
8use ibc_core_host::types::path::{
9    ChannelEndPath, ClientConsensusStatePath, CommitmentPath, SeqSendPath,
10};
11use ibc_primitives::prelude::*;
12
13use crate::context::{SendPacketExecutionContext, SendPacketValidationContext};
14
15/// Send the given packet, including all necessary validation.
16///
17/// Equivalent to calling [`send_packet_validate`], followed by [`send_packet_execute`]
18pub fn send_packet(
19    ctx_a: &mut impl SendPacketExecutionContext,
20    packet: Packet,
21) -> Result<(), ChannelError> {
22    send_packet_validate(ctx_a, &packet)?;
23    send_packet_execute(ctx_a, packet)
24}
25
26/// Validate that sending the given packet would succeed.
27pub fn send_packet_validate(
28    ctx_a: &impl SendPacketValidationContext,
29    packet: &Packet,
30) -> Result<(), ChannelError> {
31    if !packet.timeout_height_on_b.is_set() && !packet.timeout_timestamp_on_b.is_set() {
32        return Err(ChannelError::MissingTimeout);
33    }
34
35    let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a);
36    let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?;
37
38    // Checks the channel end not be `Closed`.
39    // This allows for optimistic packet processing before a channel opens
40    chan_end_on_a.verify_not_closed()?;
41
42    let counterparty = Counterparty::new(
43        packet.port_id_on_b.clone(),
44        Some(packet.chan_id_on_b.clone()),
45    );
46
47    chan_end_on_a.verify_counterparty_matches(&counterparty)?;
48
49    let conn_id_on_a = &chan_end_on_a.connection_hops()[0];
50
51    let conn_end_on_a = ctx_a.connection_end(conn_id_on_a)?;
52
53    let client_id_on_a = conn_end_on_a.client_id();
54
55    let client_val_ctx_a = ctx_a.get_client_validation_context();
56
57    let client_state_of_b_on_a = client_val_ctx_a.client_state(client_id_on_a)?;
58
59    client_state_of_b_on_a
60        .status(ctx_a.get_client_validation_context(), client_id_on_a)?
61        .verify_is_active()?;
62
63    let latest_height_on_a = client_state_of_b_on_a.latest_height();
64
65    if packet.timeout_height_on_b.has_expired(latest_height_on_a) {
66        return Err(ChannelError::InsufficientPacketHeight {
67            chain_height: latest_height_on_a,
68            timeout_height: packet.timeout_height_on_b,
69        });
70    }
71
72    let client_cons_state_path_on_a = ClientConsensusStatePath::new(
73        client_id_on_a.clone(),
74        latest_height_on_a.revision_number(),
75        latest_height_on_a.revision_height(),
76    );
77    let consensus_state_of_b_on_a =
78        client_val_ctx_a.consensus_state(&client_cons_state_path_on_a)?;
79    let latest_timestamp = consensus_state_of_b_on_a.timestamp()?;
80    let packet_timestamp = packet.timeout_timestamp_on_b;
81    if packet_timestamp.has_expired(&latest_timestamp) {
82        return Err(ChannelError::ExpiredPacketTimestamp);
83    }
84
85    let seq_send_path_on_a = SeqSendPath::new(&packet.port_id_on_a, &packet.chan_id_on_a);
86    let next_seq_send_on_a = ctx_a.get_next_sequence_send(&seq_send_path_on_a)?;
87
88    if packet.seq_on_a != next_seq_send_on_a {
89        return Err(ChannelError::MismatchedPacketSequence {
90            actual: packet.seq_on_a,
91            expected: next_seq_send_on_a,
92        });
93    }
94
95    Ok(())
96}
97
98/// Send the packet without any validation.
99///
100/// A prior call to [`send_packet_validate`] MUST have succeeded.
101pub fn send_packet_execute(
102    ctx_a: &mut impl SendPacketExecutionContext,
103    packet: Packet,
104) -> Result<(), ChannelError> {
105    {
106        let seq_send_path_on_a = SeqSendPath::new(&packet.port_id_on_a, &packet.chan_id_on_a);
107        let next_seq_send_on_a = ctx_a.get_next_sequence_send(&seq_send_path_on_a)?;
108
109        ctx_a.store_next_sequence_send(&seq_send_path_on_a, next_seq_send_on_a.increment())?;
110    }
111
112    ctx_a.store_packet_commitment(
113        &CommitmentPath::new(&packet.port_id_on_a, &packet.chan_id_on_a, packet.seq_on_a),
114        compute_packet_commitment(
115            &packet.data,
116            &packet.timeout_height_on_b,
117            &packet.timeout_timestamp_on_b,
118        ),
119    )?;
120
121    // emit events and logs
122    {
123        let chan_end_path_on_a = ChannelEndPath::new(&packet.port_id_on_a, &packet.chan_id_on_a);
124        let chan_end_on_a = ctx_a.channel_end(&chan_end_path_on_a)?;
125        let conn_id_on_a = &chan_end_on_a.connection_hops()[0];
126
127        ctx_a.log_message("success: packet send".to_string())?;
128        let event = IbcEvent::SendPacket(SendPacket::new(
129            packet,
130            chan_end_on_a.ordering,
131            conn_id_on_a.clone(),
132        ));
133        ctx_a.emit_ibc_event(IbcEvent::Message(MessageEvent::Channel))?;
134        ctx_a.emit_ibc_event(event)?;
135    }
136
137    Ok(())
138}