Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
392 changes: 368 additions & 24 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ members = [
"voyager/plugins/event-source/evm",
# "voyager/plugins/event-source/movement",
"voyager/plugins/event-source/sui",
"voyager/plugins/event-source/starknet",

"voyager/plugins/transaction/cosmos",
"voyager/plugins/transaction/evm",
Expand Down Expand Up @@ -295,6 +296,7 @@ members = [
"cosmwasm/pausable",
"cosmwasm/gatekeeper",
"cosmwasm/proxy-account-factory",
"lib/starknet-light-client-types",
]

[workspace.package]
Expand Down Expand Up @@ -419,6 +421,8 @@ serde-utils = { path = "lib/serde-utils", default-
solidity-slot = { path = "lib/solidity-slot", default-features = false }
ssz = { path = "lib/ssz", default-features = false }
ssz-derive = { path = "lib/ssz-derive", default-features = false }
starknet-light-client-types = { path = "lib/starknet-light-client-types", default-features = false }
starknet-storage-verifier = { path = "lib/starknet-storage-verifier", default-features = false }
state-lens-ics23-ics23-light-client-types = { path = "lib/state-lens-ics23-ics23-light-client-types", default-features = false }
state-lens-ics23-mpt-light-client = { path = "cosmwasm/ibc-union/lightclient/state-lens-ics23-mpt", default-features = false }
state-lens-ics23-mpt-light-client-types = { path = "lib/state-lens-ics23-mpt-light-client-types", default-features = false }
Expand Down Expand Up @@ -528,6 +532,7 @@ serde_with = { version = "3.12.0", default-features = false, featu
sha2 = { version = "0.10.9", default-features = false }
sha3 = { version = "0.10.8", default-features = false }
sqlx = { version = "0.7.4", default-features = false }
starknet = { version = "0.17.0", default-features = false }
starknet-core = { version = "0.16.0", default-features = false }
starknet-crypto = { version = "0.8.1", default-features = false }
static_assertions = { git = "https://github.com/nvzqz/static-assertions" } # https://github.com/nvzqz/static-assertions/pull/28
Expand Down
14 changes: 12 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,12 @@
mv $out/bin/cast $out/bin/cast-cursed

cat <<EOF >> $out/bin/cast
export LD_LIBRARY_PATH=${lib.makeLibraryPath [ super.stdenv.cc.cc.lib ]}
export LD_LIBRARY_PATH=${
lib.makeLibraryPath [
super.gcc14.cc.lib
# super.stdenv.cc.cc.lib
]
}
$out/bin/cast-cursed "\$@"
unset LD_LIBRARY_PATH
EOF
Expand All @@ -385,7 +390,12 @@
mv $out/bin/forge $out/bin/forge-cursed

cat <<EOF >> $out/bin/forge
export LD_LIBRARY_PATH=${lib.makeLibraryPath [ super.stdenv.cc.cc.lib ]}
export LD_LIBRARY_PATH=${
lib.makeLibraryPath [
super.gcc14
# super.stdenv.cc.cc.lib
]
}
$out/bin/forge-cursed "\$@"
unset LD_LIBRARY_PATH
EOF
Expand Down
35 changes: 35 additions & 0 deletions lib/starknet-light-client-types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "starknet-light-client-types"
version = "0.0.0"

authors = { workspace = true }
edition = { workspace = true }
license-file = "LICENSE"
publish = { workspace = true }
repository = { workspace = true }

[lints]
workspace = true

[dependencies]
alloy = { workspace = true, features = ["sol-types"], optional = true }
bincode = { workspace = true, features = ["alloc", "derive"], optional = true }
ethereum-light-client-types = { workspace = true }
ibc-union-spec = { workspace = true }
serde = { workspace = true, optional = true, features = ["derive"] }
starknet-core = { workspace = true } # REVIEW: Do we want to use this crate in the public api of this client's types?
starknet-types-core = { version = "1.0.0", features = ["hash"] }
unionlabs = { workspace = true }

[features]
bincode = [
"dep:bincode",
"unionlabs/bincode",
"ethereum-light-client-types/bincode",
"ibc-union-spec/bincode",
]
ethabi = ["dep:alloy", "ethereum-light-client-types/ethabi", "ibc-union-spec/ethabi"]
serde = ["dep:serde", "ethereum-light-client-types/serde", "ibc-union-spec/serde"]

[dev-dependencies]
hex-literal = { workspace = true }
27 changes: 27 additions & 0 deletions lib/starknet-light-client-types/src/client_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use starknet_core::types::Felt;
use unionlabs::primitives::H160;

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "version", content = "data", rename_all = "snake_case")
)]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
pub enum ClientState {
V1(ClientStateV1),
}

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
pub struct ClientStateV1 {
pub chain_id: Felt,
pub latest_height: u64,
pub ibc_contract_address: Felt,
/// https://docs.starknet.io/learn/cheatsheets/chain-info#important-addresses
///
/// Mainnet: `0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4`
/// Sepolia: `0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057`
pub l1_contract_address: H160,
}
47 changes: 47 additions & 0 deletions lib/starknet-light-client-types/src/consensus_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use ibc_union_spec::Timestamp;
use unionlabs::primitives::H256;

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ConsensusState {
pub global_root: H256,
pub ibc_storage_root: H256,
pub timestamp: Timestamp,
}

