Skip to content

Commit

Permalink
Add PublicKey to StarknetClientState and verify header during update …
Browse files Browse the repository at this point in the history
…in Wasm light client
  • Loading branch information
ljoss17 committed Jan 20, 2025
1 parent 55c5f4e commit c7e9c2a
Show file tree
Hide file tree
Showing 13 changed files with 117 additions and 2 deletions.
21 changes: 21 additions & 0 deletions light-client/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions light-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ ibc-client-starknet-types = { version = "0.1.0" }

# external dependencies
derive_more = { version = "0.99.11", features = [ "from", "try_into" ] }
secp256k1 = { version = "0.28.2" }
serde = { version = "1.0.210", features = [ "derive" ] }
prost = { version = "0.13.1" }
prost-types = { version = "0.13.1" }
Expand Down
1 change: 1 addition & 0 deletions light-client/ibc-client-starknet-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ibc-core = { workspace = true }
tendermint = { workspace = true }
derive_more = { workspace = true }
prost = { workspace = true }
secp256k1 = { workspace = true, features = [ "serde" ] }
serde = { workspace = true, optional = true }
cgp = { workspace = true }
hermes-encoding-components = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions light-client/ibc-client-starknet-types/src/client_state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use cgp::prelude::*;
use ibc_core::client::types::Height;
use ibc_core::host::types::identifiers::ChainId;
use secp256k1::PublicKey;

pub const STARKNET_CLIENT_STATE_TYPE_URL: &str = "/StarknetClientState";

Expand All @@ -9,4 +10,5 @@ pub const STARKNET_CLIENT_STATE_TYPE_URL: &str = "/StarknetClientState";
pub struct StarknetClientState {
pub latest_height: Height,
pub chain_id: ChainId,
pub pub_key: PublicKey,
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
use core::str::FromStr;

use cgp::core::component::UseContext;
use cgp::prelude::*;
use hermes_cosmos_encoding_components::impls::chain_id::EncodeChainIdField;
use hermes_encoding_components::impls::encode_mut::combine::CombineEncoders;
use hermes_encoding_components::impls::encode_mut::field::EncodeField;
use hermes_encoding_components::impls::encode_mut::from::DecodeFrom;
use hermes_encoding_components::traits::decode_mut::MutDecoder;
use hermes_encoding_components::traits::encode_mut::MutEncoder;
use hermes_encoding_components::traits::transform::Transformer;
use hermes_encoding_components::traits::types::decode_buffer::HasDecodeBufferType;
use hermes_encoding_components::traits::types::encode_buffer::HasEncodeBufferType;
use hermes_protobuf_encoding_components::components::{MutDecoderComponent, MutEncoderComponent};
use hermes_protobuf_encoding_components::impls::encode_mut::proto_field::decode_required::DecodeRequiredProtoField;
use hermes_protobuf_encoding_components::impls::encode_mut::proto_field::encode::EncodeLengthDelimitedProtoField;
use hermes_protobuf_encoding_components::impls::encode_mut::proto_field::string::EncodeStringField;
use ibc_core::client::types::Height;
use ibc_core::host::types::identifiers::ChainId;
use secp256k1::{Error as Secp256k1Error, PublicKey};

use crate::StarknetClientState;

Expand All @@ -27,26 +35,68 @@ delegate_components! {
symbol!("chain_id"),
EncodeChainIdField<2>,
>,
EncodeField<
symbol!("pub_key"),
EncodePublicKeyField<3>,
>,
]>,
MutDecoderComponent: DecodeFrom<
Self,
CombineEncoders<Product![
DecodeRequiredProtoField<1, UseContext>,
EncodeChainIdField<2>,
EncodePublicKeyField<3>,
]>
>,
}
}

impl Transformer for EncodeStarknetClientState {
type From = Product![Height, ChainId];
type From = Product![Height, ChainId, PublicKey];

type To = StarknetClientState;

fn transform(product![latest_height, chain_id]: Self::From) -> Self::To {
fn transform(product![latest_height, chain_id, pub_key]: Self::From) -> Self::To {
StarknetClientState {
latest_height,
chain_id,
pub_key,
}
}
}

// TODO: Move to Hermes SDK
pub struct EncodePublicKeyField<const TAG: u32>;

impl<Encoding, Strategy, const TAG: u32> MutEncoder<Encoding, Strategy, PublicKey>
for EncodePublicKeyField<TAG>
where
Encoding: HasEncodeBufferType + HasAsyncErrorType,
EncodeStringField<TAG>: MutEncoder<Encoding, Strategy, String>,
{
fn encode_mut(
encoding: &Encoding,
pub_key: &PublicKey,
buffer: &mut Encoding::EncodeBuffer,
) -> Result<(), Encoding::Error> {
<EncodeStringField<TAG>>::encode_mut(encoding, &pub_key.to_string(), buffer)?;

Ok(())
}
}

impl<Encoding, Strategy, const TAG: u32> MutDecoder<Encoding, Strategy, PublicKey>
for EncodePublicKeyField<TAG>
where
Encoding: HasDecodeBufferType + CanRaiseAsyncError<Secp256k1Error>,
EncodeStringField<TAG>: MutDecoder<Encoding, Strategy, String>,
{
fn decode_mut<'a>(
encoding: &Encoding,
buffer: &mut Encoding::DecodeBuffer<'_>,
) -> Result<PublicKey, Encoding::Error> {
let pub_key_str = <EncodeStringField<TAG>>::decode_mut(encoding, buffer)?;

PublicKey::from_str(&pub_key_str).map_err(Encoding::raise_error)
}
}
1 change: 1 addition & 0 deletions light-client/ibc-client-starknet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ workspace = true
[dependencies]
# external dependencies
derive_more = { workspace = true }
secp256k1 = { workspace = true }
serde = { workspace = true, optional = true }
prost = { workspace = true }
prost-types = { workspace = true }
Expand Down
15 changes: 15 additions & 0 deletions light-client/ibc-client-starknet/src/client_state/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use ibc_core::host::types::identifiers::ClientId;
use ibc_core::host::types::path::{ClientConsensusStatePath, ClientStatePath};
use ibc_core::primitives::proto::Any;
use prost_types::Any as ProstAny;
use secp256k1::ecdsa::Signature;
use secp256k1::Secp256k1;

