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 upgrade path check #1298

Merged
merged 12 commits into from
Aug 9, 2024
65 changes: 44 additions & 21 deletions ibc-clients/ics07-tendermint/src/client_state/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
use ibc_core_commitment_types::proto::ics23::{HostFunctionsManager, HostFunctionsProvider};
use ibc_core_commitment_types::specs::ProofSpecs;
use ibc_core_host::types::identifiers::ClientType;
use ibc_core_host::types::path::{Path, PathBytes, UpgradeClientPath};
use ibc_core_host::types::path::{
Path, PathBytes, UpgradeClientStatePath, UpgradeConsensusStatePath,
};
use ibc_primitives::prelude::*;
use ibc_primitives::proto::Any;
use ibc_primitives::ToVec;
Expand Down Expand Up @@ -46,20 +48,51 @@
) -> Result<(), ClientError> {
let last_height = self.latest_height().revision_height();

let upgrade_client_path_bytes = self.serialize_path(Path::UpgradeClient(
UpgradeClientPath::UpgradedClientState(last_height),
))?;

let upgrade_consensus_path_bytes = self.serialize_path(Path::UpgradeClient(
UpgradeClientPath::UpgradedClientConsensusState(last_height),
))?;
// The client state's upgrade path vector needs to parsed into a tuple in the form
// of `(upgrade_path_prefix, upgrade_path)`. Given the length of the client
// state's upgrade path vector, the following determinations are made:
// 1: The commitment prefix is left empty and the upgrade path is used as-is.
// 2: The commitment prefix and upgrade path are both taken as-is.
let upgrade_path = &self.inner().upgrade_path;
let (upgrade_path_prefix, upgrade_path) = match upgrade_path.len() {
0 => {
return Err(UpgradeClientError::InvalidUpgradePath {
reason: "no upgrade path has been set".to_string(),
}
.into());
}
1 => (CommitmentPrefix::empty(), upgrade_path[0].clone()),
2 => (
upgrade_path[0].as_bytes().to_vec().into(),
upgrade_path[1].clone(),

Check warning on line 67 in ibc-clients/ics07-tendermint/src/client_state/common.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/ics07-tendermint/src/client_state/common.rs#L51-L67

Added lines #L51 - L67 were not covered by tests
),
_ => {
return Err(UpgradeClientError::InvalidUpgradePath {
reason: "upgrade path is too long".to_string(),
}

Check warning on line 72 in ibc-clients/ics07-tendermint/src/client_state/common.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/ics07-tendermint/src/client_state/common.rs#L69-L72

Added lines #L69 - L72 were not covered by tests
.into());
}
};

let upgrade_client_path_bytes =
self.serialize_path(Path::UpgradeClientState(UpgradeClientStatePath {

Check warning on line 78 in ibc-clients/ics07-tendermint/src/client_state/common.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/ics07-tendermint/src/client_state/common.rs#L74-L78

Added lines #L74 - L78 were not covered by tests
upgrade_path: upgrade_path.clone(),
height: last_height,
}))?;

let upgrade_consensus_path_bytes =

Check warning on line 83 in ibc-clients/ics07-tendermint/src/client_state/common.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/ics07-tendermint/src/client_state/common.rs#L80-L83

Added lines #L80 - L83 were not covered by tests
self.serialize_path(Path::UpgradeConsensusState(UpgradeConsensusStatePath {
upgrade_path,
height: last_height,
}))?;

Check warning on line 87 in ibc-clients/ics07-tendermint/src/client_state/common.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/ics07-tendermint/src/client_state/common.rs#L87

Added line #L87 was not covered by tests

verify_upgrade_client::<HostFunctionsManager>(
self.inner(),
upgraded_client_state,
upgraded_consensus_state,
proof_upgrade_client,
proof_upgrade_consensus_state,
upgrade_path_prefix,

Check warning on line 95 in ibc-clients/ics07-tendermint/src/client_state/common.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/ics07-tendermint/src/client_state/common.rs#L95

Added line #L95 was not covered by tests
upgrade_client_path_bytes,
upgrade_consensus_path_bytes,
root,
Expand Down Expand Up @@ -163,6 +196,7 @@
upgraded_consensus_state: Any,
proof_upgrade_client: CommitmentProofBytes,
proof_upgrade_consensus_state: CommitmentProofBytes,
upgrade_path_prefix: CommitmentPrefix,
upgrade_client_path_bytes: PathBytes,
upgrade_consensus_path_bytes: PathBytes,
root: &CommitmentRoot,
Expand All @@ -181,22 +215,11 @@
// the height
if latest_height >= upgraded_tm_client_state_height {
Err(UpgradeClientError::LowUpgradeHeight {
upgraded_height: latest_height,
client_height: upgraded_tm_client_state_height,
upgraded_height: upgraded_tm_client_state_height,
client_height: latest_height,
})?
}

// Check to see if the upgrade path is set
let mut upgrade_path = client_state.upgrade_path.clone();

if upgrade_path.pop().is_none() {
return Err(ClientError::ClientSpecific {
description: "cannot upgrade client as no upgrade path has been set".to_string(),
});
};

let upgrade_path_prefix = CommitmentPrefix::from(upgrade_path[0].clone().into_bytes());

// Verify the proof of the upgraded client state
verify_membership::<H>(
&client_state.proof_specs,
Expand Down
2 changes: 2 additions & 0 deletions ibc-core/ics02-client/types/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ pub enum UpgradeClientError {
upgraded_height: Height,
client_height: Height,
},
/// Invalid upgrade path: `{reason}`
InvalidUpgradePath { reason: String },
/// invalid upgrade proposal: `{reason}`
InvalidUpgradeProposal { reason: String },
/// invalid upgrade plan: `{reason}`
Expand Down
10 changes: 5 additions & 5 deletions ibc-core/ics24-host/cosmos/src/upgrade_proposal/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use ibc_core_client_context::ClientValidationContext;
use ibc_core_client_types::error::UpgradeClientError;
use ibc_core_host_types::path::UpgradeClientPath;
use ibc_core_host_types::path::{UpgradeClientStatePath, UpgradeConsensusStatePath};

use super::Plan;

Expand All @@ -28,13 +28,13 @@ pub trait UpgradeValidationContext {
/// Returns the upgraded client state at the specified upgrade path.
fn upgraded_client_state(
&self,
upgrade_path: &UpgradeClientPath,
upgrade_path: &UpgradeClientStatePath,
) -> Result<UpgradedClientStateRef<Self>, UpgradeClientError>;

/// Returns the upgraded consensus state at the specified upgrade path.
fn upgraded_consensus_state(
&self,
upgrade_path: &UpgradeClientPath,
upgrade_path: &UpgradeConsensusStatePath,
) -> Result<UpgradedConsensusStateRef<Self>, UpgradeClientError>;
}

Expand All @@ -50,14 +50,14 @@ pub trait UpgradeExecutionContext: UpgradeValidationContext {
/// Stores the upgraded client state at the specified upgrade path.
fn store_upgraded_client_state(
&mut self,
upgrade_path: UpgradeClientPath,
upgrade_path: UpgradeClientStatePath,
client_state: UpgradedClientStateRef<Self>,
) -> Result<(), UpgradeClientError>;

/// Stores the upgraded consensus state at the specified upgrade path.
fn store_upgraded_consensus_state(
&mut self,
upgrade_path: UpgradeClientPath,
upgrade_path: UpgradeConsensusStatePath,
consensus_state: UpgradedConsensusStateRef<Self>,
) -> Result<(), UpgradeClientError>;
}
4 changes: 2 additions & 2 deletions ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ibc_client_tendermint::types::ClientState as TmClientState;
use ibc_core_client_types::error::UpgradeClientError;
use ibc_core_host_types::path::UpgradeClientPath;
use ibc_core_host_types::path::UpgradeClientStatePath;
use ibc_primitives::prelude::*;
use tendermint::abci::Event as TmEvent;

Expand Down Expand Up @@ -37,7 +37,7 @@

ctx.schedule_upgrade(plan.clone())?;

let upgraded_client_state_path = UpgradeClientPath::UpgradedClientState(plan.height);
let upgraded_client_state_path = UpgradeClientStatePath::new_with_default_path(plan.height);

Check warning on line 40 in ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs

View check run for this annotation

Codecov / codecov/patch

ibc-core/ics24-host/cosmos/src/upgrade_proposal/handler.rs#L40

Added line #L40 was not covered by tests

ctx.store_upgraded_client_state(upgraded_client_state_path, client_state.into())?;

Expand Down
133 changes: 104 additions & 29 deletions ibc-core/ics24-host/types/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@
Commitment(CommitmentPath),
Ack(AckPath),
Receipt(ReceiptPath),
UpgradeClient(UpgradeClientPath),
UpgradeClientState(UpgradeClientStatePath),
UpgradeConsensusState(UpgradeConsensusStatePath),
}

#[cfg_attr(
Expand Down Expand Up @@ -693,13 +694,51 @@
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
/// Paths that are specific for client upgrades.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
pub enum UpgradeClientPath {
#[display(fmt = "{UPGRADED_IBC_STATE}/{_0}/{UPGRADED_CLIENT_STATE}")]
UpgradedClientState(u64),
#[display(fmt = "{UPGRADED_IBC_STATE}/{_0}/{UPGRADED_CLIENT_CONSENSUS_STATE}")]
UpgradedClientConsensusState(u64),
#[display(fmt = "{upgrade_path}/{height}/{UPGRADED_CLIENT_STATE}")]
pub struct UpgradeClientStatePath {
pub upgrade_path: String,
pub height: u64,
}

impl UpgradeClientStatePath {
/// Create with the default upgrade path
pub fn new_with_default_path(height: u64) -> Self {
Self {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height,
}
}

Check warning on line 711 in ibc-core/ics24-host/types/src/path.rs

View check run for this annotation

Codecov / codecov/patch

ibc-core/ics24-host/types/src/path.rs#L706-L711

Added lines #L706 - L711 were not covered by tests
}

#[cfg_attr(
feature = "parity-scale-codec",
derive(
parity_scale_codec::Encode,
parity_scale_codec::Decode,
scale_info::TypeInfo

Check warning on line 719 in ibc-core/ics24-host/types/src/path.rs

View check run for this annotation

Codecov / codecov/patch

ibc-core/ics24-host/types/src/path.rs#L719

Added line #L719 was not covered by tests
)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)

Check warning on line 724 in ibc-core/ics24-host/types/src/path.rs

View check run for this annotation

Codecov / codecov/patch

ibc-core/ics24-host/types/src/path.rs#L724

Added line #L724 was not covered by tests
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]

Check warning on line 726 in ibc-core/ics24-host/types/src/path.rs

View check run for this annotation

Codecov / codecov/patch

ibc-core/ics24-host/types/src/path.rs#L726

Added line #L726 was not covered by tests
#[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 {
/// Create with the default upgrade path
pub fn new_with_default_path(height: u64) -> Self {
Self {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height,
}
}

Check warning on line 741 in ibc-core/ics24-host/types/src/path.rs

View check run for this annotation

Codecov / codecov/patch

ibc-core/ics24-host/types/src/path.rs#L736-L741

Added lines #L736 - L741 were not covered by tests
}