#[cfg(feature = "ethabi")]
pub mod ethabi {
use unionlabs::impl_ethabi_via_try_from_into;

use super::*;

impl_ethabi_via_try_from_into!(ConsensusState => SolConsensusState);

alloy::sol! {
struct SolConsensusState {
bytes32 global_root;
bytes32 ibc_storage_root;
uint64 timestamp;
}
}

impl From<ConsensusState> for SolConsensusState {
fn from(value: ConsensusState) -> Self {
Self {
global_root: value.global_root.get().into(),
ibc_storage_root: value.storage_root.get().into(),
timestamp: value.timestamp.as_nanos(),
}
}
}

impl From<SolConsensusState> for ConsensusState {
fn from(value: SolConsensusState) -> Self {
Self {
global_root: H256::new(value.global_root.0),
storage_root: H256::new(value.ibc_storage_root.0),
timestamp: Timestamp::from_nanos(value.timestamp),
}
}
}
}
161 changes: 161 additions & 0 deletions lib/starknet-light-client-types/src/header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use ethereum_light_client_types::StorageProof as L1StorageProof;
use starknet_types_core::{
felt::Felt,
hash::{Poseidon, StarkHash as _},
};
use unionlabs::primitives::H256;

use crate::StorageProof;

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(deny_unknown_fields, rename_all = "snake_case")
)]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
pub struct Header {
pub l1_height: u64,
pub l1_block_hash_proof: L1StorageProof,
pub l2_block: L2Block,
pub l2_ibc_contract_proof: StorageProof,
}

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(deny_unknown_fields, rename_all = "snake_case")
)]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
pub struct L2Block {
block_number: u64,
parent_block_hash: H256,
global_state_root: H256,
sequencer_address: H256,
// SECONDS
block_timestamp: u64,
transaction_count: u32,
events_count: u32,
state_diff_length: u32,
state_diff_commitment: H256,
transactions_commitment: H256,
events_commitment: H256,
receipts_commitment: H256,
l1_gas_price: (u128, u128),
l1_data_gas_price: (u128, u128),
l2_gas_price: (u128, u128),
l1_da_mode: L1DaMode,
protocol_version: String,
}

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "snake_case")
)]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
pub enum L1DaMode {
Blob,
Calldata,
}

impl L2Block {
/// <https://docs.starknet.io/learn/protocol/blocks#block-hash>
/// <https://github.com/starkware-libs/sequencer/blob/079ed26ce95b3b10de40c9916ffa332aaecd9f06/crates/starknet_api/src/block_hash/block_hash_calculator.rs#L134>
// TODO: Handle different versions
pub fn hash(&self) -> H256 {
Poseidon::hash_array(&[
// hex(b"STARKNET_BLOCK_HASH1")
const { Felt::from_hex_unwrap("0x535441524b4e45545f424c4f434b5f4841534831") },
self.block_number.into(),
Felt::from_bytes_be(self.global_state_root.get()),
Felt::from_bytes_be(self.sequencer_address.get()),
self.block_timestamp.into(),
// https://github.com/starkware-libs/sequencer/blob/079ed26ce95b3b10de40c9916ffa332aaecd9f06/crates/starknet_api/src/block_hash/block_hash_calculator.rs#L230
Felt::from_bytes_be_slice(
[
(self.transaction_count as u64),
(self.events_count as u64),
(self.state_diff_length as u64),
match self.l1_da_mode {
// 0b0000_0000 ++ 7 bytes 0 padding
L1DaMode::Calldata => 0_u64,
// 0b1000_0000 ++ 7 bytes 0 padding
L1DaMode::Blob => 1 << 63,
},
]
.map(u64::to_be_bytes)
.as_flattened(),
),
Felt::from_bytes_be(self.state_diff_commitment.get()),
Felt::from_bytes_be(self.transactions_commitment.get()),
Felt::from_bytes_be(self.events_commitment.get()),
Felt::from_bytes_be(self.receipts_commitment.get()),
Poseidon::hash_array(&[
// hex(b"STARKNET_GAS_PRICES0")
const { Felt::from_hex_unwrap("0x535441524b4e45545f4741535f50524943455330") },
self.l1_gas_price.0.into(),
self.l1_gas_price.1.into(),
self.l1_data_gas_price.0.into(),
self.l1_data_gas_price.1.into(),
self.l2_gas_price.0.into(),
self.l2_gas_price.1.into(),
]),
Felt::from_bytes_be_slice(self.protocol_version.as_bytes()),
Felt::ZERO,
Felt::from_bytes_be(self.parent_block_hash.get()),
])
.to_bytes_be()
.into()
}
}