use super::ClientState;
use crate::encoding::context::StarknetLightClientEncoding;
Expand Down Expand Up @@ -58,13 +60,26 @@ where

let header = signed_header.header;

// TODO: Encode header to byte array
self.0
.pub_key
.verify(
&Secp256k1::verification_only(),
&header.into(),
&Signature::from_der(&signed_header.signature).unwrap(),
)
.map_err(|e| ClientError::ClientSpecific {
description: e.to_string(),
})?;

let latest_height = header.height;

let new_consensus_state = header.consensus_state;

let new_client_state = ClientStateType {
latest_height: header.height,
chain_id: self.0.chain_id.clone(),
pub_key: self.0.pub_key.clone(),
}
.into();

Expand Down
11 changes: 11 additions & 0 deletions light-client/ibc-client-starknet/src/encoding/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use ibc_core::commitment_types::commitment::CommitmentRoot;
use ibc_core::host::types::error::{DecodingError, IdentifierError};
use ibc_core::primitives::{Timestamp, TimestampError};
use prost::DecodeError;
use secp256k1::Error as Secp256k1Error;

pub struct StarknetLightClientEncoding;

Expand Down Expand Up @@ -185,6 +186,16 @@ impl ErrorRaiser<StarknetLightClientEncoding, IdentifierError>
}
}

impl ErrorRaiser<StarknetLightClientEncoding, Secp256k1Error>
for StarknetLightClientEncodingContextComponents
{
fn raise_error(e: Secp256k1Error) -> ClientError {
ClientError::ClientSpecific {
description: format!("{e:?}"),
}
}
}

pub trait CanUseStarknetLightClientEncoding:
Async
+ CanEncodeAndDecode<ViaProtobuf, Any>
Expand Down
2 changes: 2 additions & 0 deletions relayer/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions relayer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ starknet-types-core = { version = "0.1.7" }
url = { version = "2.4.0" }
eyre = { version = "0.6.12" }
tokio = { version = "1.38" }
secp256k1 = { version = "0.28.2" }
serde = { version = "1.0" }
serde_json = { version = "1.0" }
rand = { version = "0.8.5" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use cgp::prelude::CanRaiseAsyncError;
use hermes_chain_components::traits::types::chain_id::HasChainId;
use hermes_cosmos_chain_components::types::key_types::secp256k1::Secp256k1KeyPair;
use hermes_relayer_components::chain::traits::payload_builders::create_client::CreateClientPayloadBuilder;
use hermes_relayer_components::chain::traits::queries::chain_status::CanQueryChainStatus;
use hermes_relayer_components::chain::traits::types::create_client::{
HasCreateClientPayloadOptionsType, HasCreateClientPayloadType,
};
use hermes_relayer_components::transaction::traits::default_signer::HasDefaultSigner;
use ibc::core::client::types::error::ClientError;
use ibc::core::client::types::Height;
use ibc::core::host::types::identifiers::ChainId;
Expand All @@ -28,6 +30,8 @@ where
> + HasCreateClientPayloadType<Counterparty, CreateClientPayload = StarknetCreateClientPayload>
+ CanQueryChainStatus<ChainStatus = StarknetChainStatus>
+ HasChainId<ChainId = ChainId>
// TODO: StarknetChain doesn't have a Secp256k1KeyPair Signer
+ HasDefaultSigner<Signer = Secp256k1KeyPair>
+ CanRaiseAsyncError<ClientError>,
{
async fn build_create_client_payload(
Expand All @@ -38,11 +42,14 @@ where

let root = Vec::from(chain_status.block_hash.to_bytes_be());

let signer = chain.get_default_signer();

let client_state = WasmStarknetClientState {
wasm_code_hash: create_client_options.wasm_code_hash.into(),
client_state: StarknetClientState {
latest_height: Height::new(0, 1).map_err(Chain::raise_error)?,
chain_id: chain.chain_id().clone(),
pub_key: signer.public_key.clone(),
},
};

Expand Down
1 change: 1 addition & 0 deletions relayer/crates/starknet-chain-context/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ starknet = { workspace = true }
url = { workspace = true }
eyre = { workspace = true }
prost = { workspace = true }
secp256k1 = { workspace = true }
serde_json = { workspace = true }
cairo-lang-starknet-classes = { workspace = true }
ibc-client-starknet-types = { workspace = true }
2 changes: 2 additions & 0 deletions relayer/crates/starknet-chain-context/src/impls/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use ibc::core::client::types::error::ClientError;
use ibc::core::host::types::error::IdentifierError;
use ibc::primitives::TimestampError;
use prost::{DecodeError, EncodeError};
use secp256k1::Error as Secp256k1Error;
use starknet::accounts::{single_owner, AccountError};
use starknet::core::types::contract::{ComputeClassHashError, JsonError};
use starknet::core::types::{RevertedInvocation, StarknetError};
Expand Down Expand Up @@ -72,6 +73,7 @@ delegate_components! {
ClientError,
TimestampError,
IdentifierError,
Secp256k1Error,
ComputeClassHashError,
StarknetSierraCompilationError,
]: ReportError,
Expand Down

0 comments on commit c7e9c2a

Please sign in to comment.