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

Include Client's Input when an error occurs. #51

Merged
merged 6 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
change error
Signed-off-by: Naohiro Yoshida <naohiro.yoshida@datachain.jp>
  • Loading branch information
Naohiro Yoshida committed Jun 28, 2024
commit 12df75ca5d19a8198030aa3d43376281f50e96f4
155 changes: 118 additions & 37 deletions light-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::commitment::{
calculate_ibc_commitment_storage_key, decode_eip1184_rlp_proof, verify_proof,
};
use crate::consensus_state::ConsensusState;
use crate::errors::Error;
use crate::errors::{ClientError, Error};

use crate::header::Header;
use crate::message::ClientMessage;
Expand All @@ -38,16 +38,86 @@ impl LightClient for ParliaLightClient {
client_id: &ClientId,
) -> Result<Height, LightClientError> {
let any_client_state = ctx.client_state(client_id)?;
let client_state = ClientState::try_from(any_client_state)?;
let client_state = ClientState::try_from(any_client_state)
.map_err(|e| ClientError::LatestHeight(e, client_id.clone()))?;
Ok(client_state.latest_height)
}

fn create_client(
&self,
_ctx: &dyn HostClientReader,
ctx: &dyn HostClientReader,
any_client_state: Any,
any_consensus_state: Any,
) -> Result<CreateClientResult, LightClientError> {
self.process_create_client(ctx, any_client_state.clone(), any_consensus_state.clone())
.map_err(|e| ClientError::CreateClient(e, any_client_state, any_consensus_state).into())
}

fn update_client(
&self,
ctx: &dyn HostClientReader,
client_id: ClientId,
any_message: Any,
) -> Result<UpdateClientResult, LightClientError> {
self.process_update_client(ctx, client_id.clone(), any_message.clone())
.map_err(|e| ClientError::UpdateClient(e, client_id, any_message).into())
}

fn verify_membership(
&self,
ctx: &dyn HostClientReader,
client_id: ClientId,
prefix: CommitmentPrefix,
path: String,
value: Vec<u8>,
proof_height: Height,
proof: Vec<u8>,
) -> Result<VerifyMembershipResult, LightClientError> {
self.process_verify_membership(
ctx,
client_id.clone(),
prefix.clone(),
path.clone(),
value.clone(),
proof_height,
proof.clone(),
)
.map_err(|e| {
ClientError::VerifyMembership(e, client_id, prefix, path, value, proof_height, proof)
.into()
})
}

fn verify_non_membership(
&self,
ctx: &dyn HostClientReader,
client_id: ClientId,
prefix: CommitmentPrefix,
path: String,
proof_height: Height,
proof: Vec<u8>,
) -> Result<VerifyNonMembershipResult, LightClientError> {
self.process_verify_non_membership(
ctx,
client_id.clone(),
prefix.clone(),
path.clone(),
proof_height,
proof.clone(),
)
.map_err(|e| {
ClientError::VerifyNonMembership(e, client_id, prefix, path, proof_height, proof).into()
})
}
}

impl ParliaLightClient {
fn process_create_client(
&self,
_ctx: &dyn HostClientReader,
any_client_state: Any,
any_consensus_state: Any,
) -> Result<CreateClientResult, Error> {
let client_state = ClientState::try_from(any_client_state.clone())?;
let consensus_state = ConsensusState::try_from(any_consensus_state)?;

Expand All @@ -72,12 +142,12 @@ impl LightClient for ParliaLightClient {
})
}

