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

imp: avoid unnecessary clones and validations when creating ClientId #1015

Merged
merged 8 commits into from
Dec 21, 2023
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,4 @@
- `[ibc-core-host-types]` Introduce `ClientType::build_client_id` which avoids unnecessary validaiton.
([#1014](https://github.com/cosmos/ibc-rs/issues/1014))
- `[ibc-core-host-types]` Optimise `ClientId::new` to avoid unnecessary validaiton and temporary
string allocation. ([#1014](https://github.com/cosmos/ibc-rs/issues/1014))
22 changes: 2 additions & 20 deletions ibc-core/ics02-client/src/handler/create_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use ibc_core_client_types::events::CreateClient;
use ibc_core_client_types::msgs::MsgCreateClient;
use ibc_core_handler_types::error::ContextError;
use ibc_core_handler_types::events::{IbcEvent, MessageEvent};
use ibc_core_host::types::identifiers::ClientId;
use ibc_core_host::{ExecutionContext, ValidationContext};
use ibc_primitives::prelude::*;

Expand All @@ -29,15 +28,7 @@ where

client_state.verify_consensus_state(consensus_state)?;

let client_type = client_state.client_type();

let client_id = ClientId::new(client_type, id_counter).map_err(|e| {
ClientError::ClientIdentifierConstructor {
mina86 marked this conversation as resolved.
Show resolved Hide resolved
client_type: client_state.client_type(),
counter: id_counter,
validation_error: e,
}
})?;
let client_id = client_state.client_type().build_client_id(id_counter);

if ctx.client_state(&client_id).is_ok() {
return Err(ClientError::ClientStateAlreadyExists { client_id }.into());
Expand All @@ -58,18 +49,9 @@ where

// Construct this client's identifier
let id_counter = ctx.client_counter()?;

let client_state = ctx.decode_client_state(client_state)?;

let client_type = client_state.client_type();

let client_id = ClientId::new(client_type.clone(), id_counter).map_err(|e| {
ContextError::from(ClientError::ClientIdentifierConstructor {
client_type: client_type.clone(),
counter: id_counter,
validation_error: e,
})
})?;
let client_id = client_type.build_client_id(id_counter);

client_state.initialise(
ctx.get_client_execution_context(),
Expand Down
10 changes: 0 additions & 10 deletions ibc-core/ics02-client/types/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ use crate::height::Height;
pub enum ClientError {
/// upgrade client error: `{0}`
Upgrade(UpgradeClientError),
/// Client identifier constructor failed for type `{client_type}` with counter `{counter}`, validation error: `{validation_error}`
ClientIdentifierConstructor {
client_type: ClientType,
counter: u64,
validation_error: IdentifierError,
},
/// client is frozen with description: `{description}`
ClientFrozen { description: String },
/// client is not active. Status=`{status}`
Expand Down Expand Up @@ -118,10 +112,6 @@ impl From<&'static str> for ClientError {
impl std::error::Error for ClientError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self {
Self::ClientIdentifierConstructor {
validation_error: e,
..
} => Some(e),
Self::InvalidMsgUpdateClientId(e) => Some(e),
Self::InvalidClientIdentifier(e) => Some(e),
Self::InvalidRawMisbehaviour(e) => Some(e),
Expand Down
4 changes: 2 additions & 2 deletions ibc-core/ics03-connection/types/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,9 @@ mod tests {
let client_type = ClientType::from_str("07-tendermint")
.expect("never fails because it's a valid client type");
let conn_id_on_a = ConnectionId::default();
let client_id_on_a = ClientId::new(client_type.clone(), 0).unwrap();
let client_id_on_a = client_type.build_client_id(0);
let conn_id_on_b = ConnectionId::new(1);
let client_id_on_b = ClientId::new(client_type, 1).unwrap();
let client_id_on_b = client_type.build_client_id(1);
let expected_keys = vec![
"connection_id",
"client_id",
Expand Down
47 changes: 26 additions & 21 deletions ibc-core/ics24-host/types/src/identifiers/client_id.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use core::fmt::{Debug, Display, Error as FmtError, Formatter};
use core::str::FromStr;

use derive_more::Into;
use ibc_primitives::prelude::*;

use super::ClientType;
use crate::error::IdentifierError;
use crate::validate::{validate_client_identifier, validate_client_type};

Expand All @@ -22,27 +20,41 @@
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Into)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Into, derive_more::Display)]
pub struct ClientId(String);

impl ClientId {
/// Builds a new client identifier. Client identifiers are deterministically formed from two
/// elements: a prefix derived from the client type `ctype`, and a monotonically increasing
/// `counter`; these are separated by a dash "-".
/// Builds a new client identifier.
///
/// Client identifiers are deterministically formed from two elements:
/// a prefix derived from the client type `ctype`, and a monotonically
/// increasing `counter`; these are separated by a dash "-".
///
/// See also [`ClientType::build_client_id`](super::ClientType::build_client_id)
/// method.
///
/// # Example
///
/// ```
/// # use ibc_core_host_types::identifiers::ClientId;
/// # use ibc_core_host_types::identifiers::ClientType;
/// # use std::str::FromStr;
/// let tm_client_id = ClientId::new(ClientType::from_str("07-tendermint").unwrap(), 0);
/// assert!(tm_client_id.is_ok());
/// tm_client_id.map(|id| { assert_eq!(&id, "07-tendermint-0") });
/// let client_type = ClientType::from_str("07-tendermint").unwrap();
/// let client_id = &client_type.build_client_id(0);
/// assert_eq!(client_id.as_str(), "07-tendermint-0");
/// ```
pub fn new(client_type: ClientType, counter: u64) -> Result<Self, IdentifierError> {
let prefix = client_type.as_str().trim();
validate_client_type(prefix)?;
let id = format!("{prefix}-{counter}");
Self::from_str(id.as_str())
pub fn new(client_type: &str, counter: u64) -> Result<Self, IdentifierError> {
let client_type = client_type.trim();
validate_client_type(client_type).map(|()| Self::format(client_type, counter))
}

Check warning on line 49 in ibc-core/ics24-host/types/src/identifiers/client_id.rs

View check run for this annotation

Codecov / codecov/patch

ibc-core/ics24-host/types/src/identifiers/client_id.rs#L46-L49

Added lines #L46 - L49 were not covered by tests

pub(super) fn format(client_type: &str, counter: u64) -> Self {
let client_id = format!("{client_type}-{counter}");
if cfg!(debug_assertions) {
validate_client_type(client_type).expect("valid client type");
validate_client_identifier(&client_id).expect("valid client id");
}
Self(client_id)
}

/// Get this identifier as a borrowed `&str`
Expand All @@ -56,13 +68,6 @@
}
}

/// This implementation provides a `to_string` method.
impl Display for ClientId {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(f, "{}", self.0)
}
}

impl FromStr for ClientId {
type Err = IdentifierError;

Expand Down
35 changes: 23 additions & 12 deletions ibc-core/ics24-host/types/src/identifiers/client_type.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! Defines the `ClientType` format, typically used in chain IDs.

use core::fmt::{Display, Error as FmtError, Formatter};
use core::str::FromStr;

use ibc_primitives::prelude::*;

use super::ClientId;
use crate::error::IdentifierError;
use crate::validate::validate_client_type;

Expand All @@ -22,15 +22,32 @@ use crate::validate::validate_client_type;
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
/// Type of the client, depending on the specific consensus algorithm.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)]
pub struct ClientType(String);

impl ClientType {
/// Constructs a new `ClientType` from the given `String` if it ends with a valid client identifier.
pub fn new(s: &str) -> Result<Self, IdentifierError> {
let s_trim = s.trim();
validate_client_type(s_trim)?;
Ok(Self(s_trim.to_string()))
pub fn new(client_type: &str) -> Result<Self, IdentifierError> {
let client_type = client_type.trim();
validate_client_type(client_type).map(|()| Self(client_type.into()))
}

/// Constructs a new [`ClientId`] with this types client type and given
/// `counter`.
///
/// This is equivalent to `ClientId::new(self.as_str(), counter)` but
/// infallible since client type is assumed to be correct.
///
/// ```
/// # use ibc_core_host_types::identifiers::ClientId;
/// # use ibc_core_host_types::identifiers::ClientType;
/// # use std::str::FromStr;
/// let client_type = ClientType::from_str("07-tendermint").unwrap();
/// let client_id = client_type.build_client_id(14);
/// assert_eq!(client_id.as_str(), "07-tendermint-14");
/// ```
pub fn build_client_id(&self, counter: u64) -> ClientId {
ClientId::format(self.as_str(), counter)
}

/// Yields this identifier as a borrowed `&str`
Expand All @@ -46,9 +63,3 @@ impl FromStr for ClientType {
Self::new(s)
}
}

impl Display for ClientType {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(f, "ClientType({})", self.0)
}
}
2 changes: 1 addition & 1 deletion ibc-testkit/src/fixtures/core/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ mod tests {

let client_type = ClientType::from_str("07-tendermint")
.expect("never fails because it's a valid client type");
let client_id = ClientId::new(client_type.clone(), 0).unwrap();
let client_id = client_type.build_client_id(0);
let consensus_height = Height::new(0, 5).unwrap();
let consensus_heights = vec![Height::new(0, 5).unwrap(), Height::new(0, 7).unwrap()];
let header: Any = dummy_new_mock_header(5).into();
Expand Down
4 changes: 2 additions & 2 deletions ibc-testkit/src/relayer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ mod tests {
let client_on_a_for_b_height = Height::new(1, 20).unwrap(); // Should be smaller than `chain_b_start_height`
let num_iterations = 4;

let client_on_a_for_b = ClientId::new(tm_client_type(), 0).unwrap();
let client_on_b_for_a = ClientId::new(mock_client_type(), 0).unwrap();
let client_on_a_for_b = tm_client_type().build_client_id(0);
let client_on_b_for_a = mock_client_type().build_client_id(0);

let chain_id_a = ChainId::new("mockgaiaA-1").unwrap();
let chain_id_b = ChainId::new("mockgaiaB-1").unwrap();
Expand Down
13 changes: 2 additions & 11 deletions ibc-testkit/tests/core/ics02_client/create_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use ibc::core::client::types::msgs::{ClientMsg, MsgCreateClient};
use ibc::core::client::types::Height;
use ibc::core::entrypoint::{execute, validate};
use ibc::core::handler::types::msgs::MsgEnvelope;
use ibc::core::host::types::identifiers::ClientId;
use ibc::core::host::ValidationContext;
use ibc_testkit::fixtures::clients::tendermint::{
dummy_tendermint_header, dummy_tm_client_state_from_header,
Expand Down Expand Up @@ -37,11 +36,7 @@ fn test_create_client_ok() {
let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone()));

let client_type = mock_client_type();

let client_id = {
let id_counter = ctx.client_counter().unwrap();
ClientId::new(client_type.clone(), id_counter).unwrap()
};
let client_id = client_type.build_client_id(ctx.client_counter().unwrap());

let res = validate(&ctx, &router, msg_envelope.clone());

Expand Down Expand Up @@ -69,11 +64,7 @@ fn test_tm_create_client_ok() {
let tm_client_state = dummy_tm_client_state_from_header(tm_header.clone()).into();

let client_type = tm_client_type();

let client_id = {
let id_counter = ctx.client_counter().unwrap();
ClientId::new(client_type.clone(), id_counter).unwrap()
};
let client_id = client_type.build_client_id(ctx.client_counter().unwrap());

let msg = MsgCreateClient::new(
tm_client_state,
Expand Down
22 changes: 11 additions & 11 deletions ibc-testkit/tests/core/ics02_client/update_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fn test_consensus_state_pruning() {

let client_height = Height::new(1, 1).unwrap();

let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);

let mut ctx = MockContextConfig::builder()
.host_id(chain_id.clone())
Expand Down Expand Up @@ -198,7 +198,7 @@ fn test_update_nonexisting_client() {

#[test]
fn test_update_synthetic_tendermint_client_adjacent_ok() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);
let client_height = Height::new(1, 20).unwrap();
let update_height = Height::new(1, 21).unwrap();
let chain_id_b = ChainId::new("mockgaiaB-1").unwrap();
Expand Down Expand Up @@ -252,7 +252,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() {

#[test]
fn test_update_synthetic_tendermint_client_validator_change_ok() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);
let client_height = Height::new(1, 20).unwrap();
let chain_id_b = ChainId::new("mockgaiaB-1").unwrap();

Expand Down Expand Up @@ -344,7 +344,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() {

#[test]
fn test_update_synthetic_tendermint_client_validator_change_fail() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);
let client_height = Height::new(1, 20).unwrap();
let chain_id_b = ChainId::new("mockgaiaB-1").unwrap();

Expand Down Expand Up @@ -428,7 +428,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() {

#[test]
fn test_update_synthetic_tendermint_client_non_adjacent_ok() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);
let client_height = Height::new(1, 20).unwrap();
let update_height = Height::new(1, 21).unwrap();
let chain_id_b = ChainId::new("mockgaiaB-1").unwrap();
Expand Down Expand Up @@ -484,7 +484,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() {

#[test]
fn test_update_synthetic_tendermint_client_duplicate_ok() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);
let client_height = Height::new(1, 20).unwrap();

let ctx_a_chain_id = ChainId::new("mockgaiaA-1").unwrap();
Expand Down Expand Up @@ -610,7 +610,7 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() {

#[test]
fn test_update_synthetic_tendermint_client_lower_height() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);
let client_height = Height::new(1, 20).unwrap();

let client_update_height = Height::new(1, 19).unwrap();
Expand Down Expand Up @@ -765,7 +765,7 @@ fn test_misbehaviour_nonexisting_client() {
/// Misbehaviour evidence consists of equivocal headers.
#[test]
fn test_misbehaviour_synthetic_tendermint_equivocation() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);
let client_height = Height::new(1, 20).unwrap();
let misbehaviour_height = Height::new(1, 21).unwrap();
let chain_id_b = ChainId::new("mockgaiaB-1").unwrap();
Expand Down Expand Up @@ -829,7 +829,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() {

#[test]
fn test_misbehaviour_synthetic_tendermint_bft_time() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);
let client_height = Height::new(1, 20).unwrap();
let misbehaviour_height = Height::new(1, 21).unwrap();
let chain_id_b = ChainId::new("mockgaiaB-1").unwrap();
Expand Down Expand Up @@ -898,7 +898,7 @@ fn test_expired_client() {
let update_height = Height::new(1, 21).unwrap();
let client_height = update_height.sub(3).unwrap();

let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);

let timestamp = Timestamp::now();

Expand Down Expand Up @@ -936,7 +936,7 @@ fn test_client_update_max_clock_drift() {

let client_height = Height::new(1, 20).unwrap();

let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_id = tm_client_type().build_client_id(0);

let timestamp = Timestamp::now();

Expand Down
3 changes: 1 addition & 2 deletions ibc-testkit/tests/core/ics02_client/upgrade_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use ibc::core::entrypoint::{execute, validate};
use ibc::core::handler::types::error::ContextError;
use ibc::core::handler::types::events::{IbcEvent, MessageEvent};
use ibc::core::handler::types::msgs::MsgEnvelope;
use ibc::core::host::types::identifiers::ClientId;
use ibc::core::host::types::path::ClientConsensusStatePath;
use ibc::core::host::ValidationContext;
use ibc::core::primitives::downcast;
Expand All @@ -32,7 +31,7 @@ enum Msg {
}

fn msg_upgrade_client_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture<MsgUpgradeClient> {
let client_id = ClientId::new(mock_client_type(), 0).unwrap();
let client_id = mock_client_type().build_client_id(0);

let ctx_default = MockContext::default();
let ctx_with_client = ctx_default
Expand Down
Loading
Loading