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

Fix the connection delay logic to use the header update block time #1917

Merged
merged 51 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
8f0dbaa
Return update height from update_client_{dst,src} methods
hu55a1n1 Feb 23, 2022
a376a67
Add query_host_consensus_state()
hu55a1n1 Feb 24, 2022
6f799dd
Set scheduled time appropriately
hu55a1n1 Feb 24, 2022
c7593ab
Handle connection delay only if batch contains packet events
hu55a1n1 Feb 24, 2022
79e09c3
Apply suggestion
hu55a1n1 Feb 24, 2022
f8ed1ba
Fix mock impl
hu55a1n1 Feb 24, 2022
6877290
Fix schedule_time calculation
hu55a1n1 Feb 24, 2022
52a4a44
Update comment
hu55a1n1 Feb 24, 2022
eab6908
Fix comment
hu55a1n1 Feb 24, 2022
3fcb91f
Add .changelog entry
hu55a1n1 Feb 24, 2022
09b1da6
Improve has_packet_msgs()
hu55a1n1 Feb 24, 2022
6181fb9
Implement query_host_consensus_state() for wrapper chain handles
hu55a1n1 Mar 8, 2022
2b8ef52
Apply suggestion
hu55a1n1 Mar 10, 2022
02e6170
Fix clippy errors
hu55a1n1 Mar 10, 2022
9192eec
Rework update methods
hu55a1n1 Mar 14, 2022
e61c05e
Minor refactoring
hu55a1n1 Mar 14, 2022
6d1a0de
Avoid partitioning events
hu55a1n1 Mar 14, 2022
0691987
Handle misbehaviour case explicitly
hu55a1n1 Mar 14, 2022
a089c6e
Merge remote-tracking branch 'origin/master' into hu55a1n1/1772-fix-c…
soareschen Mar 15, 2022
aa799bf
Add connection delay test
soareschen Mar 15, 2022
27ef25f
Merge remote-tracking branch 'origin/master' into hu55a1n1/1772-fix-c…
soareschen Mar 15, 2022
a9abda4
Use first UpdateClient event to determine processed_height
hu55a1n1 Mar 15, 2022
54281b6
Add comment
hu55a1n1 Mar 15, 2022
fc5c866
Check for frozen clients in case of misbehavior during client update
hu55a1n1 Mar 15, 2022
792011e
Adjust connection delay for avg block time
hu55a1n1 Mar 15, 2022
4636652
Merge branch 'hu55a1n1/1772-fix-conn-delay-check' of github.com:infor…
hu55a1n1 Mar 15, 2022
75d7501
Add TODO for moving to /header endpoint
hu55a1n1 Mar 16, 2022
bf79965
Make update_client_dst() similar to update_client_src()
hu55a1n1 Mar 16, 2022
009d4f4
Fix integration test
hu55a1n1 Mar 16, 2022
4df249a
Compare scheduled time against latest chain time
hu55a1n1 Mar 16, 2022
a42d830
Allow query_host_consensus_state to return latest state when Height i…
hu55a1n1 Mar 16, 2022
c98829c
Fix conn delay elapsed calculation
hu55a1n1 Mar 16, 2022
68fa5d2
Cleanup
hu55a1n1 Mar 16, 2022
f3c4a93
Check for block delay
hu55a1n1 Mar 16, 2022
111fb68
Add config comment for max_expected_time_per_block
hu55a1n1 Mar 16, 2022
8526a1f
Wait for conn delay
hu55a1n1 Mar 16, 2022
52684a3
More comments
hu55a1n1 Mar 17, 2022
287e3a9
Extract all connection-delay logic from RelayPath
hu55a1n1 Mar 18, 2022
d1b712b
Address review feedback
hu55a1n1 Mar 24, 2022
1b2db6e
Merge remote-tracking branch 'origin/master' into hu55a1n1/1772-fix-c…
hu55a1n1 Mar 25, 2022
b9323b8
Merge branch 'master' into hu55a1n1/1772-fix-conn-delay-check
ancazamfir Mar 29, 2022
ef95dd2
Address review feedback
hu55a1n1 Mar 30, 2022
beb9fd9
Closures for conn-delay specific lazy eval
hu55a1n1 Mar 30, 2022
818e614
Merge branch 'hu55a1n1/1772-fix-conn-delay-check' of github.com:infor…
hu55a1n1 Mar 30, 2022
ec076a7
Minor refactoring
hu55a1n1 Mar 30, 2022
afff3c4
Add helpers has_conn_delay_elapsed() and conn_delay_remaining()
hu55a1n1 Mar 30, 2022
8b32f51
Handle connection block delay
hu55a1n1 Mar 30, 2022
2f5679e
Cast block delay to u32
hu55a1n1 Mar 30, 2022
e058358
Extract out CLI specific link code
hu55a1n1 Mar 31, 2022
f8f6a94
Make opdata methods priv
hu55a1n1 Apr 1, 2022
14dcb2a
Apply suggestions from review
hu55a1n1 Apr 1, 2022
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,2 @@
- Fix the connection delay logic to use the timestamp of the host block when the client update header was installed.
([#1772](https://github.com/informalsystems/ibc-rs/issues/1772))
1 change: 1 addition & 0 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ clock_drift = '5s'
# The block time together with the clock drift are added to the source drift to estimate
# the maximum clock drift when creating a client on this chain. Default: 10s
# For cosmos-SDK chains a good approximation is `timeout_propose` + `timeout_commit`
# Note: This MUST be the same as the `max_expected_time_per_block` genesis parameter for Tendermint chains.
max_block_time = '10s'

# Specify the amount of time to be used as the light client trusting period.
Expand Down
20 changes: 13 additions & 7 deletions modules/src/core/ics04_channel/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,7 @@ pub trait ChannelReader {
fn max_expected_time_per_block(&self) -> Duration;

fn block_delay(&self, delay_period_time: Duration) -> u64 {
let expected_time_per_block = self.max_expected_time_per_block();
if expected_time_per_block.is_zero() {
return 0;
}

FloatCore::ceil(delay_period_time.as_secs_f64() / expected_time_per_block.as_secs_f64())
as u64
calculate_block_delay(delay_period_time, self.max_expected_time_per_block())
}
}

Expand Down Expand Up @@ -279,3 +273,15 @@ pub trait ChannelKeeper {
/// Should never fail.
fn increase_channel_counter(&mut self);
}

pub fn calculate_block_delay(
delay_period_time: Duration,
max_expected_time_per_block: Duration,
) -> u64 {
if max_expected_time_per_block.is_zero() {
return 0;
}

FloatCore::ceil(delay_period_time.as_secs_f64() / max_expected_time_per_block.as_secs_f64())
as u64
}
2 changes: 2 additions & 0 deletions relayer/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ pub trait ChainEndpoint: Sized {
request: QueryBlockRequest,
) -> Result<(Vec<IbcEvent>, Vec<IbcEvent>), Error>;

fn query_host_consensus_state(&self, height: ICSHeight) -> Result<Self::ConsensusState, Error>;

// Provable queries
fn proven_client_state(
&self,
Expand Down
14 changes: 14 additions & 0 deletions relayer/src/chain/cosmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1948,6 +1948,20 @@ impl ChainEndpoint for CosmosSdkChain {
}
}

fn query_host_consensus_state(&self, height: ICSHeight) -> Result<Self::ConsensusState, Error> {
let height = Height::try_from(height.revision_height).map_err(Error::invalid_height)?;

// TODO(hu55a1n1): use the `/header` RPC endpoint instead when we move to tendermint v0.35.x
let rpc_call = match height.value() {
0 => self.rpc_client.latest_block(),
_ => self.rpc_client.block(height),
};
let response = self
.block_on(rpc_call)
.map_err(|e| Error::rpc(self.config.rpc_addr.clone(), e))?;
Ok(response.block.header.into())
}

fn proven_client_state(
&self,
client_id: &ClientId,
Expand Down
7 changes: 7 additions & 0 deletions relayer/src/chain/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ pub enum ChainRequest {
request: QueryBlockRequest,
reply_to: ReplyTo<(Vec<IbcEvent>, Vec<IbcEvent>)>,
},

QueryHostConsensusState {
height: Height,
reply_to: ReplyTo<AnyConsensusState>,
},
}

pub trait ChainHandle: Clone + Send + Sync + Serialize + Debug + 'static {
Expand Down Expand Up @@ -560,4 +565,6 @@ pub trait ChainHandle: Clone + Send + Sync + Serialize + Debug + 'static {
&self,
request: QueryBlockRequest,
) -> Result<(Vec<IbcEvent>, Vec<IbcEvent>), Error>;

fn query_host_consensus_state(&self, height: Height) -> Result<AnyConsensusState, Error>;
}
4 changes: 4 additions & 0 deletions relayer/src/chain/handle/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,10 @@ impl ChainHandle for BaseChainHandle {
) -> Result<(Vec<IbcEvent>, Vec<IbcEvent>), Error> {
self.send(|reply_to| ChainRequest::QueryPacketEventDataFromBlocks { request, reply_to })
}

