Skip to content

Commit

Permalink
test(katana): feeder gateway tests (#3038)
Browse files Browse the repository at this point in the history
  • Loading branch information
kariy authored Feb 17, 2025
1 parent dfe9632 commit ce8c3f7
Show file tree
Hide file tree
Showing 45 changed files with 216,140 additions and 109 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ fn to_execution_resources(
katana_primitives::trace::ExecutionResources {
n_steps: resources.n_steps,
n_memory_holes: resources.n_memory_holes,
builtin_instance_counter: resources.builtin_instance_counter,
builtin_instance_counter: resources.builtin_instance_counter.into(),
}
}

Expand Down
3 changes: 3 additions & 0 deletions crates/katana/feeder-gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ tracing.workspace = true
url.workspace = true

[dev-dependencies]
rstest.workspace = true
serde_json.workspace = true
similar-asserts.workspace = true
tokio.workspace = true
16 changes: 8 additions & 8 deletions crates/katana/feeder-gateway/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,23 @@ pub use receipt::*;
pub use transaction::*;

/// The contract class type returns by `/get_class_by_hash` endpoint.
#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(untagged)]
pub enum ContractClass {
Class(RpcSierraContractClass),
Legacy(LegacyContractClass),
}

/// The state update type returns by `/get_state_update` endpoint.
#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct StateUpdate {
pub block_hash: Option<Felt>,
pub new_root: Option<Felt>,
pub old_root: Felt,
pub state_diff: StateDiff,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct StateDiff {
pub storage_diffs: BTreeMap<ContractAddress, Vec<StorageDiff>>,
pub deployed_contracts: Vec<DeployedContract>,
Expand All @@ -49,26 +49,26 @@ pub struct StateDiff {
pub replaced_classes: Vec<DeployedContract>,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct StorageDiff {
pub key: StorageKey,
pub value: StorageValue,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct DeployedContract {
pub address: ContractAddress,
pub class_hash: Felt,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct DeclaredContract {
pub class_hash: ClassHash,
pub compiled_class_hash: CompiledClassHash,
}

/// The state update type returns by `/get_state_update` endpoint, with `includeBlock=true`.
#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct StateUpdateWithBlock {
pub state_update: StateUpdate,
pub block: Block,
Expand All @@ -78,7 +78,7 @@ pub struct StateUpdateWithBlock {
// the serde impl is different. So for now, lets just use starknet-rs types. The type isn't
// that complex anyway so the conversion is simple. But if we can use the primitive types, we
// should.
#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct Block {
#[serde(default)]
pub block_hash: Option<BlockHash>,
Expand Down
171 changes: 162 additions & 9 deletions crates/katana/feeder-gateway/src/types/receipt.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,175 @@
use std::collections::HashMap;

use katana_primitives::receipt::{Event, MessageToL1};
use katana_primitives::Felt;
use katana_primitives::trace::{self, BuiltinCounters, BuiltinName};
use katana_primitives::{eth, Felt};
use serde::Deserialize;
use starknet::providers::sequencer::models::{
ExecutionResources, L1ToL2Message, TransactionExecutionStatus,
};

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct ConfirmedReceipt {
pub transaction_hash: Felt,
pub transaction_index: u64,
#[serde(default)]
pub execution_status: Option<TransactionExecutionStatus>,
#[serde(default)]
pub execution_status: Option<ExecutionStatus>,
pub revert_error: Option<String>,
#[serde(default)]
pub execution_resources: Option<ExecutionResources>,
pub l1_to_l2_consumed_message: Option<L1ToL2Message>,
pub l2_to_l1_messages: Vec<MessageToL1>,
pub events: Vec<Event>,
pub actual_fee: Felt,
}

#[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ExecutionStatus {
Succeeded,
Reverted,
}

#[derive(Debug, PartialEq, Eq)]
pub struct ExecutionResources {
pub vm_resources: trace::ExecutionResources,
pub data_availability: Option<DataAvailabilityResources>,
}

#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct DataAvailabilityResources {
pub l1_gas: u64,
pub l1_data_gas: u64,
}

#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct L1ToL2Message {
/// The address of the Ethereum (L1) contract that sent the message.
pub from_address: eth::Address,
pub to_address: Felt,
pub selector: Felt,
pub payload: Vec<Felt>,
pub nonce: Option<Felt>,
}

// The reason why we implement `Deserialize` manually is because we want to avoid defining redundant
// types just because the format is different than the already existing types.
impl<'de> Deserialize<'de> for ExecutionResources {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct BuiltinCounterHelper(BuiltinCounters);

impl<'de> Deserialize<'de> for BuiltinCounterHelper {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
enum Field {
Ignore,
Valid(BuiltinName),
}

struct FieldVisitor;

impl<'de> serde::de::Visitor<'de> for FieldVisitor {
type Value = Field;

fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(
formatter,
"a builtin type name: 'ecdsa_builtin', 'ec_op_builtin', \
'keccak_builtin', 'output_builtin', 'bitwise_builtin', \
'pedersen_builtin', 'poseidon_builtin', 'range_check_builtin', \
'segment_arena_builtin'"
)
}

fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
match v {
"ecdsa_builtin" => Ok(Field::Valid(BuiltinName::ecdsa)),
"ec_op_builtin" => Ok(Field::Valid(BuiltinName::ec_op)),
"keccak_builtin" => Ok(Field::Valid(BuiltinName::keccak)),
"output_builtin" => Ok(Field::Valid(BuiltinName::output)),
"bitwise_builtin" => Ok(Field::Valid(BuiltinName::bitwise)),
"pedersen_builtin" => Ok(Field::Valid(BuiltinName::pedersen)),
"poseidon_builtin" => Ok(Field::Valid(BuiltinName::poseidon)),
"range_check_builtin" => Ok(Field::Valid(BuiltinName::range_check)),
"segment_arena_builtin" => Ok(Field::Valid(BuiltinName::segment_arena)),
_ => Ok(Field::Ignore),
}
}
}

impl<'de> serde::Deserialize<'de> for Field {
#[inline]
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
serde::Deserializer::deserialize_identifier(deserializer, FieldVisitor)
}
}

struct Visitor;

impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = BuiltinCounterHelper;

fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(
formatter,
"an JSON object with builtin names as keys and instance counts as \
values"
)
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut builtins: HashMap<BuiltinName, usize> = HashMap::new();
while let Some(key) = map.next_key::<Field>()? {
match key {
Field::Valid(builtin) => {
if builtins.contains_key(&builtin) {
return Err(
<A::Error as serde::de::Error>::duplicate_field(
builtin.to_str_with_suffix(),
),
);
}

if let Some(counter) = map.next_value::<Option<u64>>()? {
builtins.insert(builtin, counter as usize);
}
}

Field::Ignore => {
let _ = map.next_value::<serde::de::IgnoredAny>()?;
}
}
}
Ok(BuiltinCounterHelper(BuiltinCounters::from(builtins)))
}
}