fn update_client(
fn process_update_client(
&self,
ctx: &dyn HostClientReader,
client_id: ClientId,
any_message: Any,
) -> Result<UpdateClientResult, LightClientError> {
) -> Result<UpdateClientResult, Error> {
match ClientMessage::try_from(any_message.clone())? {
ClientMessage::Header(header) => Ok(self.update_state(ctx, client_id, header)?.into()),
ClientMessage::Misbehaviour(misbehavior) => {
Expand All @@ -96,7 +166,8 @@ impl LightClient for ParliaLightClient {
}
}

fn verify_membership(
#[allow(clippy::too_many_arguments)]
fn process_verify_membership(
&self,
ctx: &dyn HostClientReader,
client_id: ClientId,
Expand All @@ -105,7 +176,7 @@ impl LightClient for ParliaLightClient {
value: Vec<u8>,
proof_height: Height,
proof: Vec<u8>,
) -> Result<VerifyMembershipResult, LightClientError> {
) -> Result<VerifyMembershipResult, Error> {
let value = keccak_256(&value);
let state_id = self.verify_commitment(
ctx,
Expand All @@ -128,41 +199,41 @@ impl LightClient for ParliaLightClient {
})
}

fn verify_non_membership(
fn process_verify_non_membership(
&self,
ctx: &dyn HostClientReader,
client_id: ClientId,
prefix: CommitmentPrefix,
path: String,
proof_height: Height,
proof: Vec<u8>,
) -> Result<VerifyNonMembershipResult, LightClientError> {
) -> Result<VerifyNonMembershipResult, Error> {
let state_id =
self.verify_commitment(ctx, client_id, &prefix, &path, None, &proof_height, proof)?;
Ok(VerifyNonMembershipResult {
message: VerifyMembershipProxyMessage::new(prefix, path, None, proof_height, state_id),
})
}
}

impl ParliaLightClient {
pub fn update_state(
&self,
ctx: &dyn HostClientReader,
client_id: ClientId,
header: Header,
) -> Result<UpdateStateData, LightClientError> {
) -> Result<UpdateStateData, Error> {
//Ensure header can be verified.
let height = header.height();
let timestamp = header.timestamp()?;
let trusted_height = header.trusted_height();
let any_client_state = ctx.client_state(&client_id)?;
let any_consensus_state = ctx.consensus_state(&client_id, &trusted_height)?;
let any_client_state = ctx.client_state(&client_id).map_err(Error::LCPError)?;
let any_consensus_state = ctx
.consensus_state(&client_id, &trusted_height)
.map_err(Error::LCPError)?;

//Ensure client is not frozen
let client_state = ClientState::try_from(any_client_state)?;
if client_state.frozen {
return Err(Error::ClientFrozen(client_id).into());
return Err(Error::ClientFrozen(client_id));
}

// Create new state and ensure header is valid
Expand Down Expand Up @@ -207,16 +278,18 @@ impl ParliaLightClient {
ctx: &dyn HostClientReader,
client_id: ClientId,
misbehaviour: Misbehaviour,
) -> Result<(ClientState, Vec<PrevState>, ValidationContext), LightClientError> {
let any_client_state = ctx.client_state(&client_id)?;
let any_consensus_state1 =
ctx.consensus_state(&client_id, &misbehaviour.header_1.trusted_height())?;
let any_consensus_state2 =
ctx.consensus_state(&client_id, &misbehaviour.header_2.trusted_height())?;
) -> Result<(ClientState, Vec<PrevState>, ValidationContext), Error> {
let any_client_state = ctx.client_state(&client_id).map_err(Error::LCPError)?;
let any_consensus_state1 = ctx
.consensus_state(&client_id, &misbehaviour.header_1.trusted_height())
.map_err(Error::LCPError)?;
let any_consensus_state2 = ctx
.consensus_state(&client_id, &misbehaviour.header_2.trusted_height())
.map_err(Error::LCPError)?;

let client_state = ClientState::try_from(any_client_state)?;
if client_state.frozen {
return Err(Error::ClientFrozen(client_id).into());
return Err(Error::ClientFrozen(client_id));
}

let trusted_consensus_state1 = ConsensusState::try_from(any_consensus_state1)?;
Expand Down Expand Up @@ -266,20 +339,24 @@ impl ParliaLightClient {
value: Option<Vec<u8>>,
proof_height: &Height,
storage_proof_rlp: Vec<u8>,
) -> Result<StateID, LightClientError> {
let client_state = ClientState::try_from(ctx.client_state(&client_id)?)?;
) -> Result<StateID, Error> {
let client_state =
ClientState::try_from(ctx.client_state(&client_id).map_err(Error::LCPError)?)?;
if client_state.frozen {
return Err(Error::ClientFrozen(client_id).into());
return Err(Error::ClientFrozen(client_id));
}
let proof_height = *proof_height;
if client_state.latest_height < proof_height {
return Err(
Error::UnexpectedProofHeight(proof_height, client_state.latest_height).into(),
);
return Err(Error::UnexpectedProofHeight(
proof_height,
client_state.latest_height,
));
}

let consensus_state =
ConsensusState::try_from(ctx.consensus_state(&client_id, &proof_height)?)?;
let consensus_state = ConsensusState::try_from(
ctx.consensus_state(&client_id, &proof_height)
.map_err(Error::LCPError)?,
)?;
let storage_root = consensus_state.state_root;
let storage_proof = decode_eip1184_rlp_proof(&storage_proof_rlp)?;
verify_proof(
Expand All @@ -299,11 +376,13 @@ impl ParliaLightClient {
client_id: &ClientId,
client_state: &ClientState,
heights: Vec<Height>,
) -> Result<Vec<PrevState>, LightClientError> {
) -> Result<Vec<PrevState>, Error> {
let mut prev_states = Vec::new();
for height in heights {
let consensus_state: ConsensusState =
ctx.consensus_state(client_id, &height)?.try_into()?;
let consensus_state: ConsensusState = ctx
.consensus_state(client_id, &height)
.map_err(Error::LCPError)?
.try_into()?;
prev_states.push(PrevState {
height,
state_id: gen_state_id(client_state.clone(), consensus_state)?,
Expand All @@ -316,10 +395,12 @@ impl ParliaLightClient {
fn gen_state_id(
client_state: ClientState,
consensus_state: ConsensusState,
) -> Result<StateID, LightClientError> {
) -> Result<StateID, Error> {
let client_state = Any::try_from(client_state.canonicalize())?;
let consensus_state = Any::try_from(consensus_state.canonicalize())?;
gen_state_id_from_any(&client_state, &consensus_state).map_err(LightClientError::commitment)
gen_state_id_from_any(&client_state, &consensus_state)
.map_err(LightClientError::commitment)
.map_err(Error::LCPError)
}

#[cfg(test)]
Expand Down Expand Up @@ -412,7 +493,7 @@ mod test {
.client_state
.clone()
.ok_or_else(|| light_client::Error::client_state_not_found(client_id.clone()))?;
Ok(Any::try_from(cs)?)
Ok(Any::try_from(cs).unwrap())
}

fn consensus_state(
Expand All @@ -427,7 +508,7 @@ mod test {
light_client::Error::consensus_state_not_found(client_id.clone(), *height)
})?
.clone();
Ok(Any::try_from(state)?)
Ok(Any::try_from(state).unwrap())
}
}

Expand Down
71 changes: 68 additions & 3 deletions light-client/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use alloc::vec::Vec;
use core::fmt::Formatter;

use k256::ecdsa::signature;
use light_client::commitments::Error as CommitmentError;
use light_client::types::{ClientId, Height, Time, TimeError};
use light_client::commitments::{CommitmentPrefix, Error as CommitmentError};
use light_client::types::{Any, ClientId, Height, Time, TimeError};
use trie_db::TrieError;

use crate::misc::{Address, BlockNumber, Hash};
Expand Down Expand Up @@ -112,6 +112,9 @@ pub enum Error {
UnexpectedDifferentHeight(Height, Height),
UnexpectedSameBlockHash(Height),
TrieError(BoxedTrieError),

// Framework
LCPError(light_client::Error),
}

impl core::fmt::Display for Error {
Expand Down Expand Up @@ -327,8 +330,70 @@ impl core::fmt::Display for Error {
Error::LCPCommitmentError(e1) => {
write!(f, "LCPCommitmentError : {}", e1)
}
Error::LCPError(e1) => {
write!(f, "LCPError: {}", e1)
}
}
}
}

impl light_client::LightClientSpecificError for Error {}
#[derive(Debug)]
pub enum ClientError {
LatestHeight(Error, ClientId),
CreateClient(Error, Any, Any),
UpdateClient(Error, ClientId, Any),
VerifyMembership(
Error,
ClientId,
CommitmentPrefix,
String,
Vec<u8>,
Height,
Vec<u8>,
),
VerifyNonMembership(Error, ClientId, CommitmentPrefix, String, Height, Vec<u8>),
}

impl core::fmt::Display for ClientError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
ClientError::LatestHeight(e, client_id) => write!(
f,
"LatestHeight: cause={}\n, client_id={:?}",
e, client_id
),
ClientError::CreateClient(e, any_client_state, any_consensus_state) => write!(
f,
"CreateClient: cause={}\n, any_client_state={:?}, any_consensus_state={:?}",
e, any_client_state, any_consensus_state
),
ClientError::UpdateClient(e, client_id, message) => write!(
f,
"CreateClient: cause={}\n, client_id={:?}, message={:?}",
e, client_id, message
),
ClientError::VerifyMembership(e, client_id,
prefix,
path,
value,
proof_height,
proof
) => write!(
f,
"VerifyMembership: cause={}\n, client_id={:?}, prefix={:?}, path={:?}, value={:?}, proof_height={:?}, proof={:?}",
e, client_id, prefix, path, value, proof_height, proof
),
ClientError::VerifyNonMembership(e, client_id,
prefix,
path,
proof_height,
proof
) => write!(
f,
"VerifyNonMembership: cause={}\n, client_id={:?}, prefix={:?}, path={:?}, proof_height={:?}, proof={:?}",
e, client_id, prefix, path, proof_height, proof
),
}
}
}
impl light_client::LightClientSpecificError for ClientError {}