fn query_host_consensus_state(&self, height: Height) -> Result<AnyConsensusState, Error> {
self.send(|reply_to| ChainRequest::QueryHostConsensusState { height, reply_to })
}
}

impl Serialize for BaseChainHandle {
Expand Down
4 changes: 4 additions & 0 deletions relayer/src/chain/handle/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,4 +412,8 @@ impl<Handle: ChainHandle> ChainHandle for CachingChainHandle<Handle> {
) -> Result<(Vec<IbcEvent>, Vec<IbcEvent>), Error> {
self.inner().query_blocks(request)
}

fn query_host_consensus_state(&self, height: Height) -> Result<AnyConsensusState, Error> {
self.inner.query_host_consensus_state(height)
}
}
4 changes: 4 additions & 0 deletions relayer/src/chain/handle/counting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,4 +448,8 @@ impl<Handle: ChainHandle> ChainHandle for CountingChainHandle<Handle> {
self.inc_metric("query_blocks");
self.inner().query_blocks(request)
}

fn query_host_consensus_state(&self, height: Height) -> Result<AnyConsensusState, Error> {
self.inner.query_host_consensus_state(height)
}
}
4 changes: 4 additions & 0 deletions relayer/src/chain/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ impl ChainEndpoint for MockChain {
unimplemented!()
}