#[cfg_attr(
Expand Down Expand Up @@ -760,7 +799,8 @@
.or_else(|| parse_commitments(&components))
.or_else(|| parse_acks(&components))
.or_else(|| parse_receipts(&components))
.or_else(|| parse_upgrades(&components))
.or_else(|| parse_upgrade_client_state(&components))
.or_else(|| parse_upgrade_consensus_state(&components))
.ok_or(PathError::ParseFailure {
path: s.to_string(),
})
Expand Down Expand Up @@ -1084,28 +1124,52 @@
)
}

fn parse_upgrades(components: &[&str]) -> Option<Path> {
fn parse_upgrade_client_state(components: &[&str]) -> Option<Path> {
if components.len() != 3 {
return None;
}

let first = *components.first()?;
let last = *components.last()?;

if first != UPGRADED_IBC_STATE {
if last != UPGRADED_CLIENT_STATE {
return None;
}

let last = *components.last()?;
let upgrade_path = components.first()?.to_string();

let height = components[1].parse::<u64>().ok()?;
let height = u64::from_str(components[1]).ok()?;

match last {
UPGRADED_CLIENT_STATE => Some(UpgradeClientPath::UpgradedClientState(height).into()),
UPGRADED_CLIENT_CONSENSUS_STATE => {
Some(UpgradeClientPath::UpgradedClientConsensusState(height).into())
Some(
UpgradeClientStatePath {
upgrade_path,
height,
}
_ => None,
.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;

Check warning on line 1159 in ibc-core/ics24-host/types/src/path.rs

View check run for this annotation

Codecov / codecov/patch

ibc-core/ics24-host/types/src/path.rs#L1159

Added line #L1159 was not covered by tests
}

let upgrade_path = components.first()?.to_string();

let height = u64::from_str(components[1]).ok()?;

Some(
UpgradeConsensusStatePath {
upgrade_path,
height,
}
.into(),
)
}

#[cfg(test)]
Expand Down Expand Up @@ -1207,11 +1271,17 @@
)]
#[case(
"upgradedIBCState/0/upgradedClient",
Path::UpgradeClient(UpgradeClientPath::UpgradedClientState(0))
Path::UpgradeClientState(UpgradeClientStatePath {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height: 0,
})
)]
#[case(
"upgradedIBCState/0/upgradedConsState",
Path::UpgradeClient(UpgradeClientPath::UpgradedClientConsensusState(0))
Path::UpgradeConsensusState(UpgradeConsensusStatePath {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height: 0,
})
)]
fn test_successful_parsing(#[case] path_str: &str, #[case] path: Path) {
// can be parsed into Path
Expand Down Expand Up @@ -1419,25 +1489,30 @@
}

#[test]
fn test_parse_upgrades_fn() {
fn test_parse_upgrade_client_state_fn() {
let path = "upgradedIBCState/0/upgradedClient";
let components: Vec<&str> = path.split('/').collect();

assert_eq!(
parse_upgrades(&components),
Some(Path::UpgradeClient(UpgradeClientPath::UpgradedClientState(
0
))),
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_upgrades(&components),
Some(Path::UpgradeClient(
UpgradeClientPath::UpgradedClientConsensusState(0)
)),
parse_upgrade_consensus_state(&components),
Some(Path::UpgradeConsensusState(UpgradeConsensusStatePath {
upgrade_path: UPGRADED_IBC_STATE.to_string(),
height: 0,
})),
)
}
}
Loading
Loading