use core::str::FromStr;
use derive_more::{Display, From};
use ibc_primitives::prelude::*;
use crate::identifiers::{ChannelId, ClientId, ConnectionId, PortId, Sequence};
pub const NEXT_CLIENT_SEQUENCE: &str = "nextClientSequence";
pub const NEXT_CONNECTION_SEQUENCE: &str = "nextConnectionSequence";
pub const NEXT_CHANNEL_SEQUENCE: &str = "nextChannelSequence";
pub const CLIENT_PREFIX: &str = "clients";
pub const CLIENT_STATE: &str = "clientState";
pub const CONSENSUS_STATE_PREFIX: &str = "consensusStates";
pub const CONNECTION_PREFIX: &str = "connections";
pub const CHANNEL_PREFIX: &str = "channels";
pub const CHANNEL_END_PREFIX: &str = "channelEnds";
pub const PORT_PREFIX: &str = "ports";
pub const SEQUENCE_PREFIX: &str = "sequences";
pub const NEXT_SEQ_SEND_PREFIX: &str = "nextSequenceSend";
pub const NEXT_SEQ_RECV_PREFIX: &str = "nextSequenceRecv";
pub const NEXT_SEQ_ACK_PREFIX: &str = "nextSequenceAck";
pub const PACKET_COMMITMENT_PREFIX: &str = "commitments";
pub const PACKET_ACK_PREFIX: &str = "acks";
pub const PACKET_RECEIPT_PREFIX: &str = "receipts";
pub const ITERATE_CONSENSUS_STATE_PREFIX: &str = "iterateConsensusStates";
pub const PROCESSED_TIME: &str = "processedTime";
pub const PROCESSED_HEIGHT: &str = "processedHeight";
pub const UPGRADED_IBC_STATE: &str = "upgradedIBCState";
pub const UPGRADED_CLIENT_STATE: &str = "upgradedClient";
pub const UPGRADED_CLIENT_CONSENSUS_STATE: &str = "upgradedConsState";
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, From)]
pub struct PathBytes(Vec<u8>);
impl PathBytes {
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
Self(bytes.as_ref().to_vec())
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn into_vec(self) -> Vec<u8> {
self.0
}
pub fn flatten<T: AsRef<[u8]>>(paths: Vec<T>) -> Self {
let mut bytes = Vec::new();
paths.iter().for_each(|path| {
bytes.extend_from_slice(path.as_ref());
});
Self(bytes)
}
}
impl AsRef<[u8]> for PathBytes {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, From, Display)]
pub enum Path {
NextClientSequence(NextClientSequencePath),
NextConnectionSequence(NextConnectionSequencePath),
NextChannelSequence(NextChannelSequencePath),
ClientState(ClientStatePath),
ClientConsensusState(ClientConsensusStatePath),
ClientUpdateTime(ClientUpdateTimePath),
ClientUpdateHeight(ClientUpdateHeightPath),
ClientConnection(ClientConnectionPath),
Connection(ConnectionPath),
Ports(PortPath),
ChannelEnd(ChannelEndPath),
SeqSend(SeqSendPath),
SeqRecv(SeqRecvPath),
SeqAck(SeqAckPath),
Commitment(CommitmentPath),
Ack(AckPath),
Receipt(ReceiptPath),
UpgradeClientState(UpgradeClientStatePath),
UpgradeConsensusState(UpgradeConsensusStatePath),
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{NEXT_CLIENT_SEQUENCE}")]
pub struct NextClientSequencePath;
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{NEXT_CONNECTION_SEQUENCE}")]
pub struct NextConnectionSequencePath;
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{NEXT_CHANNEL_SEQUENCE}")]
pub struct NextChannelSequencePath;
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display, From)]
#[display(fmt = "{CLIENT_PREFIX}/{_0}/{CLIENT_STATE}")]
pub struct ClientStatePath(pub ClientId);
impl ClientStatePath {
pub fn new(client_id: ClientId) -> ClientStatePath {
ClientStatePath(client_id)
}
pub fn prefix() -> String {
CLIENT_PREFIX.to_string()
}
pub fn leaf() -> String {
CLIENT_STATE.to_string()
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(
fmt = "{CLIENT_PREFIX}/{client_id}/{CONSENSUS_STATE_PREFIX}/{revision_number}-{revision_height}"
)]
pub struct ClientConsensusStatePath {
pub client_id: ClientId,
pub revision_number: u64,
pub revision_height: u64,
}
pub fn full_consensus_state_path(client_id: &ClientId) -> String {
format!("{CLIENT_PREFIX}/{client_id}/{CONSENSUS_STATE_PREFIX}")
}
impl ClientConsensusStatePath {
pub fn new(
client_id: ClientId,
revision_number: u64,
revision_height: u64,
) -> ClientConsensusStatePath {
ClientConsensusStatePath {
client_id,
revision_number,
revision_height,
}
}
pub fn parent(&self) -> String {
full_consensus_state_path(&self.client_id)
}
pub fn leaf(&self) -> String {
format!(
"{CONSENSUS_STATE_PREFIX}/{}-{}",
self.revision_number, self.revision_height
)
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(
fmt = "{CLIENT_PREFIX}/{client_id}/{CONSENSUS_STATE_PREFIX}/{revision_number}-{revision_height}/{PROCESSED_TIME}"
)]
pub struct ClientUpdateTimePath {
pub client_id: ClientId,
pub revision_number: u64,
pub revision_height: u64,
}
impl ClientUpdateTimePath {
pub fn new(client_id: ClientId, revision_number: u64, revision_height: u64) -> Self {
Self {
client_id,
revision_number,
revision_height,
}
}
pub fn parent(&self) -> String {
full_consensus_state_path(&self.client_id)
}
pub fn leaf(&self) -> String {
format!(
"{CONSENSUS_STATE_PREFIX}/{}-{}/{PROCESSED_TIME}",
self.revision_number, self.revision_height
)
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(
fmt = "{CLIENT_PREFIX}/{client_id}/{CONSENSUS_STATE_PREFIX}/{revision_number}-{revision_height}/{PROCESSED_HEIGHT}"
)]
pub struct ClientUpdateHeightPath {
pub client_id: ClientId,
pub revision_number: u64,
pub revision_height: u64,
}
impl ClientUpdateHeightPath {
pub fn new(client_id: ClientId, revision_number: u64, revision_height: u64) -> Self {
Self {
client_id,
revision_number,
revision_height,
}
}
pub fn parent(&self) -> String {
full_consensus_state_path(&self.client_id)
}
pub fn leaf(&self) -> String {
format!(
"{CONSENSUS_STATE_PREFIX}/{}-{}/{PROCESSED_HEIGHT}",
self.revision_number, self.revision_height
)
}
}
pub fn iteration_key(revision_number: u64, revision_height: u64) -> Vec<u8> {
let mut path = Vec::new();
path.extend_from_slice(ITERATE_CONSENSUS_STATE_PREFIX.as_bytes());
path.extend(revision_number.to_be_bytes());
path.extend(revision_height.to_be_bytes());
path
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{CLIENT_PREFIX}/{_0}/{CONNECTION_PREFIX}")]
pub struct ClientConnectionPath(pub ClientId);
impl ClientConnectionPath {
pub fn new(client_id: ClientId) -> ClientConnectionPath {
ClientConnectionPath(client_id)
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{CONNECTION_PREFIX}/{_0}")]
pub struct ConnectionPath(pub ConnectionId);
impl ConnectionPath {
pub fn new(connection_id: &ConnectionId) -> ConnectionPath {
ConnectionPath(connection_id.clone())
}
pub fn prefix() -> String {
CONNECTION_PREFIX.to_string()
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{PORT_PREFIX}/{_0}")]
pub struct PortPath(pub PortId);
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{CHANNEL_END_PREFIX}/{PORT_PREFIX}/{_0}/{CHANNEL_PREFIX}/{_1}")]
pub struct ChannelEndPath(pub PortId, pub ChannelId);
impl ChannelEndPath {
pub fn new(port_id: &PortId, channel_id: &ChannelId) -> ChannelEndPath {
ChannelEndPath(port_id.clone(), channel_id.clone())
}
pub fn prefix() -> String {
CHANNEL_END_PREFIX.to_string()
}
fn full_sequences_path(&self, prefix: &str) -> String {
format!(
"{prefix}/{PORT_PREFIX}/{}/{CHANNEL_PREFIX}/{}/{SEQUENCE_PREFIX}",
self.0, self.1,
)
}
pub fn commitments_path(&self) -> String {
self.full_sequences_path(PACKET_COMMITMENT_PREFIX)
}
pub fn acks_path(&self) -> String {
self.full_sequences_path(PACKET_ACK_PREFIX)
}
pub fn receipts_path(&self) -> String {
self.full_sequences_path(PACKET_RECEIPT_PREFIX)
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{NEXT_SEQ_SEND_PREFIX}/{PORT_PREFIX}/{_0}/{CHANNEL_PREFIX}/{_1}")]
pub struct SeqSendPath(pub PortId, pub ChannelId);
impl SeqSendPath {
pub fn new(port_id: &PortId, channel_id: &ChannelId) -> SeqSendPath {
SeqSendPath(port_id.clone(), channel_id.clone())
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{NEXT_SEQ_RECV_PREFIX}/{PORT_PREFIX}/{_0}/{CHANNEL_PREFIX}/{_1}")]
pub struct SeqRecvPath(pub PortId, pub ChannelId);
impl SeqRecvPath {
pub fn new(port_id: &PortId, channel_id: &ChannelId) -> SeqRecvPath {
SeqRecvPath(port_id.clone(), channel_id.clone())
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{NEXT_SEQ_ACK_PREFIX}/{PORT_PREFIX}/{_0}/{CHANNEL_PREFIX}/{_1}")]
pub struct SeqAckPath(pub PortId, pub ChannelId);
impl SeqAckPath {
pub fn new(port_id: &PortId, channel_id: &ChannelId) -> SeqAckPath {
SeqAckPath(port_id.clone(), channel_id.clone())
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(
fmt = "{PACKET_COMMITMENT_PREFIX}/{PORT_PREFIX}/{port_id}/{CHANNEL_PREFIX}/{channel_id}/{SEQUENCE_PREFIX}/{sequence}"
)]
pub struct CommitmentPath {
pub port_id: PortId,
pub channel_id: ChannelId,
pub sequence: Sequence,
}
impl CommitmentPath {
pub fn new(port_id: &PortId, channel_id: &ChannelId, sequence: Sequence) -> CommitmentPath {
CommitmentPath {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
sequence,
}
}
pub fn prefix() -> String {
PACKET_COMMITMENT_PREFIX.to_string()
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(
fmt = "{PACKET_ACK_PREFIX}/{PORT_PREFIX}/{port_id}/{CHANNEL_PREFIX}/{channel_id}/{SEQUENCE_PREFIX}/{sequence}"
)]
pub struct AckPath {
pub port_id: PortId,
pub channel_id: ChannelId,
pub sequence: Sequence,
}
impl AckPath {
pub fn new(port_id: &PortId, channel_id: &ChannelId, sequence: Sequence) -> AckPath {
AckPath {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
sequence,
}
}
pub fn prefix() -> String {
PACKET_ACK_PREFIX.to_string()
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(
fmt = "{PACKET_RECEIPT_PREFIX}/{PORT_PREFIX}/{port_id}/{CHANNEL_PREFIX}/{channel_id}/{SEQUENCE_PREFIX}/{sequence}"
)]
pub struct ReceiptPath {
pub port_id: PortId,
pub channel_id: ChannelId,
pub sequence: Sequence,
}
impl ReceiptPath {
pub fn new(port_id: &PortId, channel_id: &ChannelId, sequence: Sequence) -> ReceiptPath {
ReceiptPath {
port_id: port_id.clone(),
channel_id: channel_id.clone(),
sequence,
}
}
pub fn prefix() -> String {
PACKET_RECEIPT_PREFIX.to_string()
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{upgrade_path}/{height}/{UPGRADED_CLIENT_STATE}")]
pub struct UpgradeClientStatePath {
pub upgrade_path: String,
pub height: u64,
}
impl UpgradeClientStatePath {
pub fn new_with_default_path(height: u64) -> Self {
Self {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height,
}
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display(fmt = "{upgrade_path}/{height}/{UPGRADED_CLIENT_CONSENSUS_STATE}")]
pub struct UpgradeConsensusStatePath {
pub upgrade_path: String,
pub height: u64,
}
impl UpgradeConsensusStatePath {
pub fn new_with_default_path(height: u64) -> Self {
Self {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height,
}
}
}
#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum SubPath {
Channels(ChannelId),
Sequences(Sequence),
}
impl Path {
pub fn is_provable(&self) -> bool {
!matches!(&self, Path::ClientConnection(_) | Path::Ports(_))
}
pub fn into_bytes(self) -> Vec<u8> {
self.to_string().into_bytes()
}
}
#[derive(Debug, displaydoc::Display)]
pub enum PathError {
ParseFailure { path: String },
}
#[cfg(feature = "std")]
impl std::error::Error for PathError {}
impl FromStr for Path {
type Err = PathError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let components: Vec<&str> = s.split('/').collect();
parse_next_sequence(&components)
.or_else(|| parse_client_paths(&components))
.or_else(|| parse_connections(&components))
.or_else(|| parse_ports(&components))
.or_else(|| parse_channel_ends(&components))
.or_else(|| parse_seqs(&components))
.or_else(|| parse_commitments(&components))
.or_else(|| parse_acks(&components))
.or_else(|| parse_receipts(&components))
.or_else(|| parse_upgrade_client_state(&components))
.or_else(|| parse_upgrade_consensus_state(&components))
.ok_or(PathError::ParseFailure {
path: s.to_string(),
})
}
}
fn parse_next_sequence(components: &[&str]) -> Option<Path> {
if components.len() != 1 {
return None;
}
match *components.first()? {
NEXT_CLIENT_SEQUENCE => Some(NextClientSequencePath.into()),
NEXT_CONNECTION_SEQUENCE => Some(NextConnectionSequencePath.into()),
NEXT_CHANNEL_SEQUENCE => Some(NextChannelSequencePath.into()),
_ => None,
}
}
fn parse_client_paths(components: &[&str]) -> Option<Path> {
let first = *components.first()?;
if first != CLIENT_PREFIX {
return None;
}
let client_id = ClientId::from_str(components[1]).ok()?;
if components.len() == 3 {
match components[2] {
CLIENT_STATE => Some(ClientStatePath(client_id).into()),
CONNECTION_PREFIX => Some(ClientConnectionPath(client_id).into()),
_ => None,
}
} else if components.len() == 4 || components.len() == 5 {
match components[2] {
CONSENSUS_STATE_PREFIX => {}
_ => return None,
}
let epoch_height: Vec<&str> = components[3].split('-').collect();
if epoch_height.len() != 2 {
return None;
}
let revision_number = epoch_height[0];
let revision_height = epoch_height[1];
let revision_number = revision_number.parse::<u64>().ok()?;
let revision_height = revision_height.parse::<u64>().ok()?;
match components.len() {
4 => Some(
ClientConsensusStatePath {
client_id,
revision_number,
revision_height,
}
.into(),
),
5 => match components[4] {
PROCESSED_TIME => Some(
ClientUpdateTimePath {
client_id,
revision_number,
revision_height,
}
.into(),
),
PROCESSED_HEIGHT => Some(
ClientUpdateHeightPath {
client_id,
revision_number,
revision_height,
}
.into(),
),
_ => None,
},
_ => None,
}
} else {
None
}
}
fn parse_connections(components: &[&str]) -> Option<Path> {
if components.len() != 2 {
return None;
}
let first = *components.first()?;
if first != CONNECTION_PREFIX {
return None;
}
let connection_id = *components.last()?;
let connection_id = ConnectionId::from_str(connection_id).ok()?;
Some(ConnectionPath(connection_id).into())
}
fn parse_ports(components: &[&str]) -> Option<Path> {
if components.len() != 2 {
return None;
}
let first = *components.first()?;
if first != PORT_PREFIX {
return None;
}
let port_id = *components.last()?;
let port_id = PortId::from_str(port_id).ok()?;
Some(PortPath(port_id).into())
}
fn parse_channels(components: &[&str]) -> Option<SubPath> {
if components.len() != 2 {
return None;
}
let first = *components.first()?;
if first != CHANNEL_PREFIX {
return None;
}
let channel_id = *components.last()?;
let channel_id = ChannelId::from_str(channel_id).ok()?;
Some(SubPath::Channels(channel_id))
}
fn parse_sequences(components: &[&str]) -> Option<SubPath> {
if components.len() != 2 {
return None;
}
let first = *components.first()?;
if first != SEQUENCE_PREFIX {
return None;
}
let sequence_number = *components.last()?;
match Sequence::from_str(sequence_number) {
Ok(seq) => Some(SubPath::Sequences(seq)),
Err(_) => None,
}
}
fn parse_channel_ends(components: &[&str]) -> Option<Path> {
if components.len() != 5 {
return None;
}
let first = *components.first()?;
if first != CHANNEL_END_PREFIX {
return None;
}
let port = parse_ports(&components[1..=2]);
let channel = parse_channels(&components[3..=4]);
let Some(Path::Ports(PortPath(port_id))) = port else {
return None;
};
let Some(SubPath::Channels(channel_id)) = channel else {
return None;
};
Some(ChannelEndPath(port_id, channel_id).into())
}
fn parse_seqs(components: &[&str]) -> Option<Path> {
if components.len() != 5 {
return None;
}
let first = *components.first()?;
let port = parse_ports(&components[1..=2]);
let channel = parse_channels(&components[3..=4]);
let Some(Path::Ports(PortPath(port_id))) = port else {
return None;
};
let Some(SubPath::Channels(channel_id)) = channel else {
return None;
};
match first {
NEXT_SEQ_SEND_PREFIX => Some(SeqSendPath(port_id, channel_id).into()),
NEXT_SEQ_RECV_PREFIX => Some(SeqRecvPath(port_id, channel_id).into()),
NEXT_SEQ_ACK_PREFIX => Some(SeqAckPath(port_id, channel_id).into()),
_ => None,
}
}
fn parse_commitments(components: &[&str]) -> Option<Path> {
if components.len() != 7 {
return None;
}
let first = *components.first()?;
if first != PACKET_COMMITMENT_PREFIX {
return None;
}
let port = parse_ports(&components[1..=2]);
let channel = parse_channels(&components[3..=4]);
let sequence = parse_sequences(&components[5..]);
let Some(Path::Ports(PortPath(port_id))) = port else {
return None;
};
let Some(SubPath::Channels(channel_id)) = channel else {
return None;
};
let Some(SubPath::Sequences(sequence)) = sequence else {
return None;
};
Some(
CommitmentPath {
port_id,
channel_id,
sequence,
}
.into(),
)
}
fn parse_acks(components: &[&str]) -> Option<Path> {
if components.len() != 7 {
return None;
}
let first = *components.first()?;
if first != PACKET_ACK_PREFIX {
return None;
}
let port = parse_ports(&components[1..=2]);
let channel = parse_channels(&components[3..=4]);
let sequence = parse_sequences(&components[5..]);
let Some(Path::Ports(PortPath(port_id))) = port else {
return None;
};
let Some(SubPath::Channels(channel_id)) = channel else {
return None;
};
let Some(SubPath::Sequences(sequence)) = sequence else {
return None;
};
Some(
AckPath {
port_id,
channel_id,
sequence,
}
.into(),
)
}
fn parse_receipts(components: &[&str]) -> Option<Path> {
if components.len() != 7 {
return None;
}
let first = *components.first()?;
if first != PACKET_RECEIPT_PREFIX {
return None;
}
let port = parse_ports(&components[1..=2]);
let channel = parse_channels(&components[3..=4]);
let sequence = parse_sequences(&components[5..]);
let Some(Path::Ports(PortPath(port_id))) = port else {
return None;
};
let Some(SubPath::Channels(channel_id)) = channel else {
return None;
};
let Some(SubPath::Sequences(sequence)) = sequence else {
return None;
};
Some(
ReceiptPath {
port_id,
channel_id,
sequence,
}
.into(),
)
}
fn parse_upgrade_client_state(components: &[&str]) -> Option<Path> {
if components.len() != 3 {
return None;
}
let last = *components.last()?;
if last != UPGRADED_CLIENT_STATE {
return None;
}
let upgrade_path = components.first()?.to_string();
let height = u64::from_str(components[1]).ok()?;
Some(
UpgradeClientStatePath {
upgrade_path,
height,
}
.into(),
)
}
fn parse_upgrade_consensus_state(components: &[&str]) -> Option<Path> {
if components.len() != 3 {
return None;
}
let last = *components.last()?;
if last != UPGRADED_CLIENT_CONSENSUS_STATE {
return None;
}
let upgrade_path = components.first()?.to_string();
let height = u64::from_str(components[1]).ok()?;
Some(
UpgradeConsensusStatePath {
upgrade_path,
height,
}
.into(),
)
}
#[cfg(test)]
mod tests {
use super::*;
const DEFAULT_CLIENT_ID_STR: &str = "07-tendermint-0";
impl ClientId {
pub fn new_dummy() -> Self {
ClientId::from_str(DEFAULT_CLIENT_ID_STR)
.expect("should not fail since we use a valid client id")
}
}
#[rstest::rstest]
#[case(NEXT_CLIENT_SEQUENCE, Path::NextClientSequence(NextClientSequencePath))]
#[case(
NEXT_CONNECTION_SEQUENCE,
Path::NextConnectionSequence(NextConnectionSequencePath)
)]
#[case(
NEXT_CHANNEL_SEQUENCE,
Path::NextChannelSequence(NextChannelSequencePath)
)]
#[case(
"clients/07-tendermint-0/clientState",
Path::ClientState(ClientStatePath(ClientId::new_dummy()))
)]
#[case(
"clients/07-tendermint-0/consensusStates/15-31",
Path::ClientConsensusState(ClientConsensusStatePath {
client_id: ClientId::new_dummy(),
revision_number: 15,
revision_height: 31,
})
)]
#[case(
"clients/07-tendermint-0/consensusStates/15-31/processedTime",
Path::ClientUpdateTime(ClientUpdateTimePath {
client_id: ClientId::new_dummy(),
revision_number: 15,
revision_height: 31,
})
)]
#[case(
"clients/07-tendermint-0/consensusStates/15-31/processedHeight",
Path::ClientUpdateHeight(ClientUpdateHeightPath {
client_id: ClientId::new_dummy(),
revision_number: 15,
revision_height: 31,
})
)]
#[case(
"clients/07-tendermint-0/connections",
Path::ClientConnection(ClientConnectionPath(ClientId::new_dummy()))
)]
#[case(
"connections/connection-0",
Path::Connection(ConnectionPath(ConnectionId::zero()))
)]
#[case("ports/transfer", Path::Ports(PortPath(PortId::transfer())))]
#[case(
"channelEnds/ports/transfer/channels/channel-0",
Path::ChannelEnd(ChannelEndPath(PortId::transfer(), ChannelId::zero()))
)]
#[case(
"nextSequenceSend/ports/transfer/channels/channel-0",
Path::SeqSend(SeqSendPath(PortId::transfer(), ChannelId::zero()))
)]
#[case(
"nextSequenceRecv/ports/transfer/channels/channel-0",
Path::SeqRecv(SeqRecvPath(PortId::transfer(), ChannelId::zero()))
)]
#[case(
"nextSequenceAck/ports/transfer/channels/channel-0",
Path::SeqAck(SeqAckPath(PortId::transfer(), ChannelId::zero()))
)]
#[case(
"commitments/ports/transfer/channels/channel-0/sequences/0",
Path::Commitment(CommitmentPath {
port_id: PortId::transfer(),
channel_id: ChannelId::zero(),
sequence: Sequence::from(0),
})
)]
#[case(
"acks/ports/transfer/channels/channel-0/sequences/0",
Path::Ack(AckPath {
port_id: PortId::transfer(),
channel_id: ChannelId::zero(),
sequence: Sequence::from(0),
})
)]
#[case(
"receipts/ports/transfer/channels/channel-0/sequences/0",
Path::Receipt(ReceiptPath {
port_id: PortId::transfer(),
channel_id: ChannelId::zero(),
sequence: Sequence::from(0),
})
)]
#[case(
"upgradedIBCState/0/upgradedClient",
Path::UpgradeClientState(UpgradeClientStatePath {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height: 0,
})
)]
#[case(
"upgradedIBCState/0/upgradedConsState",
Path::UpgradeConsensusState(UpgradeConsensusStatePath {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height: 0,
})
)]
fn test_successful_parsing(#[case] path_str: &str, #[case] path: Path) {
assert_eq!(Path::from_str(path_str).expect("no error"), path);
assert_eq!(path_str, path.to_string());
}
#[rstest::rstest]
#[case("clients/clientType")]
#[case("channels/channel-0")]
#[case("sequences/0")]
fn test_failure_parsing(#[case] path_str: &str) {
assert!(Path::from_str(path_str).is_err());
}
#[test]
fn test_parse_client_paths_fn() {
let path = "clients/07-tendermint-0/clientState";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_client_paths(&components),
Some(Path::ClientState(ClientStatePath(ClientId::new_dummy())))
);
let path = "clients/07-tendermint-0/consensusStates/15-31";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_client_paths(&components),
Some(Path::ClientConsensusState(ClientConsensusStatePath {
client_id: ClientId::new_dummy(),
revision_number: 15,
revision_height: 31,
}))
);
}
#[test]
fn test_parse_client_update_paths_fn() {
let path = "clients/07-tendermint-0/consensusStates/15-31/processedTime";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_client_paths(&components),
Some(Path::ClientUpdateTime(ClientUpdateTimePath {
client_id: ClientId::new_dummy(),
revision_number: 15,
revision_height: 31,
}))
);
let path = "clients/07-tendermint-0/consensusStates/15-31/processedHeight";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_client_paths(&components),
Some(Path::ClientUpdateHeight(ClientUpdateHeightPath {
client_id: ClientId::new_dummy(),
revision_number: 15,
revision_height: 31,
}))
);
}
#[test]
fn test_parse_connections_fn() {
let path = "connections/connection-0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_connections(&components),
Some(Path::Connection(ConnectionPath(ConnectionId::zero()))),
);
}
#[test]
fn test_parse_ports_fn() {
let path = "ports/transfer";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_ports(&components),
Some(Path::Ports(PortPath(PortId::transfer()))),
);
}
#[test]
fn test_parse_channels_fn() {
let path = "channels/channel-0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_channels(&components),
Some(SubPath::Channels(ChannelId::zero())),
);
}
#[test]
fn test_parse_sequences_fn() {
let path = "sequences/0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_sequences(&components),
Some(SubPath::Sequences(Sequence::from(0)))
);
}
#[test]
fn test_parse_channel_ends_fn() {
let path = "channelEnds/ports/transfer/channels/channel-0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_channel_ends(&components),
Some(Path::ChannelEnd(ChannelEndPath(
PortId::transfer(),
ChannelId::zero()
))),
);
}
#[test]
fn test_parse_seqs_fn() {
let path = "nextSequenceSend/ports/transfer/channels/channel-0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_seqs(&components),
Some(Path::SeqSend(SeqSendPath(
PortId::transfer(),
ChannelId::zero()
))),
);
let path = "nextSequenceRecv/ports/transfer/channels/channel-0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_seqs(&components),
Some(Path::SeqRecv(SeqRecvPath(
PortId::transfer(),
ChannelId::zero()
))),
);
let path = "nextSequenceAck/ports/transfer/channels/channel-0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_seqs(&components),
Some(Path::SeqAck(SeqAckPath(
PortId::transfer(),
ChannelId::zero()
))),
);
}
#[test]
fn test_parse_commitments_fn() {
let path = "commitments/ports/transfer/channels/channel-0/sequences/0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_commitments(&components),
Some(Path::Commitment(CommitmentPath {
port_id: PortId::transfer(),
channel_id: ChannelId::zero(),
sequence: Sequence::from(0),
})),
);
}
#[test]
fn test_parse_acks_fn() {
let path = "acks/ports/transfer/channels/channel-0/sequences/0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_acks(&components),
Some(Path::Ack(AckPath {
port_id: PortId::transfer(),
channel_id: ChannelId::zero(),
sequence: Sequence::from(0),
})),
);
}
#[test]
fn test_parse_receipts_fn() {
let path = "receipts/ports/transfer/channels/channel-0/sequences/0";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_receipts(&components),
Some(Path::Receipt(ReceiptPath {
port_id: PortId::transfer(),
channel_id: ChannelId::zero(),
sequence: Sequence::from(0),
})),
);
}
#[test]
fn test_parse_upgrade_client_state_fn() {
let path = "upgradedIBCState/0/upgradedClient";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_upgrade_client_state(&components),
Some(Path::UpgradeClientState(UpgradeClientStatePath {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height: 0,
})),
);
}
#[test]
fn test_parse_upgrade_consensus_state_fn() {
let path = "upgradedIBCState/0/upgradedConsState";
let components: Vec<&str> = path.split('/').collect();
assert_eq!(
parse_upgrade_consensus_state(&components),
Some(Path::UpgradeConsensusState(UpgradeConsensusStatePath {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height: 0,
})),
)
}
}