fn query_host_consensus_state(&self, _height: Height) -> Result<Self::ConsensusState, Error> {
unimplemented!()
}

fn proven_client_state(
&self,
_client_id: &ClientId,
Expand Down
19 changes: 19 additions & 0 deletions relayer/src/chain/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,10 @@ where
self.query_blocks(request, reply_to)?
},

Ok(ChainRequest::QueryHostConsensusState { height, reply_to }) => {
self.query_host_consensus_state(height, reply_to)?
},

Err(e) => error!("received error via chain request channel: {}", e),
}
},
Expand Down Expand Up @@ -870,4 +874,19 @@ where

Ok(())
}

fn query_host_consensus_state(
&self,
height: Height,
reply_to: ReplyTo<AnyConsensusState>,
) -> Result<(), Error> {
let result = self
.chain
.query_host_consensus_state(height)
.map(|h| h.wrap_any());

reply_to.send(result).map_err(Error::send)?;

Ok(())
}
}
55 changes: 1 addition & 54 deletions relayer/src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ use ibc::{
ics04_channel::channel::State as ChannelState,
ics24_host::identifier::{ChannelId, PortChannelId, PortId},
},
events::IbcEvent,
Height,
};
use tracing::error_span;

use crate::chain::counterparty::check_channel_counterparty;
use crate::chain::handle::ChainHandle;
use crate::channel::{Channel, ChannelSide};
use crate::link::error::LinkError;
use crate::link::relay_path::RelayPath;

pub mod cli;
pub mod error;
pub mod operational_data;
mod pending;
Expand Down Expand Up @@ -194,56 +193,4 @@ impl<ChainA: ChainHandle, ChainB: ChainHandle> Link<ChainA, ChainB> {
// going slowly, but reliably.
Link::new_from_opts(chain_b, chain_a, opts, with_tx_confirmation)
}

/// Implements the `packet-recv` CLI
pub fn build_and_send_recv_packet_messages(&self) -> Result<Vec<IbcEvent>, LinkError> {
let _span = error_span!(
"PacketRecvCmd",
src_chain = %self.a_to_b.src_chain().id(),
src_port = %self.a_to_b.src_port_id(),
src_channel = %self.a_to_b.src_channel_id(),
dst_chain = %self.a_to_b.dst_chain().id(),
)
.entered();

self.a_to_b.build_recv_packet_and_timeout_msgs(None)?;

let mut results = vec![];

// Block waiting for all of the scheduled data (until `None` is returned)
while let Some(odata) = self.a_to_b.fetch_scheduled_operational_data() {
let mut last_res = self
.a_to_b
.relay_from_operational_data::<relay_sender::SyncSender>(odata)?;
results.append(&mut last_res.events);
}

Ok(results)
}

/// Implements the `packet-ack` CLI
pub fn build_and_send_ack_packet_messages(&self) -> Result<Vec<IbcEvent>, LinkError> {
let _span = error_span!(
"PacketAckCmd",
src_chain = %self.a_to_b.src_chain().id(),
src_port = %self.a_to_b.src_port_id(),
src_channel = %self.a_to_b.src_channel_id(),
dst_chain = %self.a_to_b.dst_chain().id(),
)
.entered();

self.a_to_b.build_packet_ack_msgs(None)?;

let mut results = vec![];

// Block waiting for all of the scheduled data
while let Some(odata) = self.a_to_b.fetch_scheduled_operational_data() {
let mut last_res = self
.a_to_b
.relay_from_operational_data::<relay_sender::SyncSender>(odata)?;
results.append(&mut last_res.events);
}

Ok(results)
}
}
154 changes: 154 additions & 0 deletions relayer/src/link/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use std::convert::TryInto;
use std::thread;
use std::time::{Duration, Instant};

use ibc::events::IbcEvent;
use ibc::Height;
use tracing::{error_span, info};