#[test]
fn l2_block_hash() {
use hex_literal::hex;

// https://feeder.alpha-mainnet.starknet.io/feeder_gateway/get_block?blockNumber=3996475
let block = L2Block {
block_number: 3996475,
parent_block_hash: hex!("07488afa914e19281d6a859f1673d91f84b124576677bc90790954934bcf6a90")
.into(),
global_state_root: hex!("000b977d63eeb59fda732ff60c6b956a91bd1c30784b2a25829f3a5fd882b0f8")
.into(),
sequencer_address: hex!("01176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8")
.into(),
block_timestamp: 1764693045,
transaction_count: 8,
events_count: 14 + 7 + 104 + 5 + 3 + 7 + 5 + 5,
state_diff_length: 108,
state_diff_commitment: hex!(
"000d69e24d96773a920991dcd7f86fea0526acb3dae9bb3955caf840c71b54f6"
)
.into(),
transactions_commitment: hex!(
"01df3ce5acd86d8c2d7f1155997a70a004ee0a0c36c67c9baafe87ace22f30d9"
)
.into(),
events_commitment: hex!("030a53d5d62958b18f1094b66c4ad4c3bcee8dd2a36666fc5fc8b46ddaa5b37c")
.into(),
receipts_commitment: hex!(
"0494e30696606f6208ac02b701f2350460c35b0be17cdf23e4017c79a6a69f2f"
)
.into(),
l1_gas_price: (0x6df5cf40, 0x27d11e1709d4),
l1_data_gas_price: (0x1, 0x5cb2),
l2_gas_price: (0x1edd2, 0xb2d05e00),
l1_da_mode: L1DaMode::Blob,
protocol_version: "0.14.0".to_owned(),
};

dbg!(&block);

assert_eq!(
block.hash(),
<H256>::new(hex!(
"0366cae7718ded291ef9c5f4c2aba8c3c27baa0e563fd64ba72fe51c2abc4675"
))
);
}
11 changes: 11 additions & 0 deletions lib/starknet-light-client-types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub mod client_state;
pub mod consensus_state;
pub mod header;
pub mod storage_proof;

pub use crate::{
client_state::{ClientState, ClientStateV1},
consensus_state::ConsensusState,
header::Header,
storage_proof::StorageProof,
};
24 changes: 24 additions & 0 deletions lib/starknet-light-client-types/src/storage_proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use unionlabs::primitives::H256;

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(deny_unknown_fields, rename_all = "snake_case")
)]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
pub struct StorageProof {
nodes: Vec<MerkleNode>,
}

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(deny_unknown_fields, rename_all = "snake_case")
)]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
pub enum MerkleNode {
BinaryNode { left: H256, right: H256 },
EdgeNode { path: H256, length: u8, child: H256 },
}
15 changes: 10 additions & 5 deletions lib/starknet-storage-verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ repository = { workspace = true }
workspace = true

[dependencies]
serde = { workspace = true, features = ["derive"] }
serde-utils = { workspace = true }
starknet-crypto = { workspace = true }
bitvec = { workspace = true }
pathfinder-crypto = "0.21.3"
serde = { workspace = true, optional = true, features = ["derive"] }

[dev-dependencies]
hex-literal = { workspace = true }
serde_json = { workspace = true }
serde_json = { workspace = true }
starknet-storage-verifier = { workspace = true, features = ["serde"] }

[features]
default = []

serde = ["dep:serde"]
Loading
Loading