Conversation
| pub enum StarknetFinality { | ||
| AcceptedOnL2, | ||
| AcceptedOnL1, | ||
| } |
There was a problem hiding this comment.
Starknet has a few more statuses we could support (see https://docs.starknet.io/learn/protocol/transactions#transaction-statuses), but I think this is a sensible start.
f7d42a3 to
c26c10d
Compare
| where | ||
| Client: ClientT + Send, |
There was a problem hiding this comment.
These trait bounds are redundant, and should be removed. But this mirrors how it's done in the other extractors so I prefer keeping it consistent for now.
crates/primitives/src/hash.rs
Outdated
| } | ||
|
|
||
| #[test] | ||
| fn test_as_hex_with_prefix() { |
There was a problem hiding this comment.
While I'd normally push for the new naming convention here (e.g. something like as_hex_with_prefix__should_result_in_prefixed_hex), this would stand out a bit too much in this test module - and this test is trivial so I chose to keep it this way for now.
There was a problem hiding this comment.
This config update is relatively stand-alone and could be broken out, but it felt nice to add it since it enables this to be verified end to end.
| }) | ||
| .collect::<anyhow::Result<_>>()? | ||
| } | ||
| dtos::ForeignChainRpcRequest::Starknet(request) => { |
There was a problem hiding this comment.
Similar to the config change, this wraps up the feature end to end so it's nice to add but is separate from the inspector.
c22269d to
9898996
Compare
Code ReviewFound several critical issues that need fixing before merge: 1. ❌ Unsafe Wildcard Pattern Matching (Production Safety)Location: let finality = match request.finality {
dtos::StarknetFinality::AcceptedOnL2 => StarknetFinality::AcceptedOnL2,
dtos::StarknetFinality::AcceptedOnL1 => StarknetFinality::AcceptedOnL1,
_ => bail\!("unknown starknet finality level"), // ❌ Dangerous wildcard
};Problem: Fix: Remove the wildcard and match exhaustively: let finality = match request.finality {
dtos::StarknetFinality::AcceptedOnL2 => StarknetFinality::AcceptedOnL2,
dtos::StarknetFinality::AcceptedOnL1 => StarknetFinality::AcceptedOnL1,
};The compiler will force you to update this code when new variants are added. 2. ❌ Same Issue with Extractor MatchingLocation: .map(|extractor| match extractor {
dtos::StarknetExtractor::BlockHash => Ok(StarknetExtractor::BlockHash),
_ => bail\!("unknown extractor found"), // ❌ Same problem
})
3.
|
|
Sorry Claude, but this wasn't your best review. 1. and 2. are needed since the enums are non-exhaustive as you already noted. Also the existing bitcoin logic follows the same pattern. 3. is wrong since this is a string and 4. isn't really important right now. |
| fn deserialize_starknet_felt_hash<'de, D>( | ||
| deserializer: D, | ||
| ) -> Result<TransportStarknetBlockHash, D::Error> | ||
| where | ||
| D: Deserializer<'de>, | ||
| { | ||
| let hash = String::deserialize(deserializer)?; | ||
| let stripped = hash.strip_prefix("0x").unwrap_or(&hash); | ||
|
|
||
| if stripped.len() > 64 { | ||
| return Err(serde::de::Error::custom(format!( | ||
| "felt hex string too long: {hash}" | ||
| ))); | ||
| } | ||
|
|
||
| let padded = format!("{stripped:0>64}"); | ||
| padded.parse().map_err(|error| { | ||
| serde::de::Error::custom(format!("invalid starknet felt hash {hash}: {error}")) | ||
| }) | ||
| } |
There was a problem hiding this comment.
Seems like the starknet response schema is the same as EVM/Ethereum by prefixing with 0x. You can reuse the ethereum_types::H256 crate to avoid these manual serialization implementations.
There was a problem hiding this comment.
Updated in 4c13d4a - unfortunately starknet allows stripped-prefix hashes so we still have some custom deserialization logic. But it's cleaner still.
Another alternative would be to drag in some starknet-specific dependency for this but that feels overkill for just hex deserialization.
DSharifi
left a comment
There was a problem hiding this comment.
Mostly looks good.
But I want to discuss if it makes sense to implement the manual (de)serialization of hex strings with 0x prefix.
They (starknet) are reusing a lot of the same format from EVM from their official spec, such as this 0x prefixing and the name of the methods https://github.com/starkware-libs/starknet-specs/blob/master/starknet_vs_ethereum_node_apis.md
In that case, I think we should reuse the ethereum_types crates we already depend on from parity which provides all this (de)serialization logic built in.
9106a16 to
4c13d4a
Compare
gilcu3
left a comment
There was a problem hiding this comment.
Thank you!
Requesting changes because we have a problem with the manual test:
FAIL [ 0.105s] foreign-chain-inspector::starknet_rpc_manual inspector_extracts_block_hash_against_live_rpc_provider
stdout ───
running 1 test
test inspector_extracts_block_hash_against_live_rpc_provider ... FAILED
failures:
failures:
inspector_extracts_block_hash_against_live_rpc_provider
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s
stderr ───
thread 'inspector_extracts_block_hash_against_live_rpc_provider' panicked at crates/foreign-chain-inspector/tests/starknet_rpc_manual.rs:26:10:
latest block should be fetched: ParseError(Error("invalid length 63, expected a (both 0x-prefixed or not) hex string or byte array containing 32 bytes", line: 1, column: 107))
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
| @@ -0,0 +1,17 @@ | |||
| use mpc_primitives::hash::Hash32; | |||
There was a problem hiding this comment.
I really have a hard time figuring when a type should come from one crate or another. In this case I naively expected this to come from the contract-interface crate, but it comes from mpc-primitives instead. Do we have a well-defined boundary between these two crates? I am all in for merging them if not
There was a problem hiding this comment.
Yeah I think this isn't completely obvious for some cases, here's how I'd do it:
Complex types used in the contract interface -> contract-interface
Primitive types used for things unrelated to the interface -> mpc-primitives
Primitive types only used for the interface -> contract-interface
I think the last one is the most interesting case, because the two above are fairly obvious. There are crates that may need some primitives that don't rely on the contract interface and those primitives should obviously live in mpc-primitives. And anything that's not a primitive should not live there.
For the last case, both would work so I have no strong opinion but here it's better to maintain locality if the primitive isn't expected to be used anywhere else.
There was a problem hiding this comment.
Thanks for the detailed explanation. What is a "primitive" type in this context? Aren't we using it as a synonym of simply "mostly non-interface type"? What is the difference between the crate node-types and mpc-primitives ? I guess that node-types is only about the node, but then why would it be outside of the node?
Thanks for the observation. This passed before, so it has to do with the hash type change probably. I'll fix it. |
a5f59aa to
504af02
Compare
closes #2054