deserializer.deserialize_map(Visitor)
}
}

#[derive(Deserialize)]
pub struct Helper {
pub n_steps: usize,
pub n_memory_holes: usize,
pub builtin_instance_counter: BuiltinCounterHelper,
pub data_availability: Option<DataAvailabilityResources>,
}

let helper = Helper::deserialize(deserializer)?;

Ok(Self {
data_availability: helper.data_availability,
vm_resources: trace::ExecutionResources {
n_steps: helper.n_steps,
n_memory_holes: helper.n_memory_holes,
builtin_instance_counter: helper.builtin_instance_counter.0,
},
})
}
}
18 changes: 9 additions & 9 deletions crates/katana/feeder-gateway/src/types/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ use super::serde_utils::{
deserialize_optional_u128, deserialize_optional_u64, deserialize_u128, deserialize_u64,
};

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct ConfirmedTransaction {
#[serde(rename = "transaction_hash")]
pub hash: TxHash,
#[serde(flatten)]
pub tx: TypedTransaction,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TypedTransaction {
Deploy(DeployTx),
Expand All @@ -34,7 +34,7 @@ pub enum TypedTransaction {
// different from the one in the `katana_primitives` crate. And changing the serde implementation in
// the `katana_primitives` crate would break the database format. So, we have to define the type
// again. But see if we can remove it once we're okay with breaking the database format.
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub enum DataAvailabilityMode {
L1,
L2,
Expand All @@ -58,22 +58,22 @@ impl<'de> Deserialize<'de> for DataAvailabilityMode {

// Same reason as `DataAvailabilityMode` above, this struct is also defined because the serde
// implementation of its primitive counterpart is different.
#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct ResourceBounds {
#[serde(deserialize_with = "deserialize_u64")]
pub max_amount: u64,
#[serde(deserialize_with = "deserialize_u128")]
pub max_price_per_unit: u128,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct ResourceBoundsMapping {
pub l1_gas: ResourceBounds,
pub l2_gas: ResourceBounds,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct RawL1HandlerTx {
/// The L1 to L2 message nonce.
pub nonce: Option<Nonce>,
Expand All @@ -87,7 +87,7 @@ pub struct RawL1HandlerTx {
pub entry_point_selector: Felt,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct RawInvokeTx {
// Alias for v0 transaction
#[serde(alias = "contract_address")]
Expand Down Expand Up @@ -118,7 +118,7 @@ pub struct RawInvokeTx {
pub version: Felt,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct RawDeclareTx {
pub sender_address: ContractAddress,
pub nonce: Felt,
Expand All @@ -144,7 +144,7 @@ pub struct RawDeclareTx {
pub version: Felt,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, PartialEq, Eq, Deserialize)]
pub struct RawDeployAccountTx {
pub nonce: Nonce,
pub signature: Vec<Felt>,
Expand Down
Loading

0 comments on commit ce8c3f7

Please sign in to comment.