use crate::chain::handle::ChainHandle;
use crate::link::error::LinkError;
use crate::link::operational_data::OperationalData;
use crate::link::relay_path::RelayPath;
use crate::link::{relay_sender, Link};

impl<ChainA: ChainHandle, ChainB: ChainHandle> RelayPath<ChainA, ChainB> {
/// Fetches an operational data that has fulfilled its predefined delay period. May _block_
/// waiting for the delay period to pass.
/// Returns `Ok(None)` if there is no operational data scheduled.
pub(crate) fn fetch_scheduled_operational_data(
&self,
) -> Result<Option<OperationalData>, LinkError> {
if let Some(odata) = self.src_operational_data.pop_front() {
Ok(Some(wait_for_conn_delay(
odata,
&|| self.src_time_latest(),
&|| self.src_max_block_time(),
&|| self.src_latest_height(),
)?))
} else if let Some(odata) = self.dst_operational_data.pop_front() {
Ok(Some(wait_for_conn_delay(
odata,
&|| self.dst_time_latest(),
&|| self.dst_max_block_time(),
&|| self.dst_latest_height(),
)?))
} else {
Ok(None)
}
}
}

impl<ChainA: ChainHandle, ChainB: ChainHandle> Link<ChainA, ChainB> {
/// Implements the `packet-recv` CLI
pub fn build_and_send_recv_packet_messages(&self) -> Result<Vec<IbcEvent>, LinkError> {
let _span = error_span!(
"PacketRecvCmd",
src_chain = %self.a_to_b.src_chain().id(),
src_port = %self.a_to_b.src_port_id(),
src_channel = %self.a_to_b.src_channel_id(),
dst_chain = %self.a_to_b.dst_chain().id(),
)
.entered();

self.a_to_b.build_recv_packet_and_timeout_msgs(None)?;

let mut results = vec![];

// Block waiting for all of the scheduled data (until `None` is returned)
while let Some(odata) = self.a_to_b.fetch_scheduled_operational_data()? {
let mut last_res = self
.a_to_b
.relay_from_operational_data::<relay_sender::SyncSender>(odata)?;
results.append(&mut last_res.events);
}

Ok(results)
}

/// Implements the `packet-ack` CLI
pub fn build_and_send_ack_packet_messages(&self) -> Result<Vec<IbcEvent>, LinkError> {
let _span = error_span!(
"PacketAckCmd",
src_chain = %self.a_to_b.src_chain().id(),
src_port = %self.a_to_b.src_port_id(),
src_channel = %self.a_to_b.src_channel_id(),
dst_chain = %self.a_to_b.dst_chain().id(),
)
.entered();

self.a_to_b.build_packet_ack_msgs(None)?;

let mut results = vec![];

// Block waiting for all of the scheduled data
while let Some(odata) = self.a_to_b.fetch_scheduled_operational_data()? {
let mut last_res = self
.a_to_b
.relay_from_operational_data::<relay_sender::SyncSender>(odata)?;
results.append(&mut last_res.events);
}

Ok(results)
}
}

fn wait_for_conn_delay<ChainTime, MaxBlockTime, LatestHeight>(
odata: OperationalData,
chain_time: &ChainTime,
max_expected_time_per_block: &MaxBlockTime,
latest_height: &LatestHeight,
) -> Result<OperationalData, LinkError>
where
ChainTime: Fn() -> Result<Instant, LinkError>,
MaxBlockTime: Fn() -> Result<Duration, LinkError>,
LatestHeight: Fn() -> Result<Height, LinkError>,
{
let (time_left, blocks_left) =
odata.conn_delay_remaining(chain_time, max_expected_time_per_block, latest_height)?;

match (time_left, blocks_left) {
(Duration::ZERO, 0) => {
info!(
"ready to fetch a scheduled op. data with batch of size {} targeting {}",
odata.batch.len(),
odata.target,
);
Ok(odata)
}
(Duration::ZERO, blocks_left) => {
info!(
"waiting ({:?} blocks left) for a scheduled op. data with batch of size {} targeting {}",
blocks_left,
odata.batch.len(),
odata.target,
);

let blocks_left: u32 = blocks_left.try_into().expect("blocks_left > u32::MAX");

// Wait until the delay period passes
thread::sleep(blocks_left * max_expected_time_per_block()?);

Ok(odata)
}
(time_left, _) => {
info!(
"waiting ({:?} left) for a scheduled op. data with batch of size {} targeting {}",
time_left,
odata.batch.len(),
odata.target,
);

// Wait until the delay period passes
thread::sleep(time_left);

// `blocks_left` maybe non-zero, so recurse to recheck that all delays are handled.
wait_for_conn_delay(
odata,
chain_time,
max_expected_time_per_block,
latest_height,
)
}
}
}
Loading