Skip to content

Commit 7025c5a

Browse files
authored
feat(lc/starknet): verify signed commitment proofs from starknet (#242)
* update ibc-client-cw patch branch * fix cw context methods * impl cw client state execution for starknet * add membership proof signer * sign connection open try message * ignore client and consensus proof * open ack proof * todo comment * generate membership proof message at starknet payload * membership proof msg for channel end * membership proof msg for packet commitment * membership proof msg for packet ack * membership proof msg for packet receipt * rename field in StarknetCommitmentProof * rm redundant todos * sign self client and consensus state proofs * use chain_status height as proof height * add tiny-bip39 dep * proof_signer components * add proof_signer field to StarknetChain * generate secp256k1 keypair from felt signing key * sign with starknet proof signer * nits * rm redundant endpoint * use packet_receipt endpoint * ibc-go compatible packet commitment * test consistent packet commitment in cairo and rust * happy clippy * rm test steps with dummy proofs * cargo fmt * cargo clippy * update expected commitment hash after fix
1 parent 51c9b8d commit 7025c5a

File tree

42 files changed

+547
-225
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+547
-225
lines changed

cairo-contracts/packages/core/src/channel/components/handler.cairo

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,15 +174,6 @@ pub mod ChannelHandlerComponent {
174174
self.read_packet_ack(@port_id, @channel_id, @sequence)
175175
}
176176

177-
fn is_packet_received(
178-
self: @ComponentState<TContractState>,
179-
port_id: PortId,
180-
channel_id: ChannelId,
181-
sequence: Sequence
182-
) -> bool {
183-
self.packet_ack_exists(@port_id, @channel_id, @sequence)
184-
}
185-
186177
fn unreceived_packet_sequences(
187178
self: @ComponentState<TContractState>,
188179
port_id: PortId,

cairo-contracts/packages/core/src/channel/interface.cairo

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,6 @@ pub trait IChannelQuery<TContractState> {
6767
fn packet_acknowledgement(
6868
self: @TContractState, port_id: PortId, channel_id: ChannelId, sequence: Sequence
6969
) -> Commitment;
70-
fn is_packet_received(
71-
self: @TContractState, port_id: PortId, channel_id: ChannelId, sequence: Sequence
72-
) -> bool;
7370
fn unreceived_packet_sequences(
7471
self: @TContractState, port_id: PortId, channel_id: ChannelId, sequences: Array<Sequence>
7572
) -> Array<Sequence>;

cairo-contracts/packages/core/src/commitment/types.cairo

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ pub fn compute_packet_commitment(
5252
json_packet_data: @ByteArray, timeout_height: Height, timeout_timestamp: Timestamp
5353
) -> Commitment {
5454
let mut coll = U32CollectorImpl::init();
55-
coll.extend(timeout_timestamp);
55+
// ibc-go uses nanosecs
56+
// https://github.com/cosmos/ibc-go/blob/98d7e7550a23ecf8d96ce042ab11ef857b184f2a/proto/ibc/core/channel/v1/channel.proto#L179-L180
57+
coll.extend(timeout_timestamp.timestamp * 1_000_000_000);
5658
coll.extend(timeout_height);
5759
coll.extend_from_chunk(compute_sha256_byte_array(json_packet_data));
5860
compute_sha256_u32_array(coll.value(), 0, 0).into()

cairo-contracts/packages/core/src/tests/commitment.cairo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ fn test_array_u8_into_array_u32() {
5252
fn test_compute_packet_commitment() {
5353
let commitment = PACKET_COMMITMENT_ON_SN(ERC20());
5454
let expected: [u32; 8] = [
55-
3458244073, 1576048754, 4210798310, 1002247062, 2365181318, 2763927782, 545147151, 944653547
55+
1561496803, 591083406, 1958596266, 2480824962, 846563094, 2634790765, 145282158, 2139799705
5656
];
5757
assert_eq!(commitment, expected.into());
5858
}

light-client/Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

light-client/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ ibc-client-starknet = { path = "./ibc-client-starknet" }
6161
ibc-client-starknet-cw = { path = "./ibc-client-starknet-cw" }
6262
ibc-client-starknet-types = { path = "./ibc-client-starknet-types" }
6363

64-
ibc-client-cw = { git = "https://github.com/informalsystems/cosmwasm-ibc.git", branch = "luca_joss/add-cw-client-extension-trait" }
64+
ibc-client-cw = { git = "https://github.com/informalsystems/cosmwasm-ibc.git", branch = "starknet/demo2" }
6565

6666
ibc = { git = "https://github.com/cosmos/ibc-rs", branch = "main" }
6767
ibc-core = { git = "https://github.com/cosmos/ibc-rs", branch = "main" }
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use ibc_client_cw::api::CwClientStateExecution;
2+
use ibc_client_cw::context::client_ctx::CwClientExecution;
3+
4+
use super::ClientState;
5+
use crate::ConsensusState;
6+
7+
impl<'a, E> CwClientStateExecution<'a, E> for ClientState
8+
where
9+
E: CwClientExecution<'a, ClientStateMut = ClientState, ConsensusStateRef = ConsensusState>,
10+
{
11+
fn public_key(&self) -> Option<Vec<u8>> {
12+
Some(self.0.pub_key.clone())
13+
}
14+
}

light-client/ibc-client-starknet/src/client_state/execution.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,11 @@ where
6262

6363
let raw_header = signed_header.header;
6464

65-
let header_digest = ctx.generate_sha256_digest(&raw_header);
65+
let header_digest = ctx.checksum(&raw_header);
6666

67-
let deps = ctx
68-
.cosmwasm_execute_context()
69-
.ok_or_else(|| ClientError::ClientSpecific {
70-
description: "missing Deps from context".to_owned(),
71-
})?;
67+
let deps = ctx.deps_mut().ok_or_else(|| ClientError::ClientSpecific {
68+
description: "missing Deps from context".to_owned(),
69+
})?;
7270

7371
match deps.api.secp256k1_verify(
7472
header_digest.as_slice(),

light-client/ibc-client-starknet/src/client_state/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod common;
2+
pub mod cw;
23
pub mod execution;
34
pub mod validation;
45

nix/ibc-starknet-cw.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ let
1212
outputHashes = {
1313
"hermes-cosmos-encoding-components-0.1.0" = "sha256-uxXpzxVc89DkAC4VqC/Au3cpBzUbxiSS2KiFKZ+rqdg=";
1414
"cgp-0.3.1" = "sha256-AOQ+WVQWPlF2ZfYYc5Eq3t7XAljd5P2qExWLYZWNnd8=";
15-
"ibc-client-cw-0.56.0" = "sha256-EkLxuJfr3vf0busmSZD7DwOS9GfgfhT+sdopi1nNiCs=";
15+
"ibc-client-cw-0.56.0" = "sha256-DA3AB8ejUrx4ksBtN/vaOznjpKE0+0F6vGA7JmWyHWA=";
1616
"ibc-0.56.0" = "sha256-7DPIqu/zs0szjmtJTfXI2eQ0HEkRyvGjArcMZsFWMT4=";
1717
};
1818
};

relayer/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

relayer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ tracing-subscriber = { version = "0.3.18" }
5353
flate2 = { version = "1.0" }
5454
http = { version = "1.0.0" }
5555
futures = { version = "0.3.30", default-features = false }
56+
tiny-bip39 = { version = "1.0.0" }
5657

5758
ibc = { version = "0.56.0" }
5859
ibc-proto = { version = "0.51.1" }

relayer/crates/starknet-chain-components/src/impls/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod messages;
99
pub mod packet_fields;
1010
pub mod packet_filter;
1111
pub mod payload_builders;
12+
pub mod proof_signer;
1213
pub mod provider;
1314
pub mod queries;
1415
pub mod send_message;

relayer/crates/starknet-chain-components/src/impls/payload_builders/create_client.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use cgp::prelude::CanRaiseAsyncError;
22
use hermes_chain_components::traits::types::chain_id::HasChainId;
3+
use hermes_cosmos_chain_components::types::key_types::secp256k1::Secp256k1KeyPair;
34
use hermes_relayer_components::chain::traits::payload_builders::create_client::CreateClientPayloadBuilder;
45
use hermes_relayer_components::chain::traits::queries::chain_status::CanQueryChainStatus;
56
use hermes_relayer_components::chain::traits::types::create_client::{
@@ -10,6 +11,7 @@ use ibc::core::client::types::Height;
1011
use ibc::core::host::types::identifiers::ChainId;
1112
use ibc::primitives::Timestamp;
1213

14+
use crate::traits::proof_signer::HasStarknetProofSigner;
1315
use crate::types::consensus_state::{StarknetConsensusState, WasmStarknetConsensusState};
1416
use crate::types::payloads::client::{
1517
StarknetCreateClientPayload, StarknetCreateClientPayloadOptions,
@@ -27,6 +29,7 @@ where
2729
> + HasCreateClientPayloadType<Counterparty, CreateClientPayload = StarknetCreateClientPayload>
2830
+ CanQueryChainStatus<ChainStatus = StarknetChainStatus>
2931
+ HasChainId<ChainId = ChainId>
32+
+ HasStarknetProofSigner<ProofSigner = Secp256k1KeyPair>
3033
+ CanRaiseAsyncError<&'static str>
3134
+ CanRaiseAsyncError<ClientError>,
3235
{
@@ -53,6 +56,7 @@ where
5356
chain_id: chain.chain_id().clone(),
5457
client_state_wasm_code_hash: create_client_options.wasm_code_hash.into(),
5558
consensus_state,
59+
proof_signer_pub_key: chain.proof_signer().public_key.serialize().to_vec(),
5660
})
5761
}
5862
}

relayer/crates/starknet-chain-components/src/impls/payload_builders/update_client.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
use cgp::prelude::CanRaiseAsyncError;
1+
use cgp::prelude::{Async, CanRaiseAsyncError};
2+
use hermes_cosmos_chain_components::types::key_types::secp256k1::Secp256k1KeyPair;
3+
use hermes_encoding_components::traits::encode::CanEncode;
4+
use hermes_encoding_components::traits::has_encoding::HasDefaultEncoding;
5+
use hermes_encoding_components::types::AsBytes;
6+
use hermes_protobuf_encoding_components::types::strategy::ViaProtobuf;
27
use hermes_relayer_components::chain::traits::payload_builders::update_client::UpdateClientPayloadBuilder;
38
use hermes_relayer_components::chain::traits::queries::chain_status::CanQueryChainStatus;
49
use hermes_relayer_components::chain::traits::types::client_state::HasClientStateType;
@@ -10,14 +15,15 @@ use ibc_client_starknet_types::header::StarknetHeader;
1015
use starknet::core::types::{BlockId, MaybePendingBlockWithTxHashes};
1116
use starknet::providers::{Provider, ProviderError};
1217

18+
use crate::traits::proof_signer::HasStarknetProofSigner;
1319
use crate::traits::provider::HasStarknetProvider;
1420
use crate::types::consensus_state::StarknetConsensusState;
1521
use crate::types::payloads::client::StarknetUpdateClientPayload;
1622
use crate::types::status::StarknetChainStatus;
1723

1824
pub struct BuildStarknetUpdateClientPayload;
1925

20-
impl<Chain, Counterparty> UpdateClientPayloadBuilder<Chain, Counterparty>
26+
impl<Chain, Counterparty, Encoding> UpdateClientPayloadBuilder<Chain, Counterparty>
2127
for BuildStarknetUpdateClientPayload
2228
where
2329
Chain: HasHeightType<Height = u64>
@@ -26,7 +32,12 @@ where
2632
+ CanQueryChainStatus<ChainStatus = StarknetChainStatus>
2733
+ HasStarknetProvider
2834
+ CanRaiseAsyncError<&'static str>
29-
+ CanRaiseAsyncError<ProviderError>,
35+
+ HasDefaultEncoding<AsBytes, Encoding = Encoding>
36+
+ HasStarknetProofSigner<ProofSigner = Secp256k1KeyPair>
37+
+ CanRaiseAsyncError<String>
38+
+ CanRaiseAsyncError<ProviderError>
39+
+ CanRaiseAsyncError<Encoding::Error>,
40+
Encoding: Async + CanEncode<ViaProtobuf, StarknetHeader, Encoded = Vec<u8>>,
3041
{
3142
async fn build_update_client_payload(
3243
chain: &Chain,
@@ -63,6 +74,15 @@ where
6374
consensus_state,
6475
};
6576

66-
Ok(StarknetUpdateClientPayload { header })
77+
let encoded_header = Chain::default_encoding()
78+
.encode(&header)
79+
.map_err(Chain::raise_error)?;
80+
81+
let signature = chain
82+
.proof_signer()
83+
.sign(&encoded_header)
84+
.map_err(Chain::raise_error)?;
85+
86+
Ok(StarknetUpdateClientPayload { header, signature })
6787
}
6888
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use core::marker::PhantomData;
2+
3+
use cgp::prelude::*;
4+
5+
use crate::traits::proof_signer::{
6+
HasStarknetProofSignerType, ProvideStarknetProofSignerType, StarknetProofSignerGetter,
7+
};
8+
9+
pub struct GetStarknetProofSignerField<Tag>(pub PhantomData<Tag>);
10+
11+
impl<Chain, Tag> ProvideStarknetProofSignerType<Chain> for GetStarknetProofSignerField<Tag>
12+
where
13+
Chain: Async + HasField<Tag>,
14+
Tag: Async,
15+
Chain::Value: Async,
16+
{
17+
type ProofSigner = Chain::Value;
18+
}
19+
20+
impl<Chain, Tag> StarknetProofSignerGetter<Chain> for GetStarknetProofSignerField<Tag>
21+
where
22+
Chain: Async + HasStarknetProofSignerType + HasField<Tag, Value = Chain::ProofSigner>,
23+
Tag: Async,
24+
{
25+
fn proof_signer(chain: &Chain) -> &Chain::ProofSigner {
26+
chain.get_field(PhantomData)
27+
}
28+
}

relayer/crates/starknet-chain-components/src/impls/queries/ack_commitment.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,45 @@ use core::marker::PhantomData;
33
use cgp::prelude::*;
44
use hermes_cairo_encoding_components::strategy::ViaCairo;
55
use hermes_cairo_encoding_components::types::as_felt::AsFelt;
6+
use hermes_chain_components::traits::commitment_prefix::HasIbcCommitmentPrefix;
7+
use hermes_chain_components::traits::queries::chain_status::CanQueryChainStatus;
68
use hermes_chain_components::traits::queries::packet_acknowledgement::PacketAcknowledgementQuerier;
79
use hermes_chain_components::traits::types::height::HasHeightType;
810
use hermes_chain_components::traits::types::ibc::{
911
HasChannelIdType, HasPortIdType, HasSequenceType,
1012
};
1113
use hermes_chain_components::traits::types::packets::ack::HasAcknowledgementType;
1214
use hermes_chain_components::traits::types::proof::HasCommitmentProofType;
15+
use hermes_cosmos_chain_components::types::key_types::secp256k1::Secp256k1KeyPair;
1316
use hermes_encoding_components::traits::decode::CanDecode;
1417
use hermes_encoding_components::traits::encode::CanEncode;
1518
use hermes_encoding_components::traits::has_encoding::HasEncoding;
1619
use hermes_encoding_components::traits::types::encoded::HasEncodedType;
1720
use ibc::core::host::types::identifiers::{PortId as IbcPortId, Sequence as IbcSequence};
21+
use ibc::core::host::types::path::{AckPath, Path};
1822
use starknet::core::types::Felt;
1923
use starknet::macros::selector;
2024

2125
use crate::traits::contract::call::CanCallContract;
26+
use crate::traits::proof_signer::HasStarknetProofSigner;
2227
use crate::traits::queries::address::CanQueryContractAddress;
2328
use crate::traits::types::blob::HasBlobType;
2429
use crate::traits::types::method::HasSelectorType;
2530
use crate::types::channel_id::ChannelId;
2631
use crate::types::commitment_proof::StarknetCommitmentProof;
32+
use crate::types::membership_proof_signer::MembershipVerifierContainer;
2733
use crate::types::messages::ibc::channel::PortId as CairoPortId;
2834
use crate::types::messages::ibc::packet::Sequence;
35+
use crate::types::status::StarknetChainStatus;
2936

3037
pub struct QueryStarknetAckCommitment;
3138

3239
impl<Chain, Counterparty, Encoding> PacketAcknowledgementQuerier<Chain, Counterparty>
3340
for QueryStarknetAckCommitment
3441
where
3542
Chain: HasHeightType<Height = u64>
43+
+ CanQueryChainStatus<ChainStatus = StarknetChainStatus>
44+
+ HasIbcCommitmentPrefix<CommitmentPrefix = Vec<u8>>
3645
+ HasChannelIdType<Counterparty, ChannelId = ChannelId>
3746
+ HasPortIdType<Counterparty, PortId = IbcPortId>
3847
+ HasAcknowledgementType<Counterparty, Acknowledgement = Vec<u8>>
@@ -42,6 +51,8 @@ where
4251
+ CanQueryContractAddress<symbol!("ibc_core_contract_address")>
4352
+ HasEncoding<AsFelt, Encoding = Encoding>
4453
+ CanCallContract
54+
+ HasStarknetProofSigner<ProofSigner = Secp256k1KeyPair>
55+
+ CanRaiseAsyncError<String>
4556
+ CanRaiseAsyncError<Encoding::Error>,
4657
Counterparty: HasSequenceType<Chain, Sequence = IbcSequence>,
4758

@@ -54,7 +65,7 @@ where
5465
channel_id: &ChannelId,
5566
port_id: &IbcPortId,
5667
sequence: &IbcSequence,
57-
height: &u64,
68+
_height: &u64,
5869
) -> Result<(Vec<u8>, StarknetCommitmentProof), Chain::Error> {
5970
let encoding = chain.encoding();
6071

@@ -80,12 +91,6 @@ where
8091
)
8192
.await?;
8293

83-
// TODO(rano): how to get the proof?
84-
let dummy_proof = StarknetCommitmentProof {
85-
proof_height: *height,
86-
proof_bytes: vec![0x1],
87-
};
88-
8994
let product![ack,] = encoding.decode(&output).map_err(Chain::raise_error)?;
9095

9196
let ack_bytes = ack
@@ -94,6 +99,29 @@ where
9499
.flat_map(|felt| felt.to_be_bytes())
95100
.collect::<Vec<_>>();
96101

102+
let chain_status = chain.query_chain_status().await?;
103+
104+
let unsigned_membership_proof_bytes = MembershipVerifierContainer {
105+
state_root: chain_status.block_hash.to_bytes_be().to_vec(),
106+
prefix: chain.ibc_commitment_prefix().clone(),
107+
path: Path::Ack(AckPath::new(port_id, channel_id, *sequence))
108+
.to_string()
109+
.into(),
110+
value: Some(ack_bytes.clone()),
111+
}
112+
.canonical_bytes();
113+
114+
let signed_bytes = chain
115+
.proof_signer()
116+
.sign(&unsigned_membership_proof_bytes)
117+
.map_err(Chain::raise_error)?;
118+
119+
// TODO(rano): how to get the proof?
120+
let dummy_proof = StarknetCommitmentProof {
121+
proof_height: chain_status.height,
122+
proof_bytes: signed_bytes,
123+
};
124+
97125
Ok((ack_bytes, dummy_proof))
98126
}
99127
}

0 commit comments

Comments
 (0)