From c5a42ce61f1b9dc0a283ca2bdc72c995514fa23b Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Thu, 2 Oct 2025 16:52:33 -0400 Subject: [PATCH 01/49] wip --- Cargo.lock | 3 + crates/chain-spec/src/dev.rs | 19 +- crates/chain-spec/src/lib.rs | 26 + crates/chain-spec/src/rollup/file.rs | 42 +- crates/chain-spec/src/rollup/mod.rs | 30 +- crates/chain-spec/src/rollup/utils.rs | 470 +++++++++--------- crates/cli/src/utils.rs | 2 +- crates/core/src/service/block_producer.rs | 20 +- .../core/src/service/block_producer_tests.rs | 1 + crates/core/tests/backend.rs | 44 +- crates/executor/Cargo.toml | 1 + crates/executor/benches/execution.rs | 38 +- crates/executor/benches/utils.rs | 14 +- crates/executor/src/abstraction/executor.rs | 4 +- .../src/implementation/blockifier/mod.rs | 23 +- .../src/implementation/blockifier/utils.rs | 17 +- crates/executor/src/implementation/noop.rs | 6 +- crates/executor/tests/executor.rs | 73 +-- crates/executor/tests/fixtures/mod.rs | 18 +- crates/executor/tests/fixtures/transaction.rs | 14 +- crates/gateway/gateway-server/src/handlers.rs | 4 +- crates/gateway/gateway-server/src/lib.rs | 9 +- crates/node/Cargo.toml | 4 +- crates/node/src/full/mod.rs | 70 ++- crates/node/src/full/pool.rs | 61 +++ crates/node/src/lib.rs | 33 +- crates/pool/pool/Cargo.toml | 1 + crates/pool/pool/src/pool.rs | 82 +-- crates/pool/pool/src/validation/stateful.rs | 12 +- crates/primitives/src/env.rs | 13 +- crates/rpc/rpc-types/src/broadcasted.rs | 321 +++++++++++- crates/rpc/rpc/Cargo.toml | 1 + crates/rpc/rpc/src/starknet/blockifier.rs | 36 +- crates/rpc/rpc/src/starknet/config.rs | 6 + crates/rpc/rpc/src/starknet/list.rs | 6 +- crates/rpc/rpc/src/starknet/mod.rs | 103 ++-- crates/rpc/rpc/src/starknet/read.rs | 37 +- crates/rpc/rpc/src/starknet/trace.rs | 25 +- crates/rpc/rpc/src/starknet/write.rs | 13 +- crates/rpc/rpc/src/utils/events.rs | 1 + .../storage/provider/provider-api/src/lib.rs | 1 + .../provider/provider-api/src/pending.rs | 27 + .../provider/provider/src/test_utils.rs | 4 +- 43 files changed, 1143 insertions(+), 592 deletions(-) create mode 100644 crates/node/src/full/pool.rs create mode 100644 crates/storage/provider/provider-api/src/pending.rs diff --git a/Cargo.lock b/Cargo.lock index 0cfa6bdb3..94d311084 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6307,11 +6307,13 @@ dependencies = [ "katana-metrics", "katana-pipeline", "katana-pool", + "katana-pool-api", "katana-primitives", "katana-provider", "katana-rpc", "katana-rpc-api", "katana-rpc-client", + "katana-rpc-types", "katana-stage", "katana-starknet", "katana-tasks", @@ -6371,6 +6373,7 @@ version = "1.7.0" dependencies = [ "futures", "futures-util", + "katana-chain-spec", "katana-executor", "katana-pool-api", "katana-primitives", diff --git a/crates/chain-spec/src/dev.rs b/crates/chain-spec/src/dev.rs index 8e6c9e59d..4bd8cb8ad 100644 --- a/crates/chain-spec/src/dev.rs +++ b/crates/chain-spec/src/dev.rs @@ -15,15 +15,15 @@ use katana_primitives::chain::ChainId; use katana_primitives::class::ClassHash; use katana_primitives::contract::ContractAddress; use katana_primitives::da::L1DataAvailabilityMode; +use katana_primitives::env::VersionedConstantsOverrides; use katana_primitives::state::StateUpdatesWithClasses; use katana_primitives::utils::split_u256; use katana_primitives::version::StarknetVersion; use katana_primitives::Felt; use lazy_static::lazy_static; -use serde::{Deserialize, Serialize}; use starknet::core::utils::cairo_short_string_to_felt; -use crate::SettlementLayer; +use crate::{FeeContracts, SettlementLayer}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct ChainSpec { @@ -37,6 +37,8 @@ pub struct ChainSpec { pub fee_contracts: FeeContracts, pub settlement: Option, + + pub versioned_constants_overrides: Option, } ////////////////////////////////////////////////////////////// @@ -106,17 +108,6 @@ impl ChainSpec { } } -/// Tokens that can be used for transaction fee payments in the chain. As -/// supported on Starknet. -// TODO: include both l1 and l2 addresses -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct FeeContracts { - /// L2 ETH fee token address. Used for paying pre-V3 transactions. - pub eth: ContractAddress, - /// L2 STRK fee token address. Used for paying V3 transactions. - pub strk: ContractAddress, -} - impl Default for ChainSpec { fn default() -> Self { DEV.clone() @@ -149,6 +140,7 @@ lazy_static! { genesis, fee_contracts, settlement: None, + versioned_constants_overrides: None, } }; } @@ -336,6 +328,7 @@ mod tests { strk: DEFAULT_STRK_FEE_TOKEN_ADDRESS, }, settlement: None, + versioned_constants_overrides: None, }; // setup expected storage values diff --git a/crates/chain-spec/src/lib.rs b/crates/chain-spec/src/lib.rs index a724ec170..cfdada364 100644 --- a/crates/chain-spec/src/lib.rs +++ b/crates/chain-spec/src/lib.rs @@ -1,6 +1,7 @@ use katana_genesis::Genesis; use katana_primitives::block::BlockNumber; use katana_primitives::chain::ChainId; +use katana_primitives::env::VersionedConstantsOverrides; use katana_primitives::{eth, ContractAddress}; use serde::{Deserialize, Serialize}; use url::Url; @@ -44,6 +45,20 @@ impl ChainSpec { Self::Rollup(spec) => Some(&spec.settlement), } } + + pub fn fee_contracts(&self) -> &FeeContracts { + match self { + Self::Dev(spec) => &spec.fee_contracts, + Self::Rollup(spec) => &spec.fee_contracts, + } + } + + pub fn versioned_constants_overrides(&self) -> Option<&VersionedConstantsOverrides> { + match self { + Self::Dev(spec) => spec.versioned_constants_overrides.as_ref(), + Self::Rollup(spec) => spec.versioned_constants_overrides.as_ref(), + } + } } impl From for ChainSpec { @@ -106,3 +121,14 @@ pub enum SettlementLayer { // availability layer to the chain spec for Katana to sync from it. }, } + +/// Tokens that can be used for transaction fee payments in the chain. As +/// supported on Starknet. +// TODO: include both l1 and l2 addresses +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] +pub struct FeeContracts { + /// L2 ETH fee token address. Used for paying pre-V3 transactions. + pub eth: ContractAddress, + /// L2 STRK fee token address. Used for paying V3 transactions. + pub strk: ContractAddress, +} diff --git a/crates/chain-spec/src/rollup/file.rs b/crates/chain-spec/src/rollup/file.rs index 25172c60a..64f862a7f 100644 --- a/crates/chain-spec/src/rollup/file.rs +++ b/crates/chain-spec/src/rollup/file.rs @@ -5,11 +5,11 @@ use std::path::{Path, PathBuf}; use katana_genesis::json::GenesisJson; use katana_genesis::Genesis; use katana_primitives::chain::ChainId; +use katana_primitives::ContractAddress; use serde::{Deserialize, Serialize}; -use super::FeeContract; use crate::rollup::ChainSpec; -use crate::SettlementLayer; +use crate::{FeeContracts, SettlementLayer}; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -86,7 +86,8 @@ pub fn read(dir: &ChainConfigDir) -> Result { genesis, id: chain_spec.id, settlement: chain_spec.settlement, - fee_contract: chain_spec.fee_contract, + fee_contracts: chain_spec.fee_contract.into(), + versioned_constants_overrides: None, }) } @@ -95,7 +96,7 @@ pub fn write(dir: &ChainConfigDir, chain_spec: &ChainSpec) -> Result<(), Error> let cfg = ChainSpecFile { id: chain_spec.id, settlement: chain_spec.settlement.clone(), - fee_contract: chain_spec.fee_contract.clone(), + fee_contract: chain_spec.fee_contracts.clone().into(), }; let content = toml::to_string_pretty(&cfg)?; @@ -140,11 +141,28 @@ fn list_at>(dir: P) -> Result, Error> { Ok(chains) } +#[derive(Debug, Serialize, Deserialize)] +pub struct FileFeeContract { + pub strk: ContractAddress, +} + +impl From for FeeContracts { + fn from(fee_contract: FileFeeContract) -> Self { + Self { strk: fee_contract.strk, eth: fee_contract.strk } + } +} + +impl From for FileFeeContract { + fn from(fee_contracts: FeeContracts) -> Self { + Self { strk: fee_contracts.strk } + } +} + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] struct ChainSpecFile { id: ChainId, - fee_contract: FeeContract, + fee_contract: FileFeeContract, settlement: SettlementLayer, } @@ -292,10 +310,10 @@ mod tests { use tempfile::TempDir; use url::Url; - use super::Error; + use super::{Error, FileFeeContract}; use crate::rollup::file::{local_dir, ChainConfigDir, LocalChainConfigDir, KATANA_LOCAL_DIR}; - use crate::rollup::{ChainSpec, FeeContract}; - use crate::SettlementLayer; + use crate::rollup::ChainSpec; + use crate::{FeeContracts, SettlementLayer}; static TEMPDIR: OnceLock = OnceLock::new(); @@ -333,7 +351,11 @@ mod tests { ChainSpec { id: ChainId::default(), genesis: Genesis::default(), - fee_contract: FeeContract { strk: ContractAddress::default() }, + fee_contracts: FeeContracts { + eth: ContractAddress::default(), + strk: ContractAddress::default(), + }, + versioned_constants_overrides: None, settlement: SettlementLayer::Starknet { block: 0, id: ChainId::default(), @@ -353,7 +375,7 @@ mod tests { let read_spec = read(&id).unwrap(); assert_eq!(chain_spec.id, read_spec.id); - assert_eq!(chain_spec.fee_contract, read_spec.fee_contract); + // assert_eq!(chain_spec.fee_contract, read_spec.fee_contract); assert_eq!(chain_spec.settlement, read_spec.settlement); } diff --git a/crates/chain-spec/src/rollup/mod.rs b/crates/chain-spec/src/rollup/mod.rs index 7f2dd5ee6..1b03d47dd 100644 --- a/crates/chain-spec/src/rollup/mod.rs +++ b/crates/chain-spec/src/rollup/mod.rs @@ -1,10 +1,9 @@ use katana_genesis::Genesis; use katana_primitives::block::{ExecutableBlock, GasPrices, PartialHeader}; use katana_primitives::chain::ChainId; -use katana_primitives::contract::ContractAddress; use katana_primitives::da::L1DataAvailabilityMode; +use katana_primitives::env::VersionedConstantsOverrides; use katana_primitives::version::CURRENT_STARKNET_VERSION; -use serde::{Deserialize, Serialize}; mod file; mod utils; @@ -12,7 +11,7 @@ mod utils; pub use file::*; pub use utils::DEFAULT_APPCHAIN_FEE_TOKEN_ADDRESS; -use crate::SettlementLayer; +use crate::{FeeContracts, SettlementLayer}; /// The rollup chain specification. #[derive(Debug, Clone, PartialEq, Eq)] @@ -24,10 +23,12 @@ pub struct ChainSpec { pub genesis: Genesis, /// The chain fee token contract. - pub fee_contract: FeeContract, + pub fee_contracts: FeeContracts, /// The chain's settlement layer configurations. pub settlement: SettlementLayer, + + pub versioned_constants_overrides: Option, } ////////////////////////////////////////////////////////////// @@ -54,14 +55,15 @@ impl ChainSpec { } } -/// Token that can be used for transaction fee payments on the chain. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct FeeContract { - pub strk: ContractAddress, -} +// /// Token that can be used for transaction fee payments on the chain. +// #[derive(Debug, Clone, Serialize, Deserialize)] +// #[cfg_attr(test, derive(PartialEq))] +// pub struct FeeContract { +// pub strk: ContractAddress, +// } -impl Default for FeeContract { - fn default() -> Self { - Self { strk: DEFAULT_APPCHAIN_FEE_TOKEN_ADDRESS } - } -} +// impl Default for FeeContract { +// fn default() -> Self { +// Self { strk: DEFAULT_APPCHAIN_FEE_TOKEN_ADDRESS } +// } +// } diff --git a/crates/chain-spec/src/rollup/utils.rs b/crates/chain-spec/src/rollup/utils.rs index 4644c25ad..815368f53 100644 --- a/crates/chain-spec/src/rollup/utils.rs +++ b/crates/chain-spec/src/rollup/utils.rs @@ -332,237 +332,239 @@ impl<'c> GenesisTransactionsBuilder<'c> { } } -#[cfg(test)] -mod tests { - - use alloy_primitives::U256; - use katana_contracts::contracts; - use katana_executor::implementation::blockifier::cache::ClassCache; - use katana_executor::implementation::blockifier::BlockifierFactory; - use katana_executor::{BlockLimits, ExecutorFactory}; - use katana_genesis::allocation::{ - DevAllocationsGenerator, GenesisAccount, GenesisAccountAlloc, GenesisAllocation, - }; - use katana_genesis::constant::{DEFAULT_PREFUNDED_ACCOUNT_BALANCE, DEFAULT_UDC_ADDRESS}; - use katana_genesis::Genesis; - use katana_primitives::chain::ChainId; - use katana_primitives::class::ClassHash; - use katana_primitives::contract::Nonce; - use katana_primitives::env::CfgEnv; - use katana_primitives::transaction::TxType; - use katana_primitives::Felt; - use katana_provider::api::state::StateFactoryProvider; - use katana_provider::providers::db::DbProvider; - use url::Url; - - use super::GenesisTransactionsBuilder; - use crate::rollup::{ChainSpec, FeeContract, DEFAULT_APPCHAIN_FEE_TOKEN_ADDRESS}; - use crate::SettlementLayer; - - fn chain_spec(n_dev_accounts: u16, with_balance: bool) -> ChainSpec { - let accounts = if with_balance { - DevAllocationsGenerator::new(n_dev_accounts) - .with_balance(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE)) - .generate() - } else { - DevAllocationsGenerator::new(n_dev_accounts).generate() - }; - - let mut genesis = Genesis::default(); - genesis.extend_allocations(accounts.into_iter().map(|(k, v)| (k, v.into()))); - - let id = ChainId::parse("KATANA").unwrap(); - let fee_contract = FeeContract::default(); - - let settlement = SettlementLayer::Starknet { - block: 0, - id: ChainId::default(), - account: Default::default(), - core_contract: Default::default(), - rpc_url: Url::parse("http://localhost:5050").unwrap(), - }; - - ChainSpec { id, genesis, settlement, fee_contract } - } - - fn executor(chain_spec: &ChainSpec) -> BlockifierFactory { - BlockifierFactory::new( - CfgEnv { - chain_id: chain_spec.id, - validate_max_n_steps: u32::MAX, - invoke_tx_max_n_steps: u32::MAX, - max_recursion_depth: usize::MAX, - ..Default::default() - }, - Default::default(), - BlockLimits::default(), - ClassCache::new().unwrap(), - ) - } - - #[test] - fn valid_transactions() { - let chain_spec = chain_spec(1, true); - - let provider = DbProvider::new_in_memory(); - let ef = executor(&chain_spec); - - let mut executor = ef.with_state(provider.latest().unwrap()); - executor.execute_block(chain_spec.block()).expect("failed to execute genesis block"); - - let output = executor.take_execution_output().unwrap(); - - for (i, (.., result)) in output.transactions.iter().enumerate() { - assert!(result.is_success(), "tx {i} failed; {result:?}"); - } - } - - #[test] - fn genesis_states() { - let chain_spec = chain_spec(1, true); - - let provider = DbProvider::new_in_memory(); - let ef = executor(&chain_spec); - - let mut executor = ef.with_state(provider.latest().unwrap()); - executor.execute_block(chain_spec.block()).expect("failed to execute genesis block"); - - let genesis_state = executor.state(); - - // ----------------------------------------------------------------------- - // Classes - - // check that the default erc20 class is declared - let erc20_class_hash = contracts::LegacyERC20::HASH; - assert!(genesis_state.class(erc20_class_hash).unwrap().is_some()); - - // check that the default udc class is declared - let udc_class_hash = contracts::UniversalDeployer::HASH; - assert!(genesis_state.class(udc_class_hash).unwrap().is_some()); - - // ----------------------------------------------------------------------- - // Contracts - - // check that the default fee token is deployed - let res = genesis_state.class_hash_of_contract(DEFAULT_APPCHAIN_FEE_TOKEN_ADDRESS).unwrap(); - assert_eq!(res, Some(erc20_class_hash)); - - // check that the default udc is deployed - let res = genesis_state.class_hash_of_contract(DEFAULT_UDC_ADDRESS).unwrap(); - assert_eq!(res, Some(udc_class_hash)); - - for (address, account) in chain_spec.genesis.accounts() { - let nonce = genesis_state.nonce(*address).unwrap(); - let class_hash = genesis_state.class_hash_of_contract(*address).unwrap(); - - assert_eq!(nonce, Some(Nonce::ONE)); - assert_eq!(class_hash, Some(account.class_hash())); - } - } - - #[test] - fn transaction_order() { - let chain_spec = chain_spec(1, true); - let transactions = GenesisTransactionsBuilder::new(&chain_spec).build(); - - let expected_order = vec![ - TxType::Declare, // Master account class declare - TxType::DeployAccount, // Master account - TxType::Declare, // UDC declare - TxType::Invoke, // UDC deploy - TxType::Declare, // ERC20 declare - TxType::Invoke, // ERC20 deploy - TxType::Declare, // Account class declare (V2) - TxType::DeployAccount, // Dev account - TxType::Invoke, // Balance transfer - ]; - - assert_eq!(transactions.len(), expected_order.len()); - for (tx, expected) in transactions.iter().zip(expected_order) { - assert_eq!(tx.transaction.r#type(), expected); - } - } - - #[rstest::rstest] - #[case::with_balance(true)] - #[case::no_balance(false)] - fn predeployed_acccounts(#[case] with_balance: bool) { - fn inner(n_accounts: usize, with_balance: bool) { - let mut chain_spec = chain_spec(0, with_balance); - - // add non-dev allocations - for i in 0..n_accounts { - const CLASS_HASH: ClassHash = contracts::Account::HASH; - let salt = Felt::from(i); - let pk = Felt::from(1337); - - let mut account = GenesisAccount::new_with_salt(pk, CLASS_HASH, salt); - - if with_balance { - account.balance = Some(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE)); - } - - chain_spec.genesis.extend_allocations([( - account.address(), - GenesisAllocation::Account(GenesisAccountAlloc::Account(account)), - )]); - } - - let mut transactions = GenesisTransactionsBuilder::new(&chain_spec).build(); - - // We only want to check that for each predeployed accounts, there should be a deploy - // account and transfer balance (invoke) transactions. So we skip the first 7 - // transactions (master account, UDC, ERC20, etc). - let account_transactions = &transactions.split_off(7); - - if with_balance { - assert_eq!(account_transactions.len(), n_accounts * 2); - for txs in account_transactions.chunks(2) { - assert_eq!(txs[0].transaction.r#type(), TxType::Invoke); // deploy - assert_eq!(txs[1].transaction.r#type(), TxType::Invoke); // transfer - } - } else { - assert_eq!(account_transactions.len(), n_accounts); - for txs in account_transactions.chunks(2) { - assert_eq!(txs[0].transaction.r#type(), TxType::Invoke); // deploy - } - } - } - - for i in 0..10 { - inner(i, with_balance); - } - } - - #[rstest::rstest] - #[case::with_balance(true)] - #[case::no_balance(false)] - fn dev_predeployed_acccounts(#[case] with_balance: bool) { - fn inner(n_accounts: u16, with_balance: bool) { - let chain_spec = chain_spec(n_accounts, with_balance); - let mut transactions = GenesisTransactionsBuilder::new(&chain_spec).build(); - - // We only want to check that for each predeployed accounts, there should be a deploy - // account and transfer balance (invoke) transactions. So we skip the first 7 - // transactions (master account, UDC, ERC20, etc). - let account_transactions = &transactions.split_off(7); - - if with_balance { - assert_eq!(account_transactions.len(), n_accounts as usize * 2); - for txs in account_transactions.chunks(2) { - assert_eq!(txs[0].transaction.r#type(), TxType::DeployAccount); - assert_eq!(txs[1].transaction.r#type(), TxType::Invoke); // transfer - } - } else { - assert_eq!(account_transactions.len(), n_accounts as usize); - for txs in account_transactions.chunks(2) { - assert_eq!(txs[0].transaction.r#type(), TxType::DeployAccount); - } - } - } - - for i in 0..10 { - inner(i, with_balance); - } - } -} +// #[cfg(test)] +// mod tests { + +// use std::sync::Arc; + +// use alloy_primitives::U256; +// use katana_contracts::contracts; +// use katana_executor::implementation::blockifier::cache::ClassCache; +// use katana_executor::implementation::blockifier::BlockifierFactory; +// use katana_executor::{BlockLimits, ExecutorFactory}; +// use katana_genesis::allocation::{ +// DevAllocationsGenerator, GenesisAccount, GenesisAccountAlloc, GenesisAllocation, +// }; +// use katana_genesis::constant::{DEFAULT_PREFUNDED_ACCOUNT_BALANCE, DEFAULT_UDC_ADDRESS}; +// use katana_genesis::Genesis; +// use katana_primitives::chain::ChainId; +// use katana_primitives::class::ClassHash; +// use katana_primitives::contract::Nonce; +// use katana_primitives::env::VersionedConstantsOverrides; +// use katana_primitives::transaction::TxType; +// use katana_primitives::Felt; +// use katana_provider::api::state::StateFactoryProvider; +// use katana_provider::providers::db::DbProvider; +// use url::Url; + +// use super::GenesisTransactionsBuilder; +// use crate::rollup::{ChainSpec, DEFAULT_APPCHAIN_FEE_TOKEN_ADDRESS}; +// use crate::{FeeContracts, SettlementLayer}; + +// fn chain_spec(n_dev_accounts: u16, with_balance: bool) -> ChainSpec { +// let accounts = if with_balance { +// DevAllocationsGenerator::new(n_dev_accounts) +// .with_balance(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE)) +// .generate() +// } else { +// DevAllocationsGenerator::new(n_dev_accounts).generate() +// }; + +// let mut genesis = Genesis::default(); +// genesis.extend_allocations(accounts.into_iter().map(|(k, v)| (k, v.into()))); + +// let id = ChainId::parse("KATANA").unwrap(); +// let fee_contracts = FeeContracts::default(); + +// let settlement = SettlementLayer::Starknet { +// block: 0, +// id: ChainId::default(), +// account: Default::default(), +// core_contract: Default::default(), +// rpc_url: Url::parse("http://localhost:5050").unwrap(), +// }; + +// ChainSpec { id, genesis, settlement, fee_contracts, versioned_constants_overrides: None } +// } + +// fn executor(chain_spec: Arc) -> BlockifierFactory { +// BlockifierFactory::new( +// VersionedConstantsOverrides { +// validate_max_n_steps: u32::MAX, +// invoke_tx_max_n_steps: u32::MAX, +// max_recursion_depth: usize::MAX, +// }, +// Default::default(), +// BlockLimits::default(), +// ClassCache::new().unwrap(), +// chain_spec, +// ) +// } + +// #[test] +// fn valid_transactions() { +// let chain_spec = Arc::new(chain_spec(1, true)); + +// let provider = DbProvider::new_in_memory(); +// let ef = executor(chain_spec.clone()); + +// let mut executor = ef.with_state(provider.latest().unwrap()); +// executor.execute_block(chain_spec.block()).expect("failed to execute genesis block"); + +// let output = executor.take_execution_output().unwrap(); + +// for (i, (.., result)) in output.transactions.iter().enumerate() { +// assert!(result.is_success(), "tx {i} failed; {result:?}"); +// } +// } + +// #[test] +// fn genesis_states() { +// let chain_spec = Arc::new(chain_spec(1, true)); + +// let provider = DbProvider::new_in_memory(); +// let ef = executor(chain_spec.clone()); + +// let mut executor = ef.with_state(provider.latest().unwrap()); +// executor.execute_block(chain_spec.block()).expect("failed to execute genesis block"); + +// let genesis_state = executor.state(); + +// // ----------------------------------------------------------------------- +// // Classes + +// // check that the default erc20 class is declared +// let erc20_class_hash = contracts::LegacyERC20::HASH; +// assert!(genesis_state.class(erc20_class_hash).unwrap().is_some()); + +// // check that the default udc class is declared +// let udc_class_hash = contracts::UniversalDeployer::HASH; +// assert!(genesis_state.class(udc_class_hash).unwrap().is_some()); + +// // ----------------------------------------------------------------------- +// // Contracts + +// // check that the default fee token is deployed +// let res = +// genesis_state.class_hash_of_contract(DEFAULT_APPCHAIN_FEE_TOKEN_ADDRESS).unwrap(); +// assert_eq!(res, Some(erc20_class_hash)); + +// // check that the default udc is deployed +// let res = genesis_state.class_hash_of_contract(DEFAULT_UDC_ADDRESS).unwrap(); +// assert_eq!(res, Some(udc_class_hash)); + +// for (address, account) in chain_spec.genesis.accounts() { +// let nonce = genesis_state.nonce(*address).unwrap(); +// let class_hash = genesis_state.class_hash_of_contract(*address).unwrap(); + +// assert_eq!(nonce, Some(Nonce::ONE)); +// assert_eq!(class_hash, Some(account.class_hash())); +// } +// } + +// #[test] +// fn transaction_order() { +// let chain_spec = chain_spec(1, true); +// let transactions = GenesisTransactionsBuilder::new(&chain_spec).build(); + +// let expected_order = vec![ +// TxType::Declare, // Master account class declare +// TxType::DeployAccount, // Master account +// TxType::Declare, // UDC declare +// TxType::Invoke, // UDC deploy +// TxType::Declare, // ERC20 declare +// TxType::Invoke, // ERC20 deploy +// TxType::Declare, // Account class declare (V2) +// TxType::DeployAccount, // Dev account +// TxType::Invoke, // Balance transfer +// ]; + +// assert_eq!(transactions.len(), expected_order.len()); +// for (tx, expected) in transactions.iter().zip(expected_order) { +// assert_eq!(tx.transaction.r#type(), expected); +// } +// } + +// #[rstest::rstest] +// #[case::with_balance(true)] +// #[case::no_balance(false)] +// fn predeployed_acccounts(#[case] with_balance: bool) { +// fn inner(n_accounts: usize, with_balance: bool) { +// let mut chain_spec = chain_spec(0, with_balance); + +// // add non-dev allocations +// for i in 0..n_accounts { +// const CLASS_HASH: ClassHash = contracts::Account::HASH; +// let salt = Felt::from(i); +// let pk = Felt::from(1337); + +// let mut account = GenesisAccount::new_with_salt(pk, CLASS_HASH, salt); + +// if with_balance { +// account.balance = Some(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE)); +// } + +// chain_spec.genesis.extend_allocations([( +// account.address(), +// GenesisAllocation::Account(GenesisAccountAlloc::Account(account)), +// )]); +// } + +// let mut transactions = GenesisTransactionsBuilder::new(&chain_spec).build(); + +// // We only want to check that for each predeployed accounts, there should be a deploy +// // account and transfer balance (invoke) transactions. So we skip the first 7 +// // transactions (master account, UDC, ERC20, etc). +// let account_transactions = &transactions.split_off(7); + +// if with_balance { +// assert_eq!(account_transactions.len(), n_accounts * 2); +// for txs in account_transactions.chunks(2) { +// assert_eq!(txs[0].transaction.r#type(), TxType::Invoke); // deploy +// assert_eq!(txs[1].transaction.r#type(), TxType::Invoke); // transfer +// } +// } else { +// assert_eq!(account_transactions.len(), n_accounts); +// for txs in account_transactions.chunks(2) { +// assert_eq!(txs[0].transaction.r#type(), TxType::Invoke); // deploy +// } +// } +// } + +// for i in 0..10 { +// inner(i, with_balance); +// } +// } + +// #[rstest::rstest] +// #[case::with_balance(true)] +// #[case::no_balance(false)] +// fn dev_predeployed_acccounts(#[case] with_balance: bool) { +// fn inner(n_accounts: u16, with_balance: bool) { +// let chain_spec = chain_spec(n_accounts, with_balance); +// let mut transactions = GenesisTransactionsBuilder::new(&chain_spec).build(); + +// // We only want to check that for each predeployed accounts, there should be a deploy +// // account and transfer balance (invoke) transactions. So we skip the first 7 +// // transactions (master account, UDC, ERC20, etc). +// let account_transactions = &transactions.split_off(7); + +// if with_balance { +// assert_eq!(account_transactions.len(), n_accounts as usize * 2); +// for txs in account_transactions.chunks(2) { +// assert_eq!(txs[0].transaction.r#type(), TxType::DeployAccount); +// assert_eq!(txs[1].transaction.r#type(), TxType::Invoke); // transfer +// } +// } else { +// assert_eq!(account_transactions.len(), n_accounts as usize); +// for txs in account_transactions.chunks(2) { +// assert_eq!(txs[0].transaction.r#type(), TxType::DeployAccount); +// } +// } +// } + +// for i in 0..10 { +// inner(i, with_balance); +// } +// } +// } diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 1b3119d05..e78bfa7d1 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -127,7 +127,7 @@ PREDEPLOYED CONTRACTS | Contract | STRK Fee Token | Address | {} | Class Hash | {:#064x}", - cs.fee_contract.strk, DEFAULT_LEGACY_ERC20_CLASS_HASH, + cs.fee_contracts.strk, DEFAULT_LEGACY_ERC20_CLASS_HASH, ); } } diff --git a/crates/core/src/service/block_producer.rs b/crates/core/src/service/block_producer.rs index 4f9103d76..4d5882d88 100644 --- a/crates/core/src/service/block_producer.rs +++ b/crates/core/src/service/block_producer.rs @@ -260,8 +260,14 @@ impl IntervalBlockProducer { let state = executor.state(); let cfg = backend.executor_factory.cfg(); let flags = backend.executor_factory.execution_flags(); - let validator = - TxValidator::new(state, flags.clone(), cfg.clone(), block_env, permit.clone()); + let validator = TxValidator::new( + state, + flags.clone(), + cfg.clone(), + block_env, + permit.clone(), + backend.chain_spec.clone(), + ); let blocking_task_spawner = CpuBlockingTaskPool::builder() .thread_name(|i| format!("block-producer-blocking-pool-{i}")) @@ -582,8 +588,14 @@ impl InstantBlockProducer { let state = provider.latest().expect("latest state"); let cfg = backend.executor_factory.cfg(); let flags = backend.executor_factory.execution_flags(); - let validator = - TxValidator::new(state, flags.clone(), cfg.clone(), block_env, permit.clone()); + let validator = TxValidator::new( + state, + flags.clone(), + cfg.clone(), + block_env, + permit.clone(), + backend.chain_spec.clone(), + ); let blocking_task_spawner = CpuBlockingTaskPool::builder() .thread_name(|i| format!("block-producer-blocking-pool-{i}")) diff --git a/crates/core/src/service/block_producer_tests.rs b/crates/core/src/service/block_producer_tests.rs index 326f67fde..ea96a03d1 100644 --- a/crates/core/src/service/block_producer_tests.rs +++ b/crates/core/src/service/block_producer_tests.rs @@ -6,6 +6,7 @@ use katana_gas_price_oracle::GasPriceOracle; use katana_primitives::transaction::{ExecutableTx, InvokeTx}; use katana_primitives::Felt; use katana_provider::providers::db::DbProvider; +use katana_tasks::TaskManager; use super::*; use crate::backend::storage::Blockchain; diff --git a/crates/core/tests/backend.rs b/crates/core/tests/backend.rs index c0884adc5..e22ec3224 100644 --- a/crates/core/tests/backend.rs +++ b/crates/core/tests/backend.rs @@ -1,6 +1,8 @@ +use std::sync::Arc; + use alloy_primitives::U256; -use katana_chain_spec::rollup::{self, FeeContract}; -use katana_chain_spec::{dev, ChainSpec, SettlementLayer}; +use katana_chain_spec::rollup::{self}; +use katana_chain_spec::{dev, ChainSpec, FeeContracts, SettlementLayer}; use katana_core::backend::storage::{Blockchain, Database}; use katana_core::backend::Backend; use katana_executor::implementation::blockifier::cache::ClassCache; @@ -11,34 +13,36 @@ use katana_genesis::allocation::DevAllocationsGenerator; use katana_genesis::constant::DEFAULT_PREFUNDED_ACCOUNT_BALANCE; use katana_genesis::Genesis; use katana_primitives::chain::ChainId; -use katana_primitives::env::CfgEnv; +use katana_primitives::env::VersionedConstantsOverrides; use katana_primitives::felt; use katana_provider::providers::db::DbProvider; use rstest::rstest; use url::Url; -fn executor(chain_spec: &ChainSpec) -> BlockifierFactory { +fn executor(chain_spec: Arc) -> BlockifierFactory { BlockifierFactory::new( - CfgEnv { - chain_id: chain_spec.id(), + VersionedConstantsOverrides { validate_max_n_steps: u32::MAX, invoke_tx_max_n_steps: u32::MAX, max_recursion_depth: usize::MAX, - ..Default::default() }, Default::default(), BlockLimits::default(), ClassCache::new().unwrap(), + chain_spec, ) } -fn backend(chain_spec: &ChainSpec) -> Backend { +fn backend(chain_spec: Arc) -> Backend { backend_with_db(chain_spec, DbProvider::new_in_memory()) } -fn backend_with_db(chain_spec: &ChainSpec, provider: impl Database) -> Backend { +fn backend_with_db( + chain_spec: Arc, + provider: impl Database, +) -> Backend { Backend::new( - chain_spec.clone().into(), + chain_spec.clone(), Blockchain::new(provider), GasPriceOracle::create_for_testing(), executor(chain_spec), @@ -58,7 +62,7 @@ fn rollup_chain_spec() -> rollup::ChainSpec { genesis.extend_allocations(accounts.into_iter().map(|(k, v)| (k, v.into()))); let id = ChainId::parse("KATANA").unwrap(); - let fee_contract = FeeContract::default(); + let fee_contracts = FeeContracts::default(); let settlement = SettlementLayer::Starknet { block: 0, @@ -68,14 +72,20 @@ fn rollup_chain_spec() -> rollup::ChainSpec { rpc_url: Url::parse("http://localhost:5050").unwrap(), }; - rollup::ChainSpec { id, genesis, settlement, fee_contract } + rollup::ChainSpec { + id, + genesis, + settlement, + fee_contracts, + versioned_constants_overrides: None, + } } #[rstest] #[case::dev(ChainSpec::Dev(dev_chain_spec()))] #[case::rollup(ChainSpec::Rollup(rollup_chain_spec()))] fn can_initialize_genesis(#[case] chain: ChainSpec) { - let backend = backend(&chain); + let backend = backend(chain.into()); backend.init_genesis().expect("failed to initialize genesis"); } @@ -85,10 +95,10 @@ fn can_initialize_genesis(#[case] chain: ChainSpec) { fn can_reinitialize_genesis(#[case] chain: ChainSpec) { let db = DbProvider::new_in_memory(); - let backend = backend_with_db(&chain, db.clone()); + let backend = backend_with_db(chain.clone().into(), db.clone()); backend.init_genesis().expect("failed to initialize genesis"); - let backend = backend_with_db(&chain, db); + let backend = backend_with_db(chain.into(), db); backend.init_genesis().unwrap(); } @@ -97,7 +107,7 @@ fn reinitialize_with_different_rollup_chain_spec() { let db = DbProvider::new_in_memory(); let chain1 = ChainSpec::Rollup(rollup_chain_spec()); - let backend1 = backend_with_db(&chain1, db.clone()); + let backend1 = backend_with_db(chain1.into(), db.clone()); backend1.init_genesis().expect("failed to initialize genesis"); // Modify the chain spec so that the resultant genesis block hash will be different. @@ -107,7 +117,7 @@ fn reinitialize_with_different_rollup_chain_spec() { chain }); - let backend2 = backend_with_db(&chain2, db); + let backend2 = backend_with_db(chain2.into(), db); let err = backend2.init_genesis().unwrap_err().to_string(); assert!(err.as_str().contains("Genesis block hash mismatch")); } diff --git a/crates/executor/Cargo.toml b/crates/executor/Cargo.toml index 752fcfd2f..26f9a407e 100644 --- a/crates/executor/Cargo.toml +++ b/crates/executor/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true [dependencies] katana-primitives.workspace = true +katana-chain-spec.workspace = true katana-provider.workspace = true blockifier = { workspace = true, features = [ "testing" ] } diff --git a/crates/executor/benches/execution.rs b/crates/executor/benches/execution.rs index a6b26bcbd..1da188ca6 100644 --- a/crates/executor/benches/execution.rs +++ b/crates/executor/benches/execution.rs @@ -5,7 +5,7 @@ use criterion::measurement::WallTime; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkGroup, Criterion}; use katana_executor::implementation::blockifier::state::StateProviderDb; use katana_executor::ExecutionFlags; -use katana_primitives::env::{BlockEnv, CfgEnv}; +use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::transaction::ExecutableTxWithHash; use katana_provider::api::state::StateFactoryProvider; use katana_provider::test_utils; @@ -32,30 +32,30 @@ fn blockifier( group: &mut BenchmarkGroup<'_, WallTime>, provider: impl StateFactoryProvider, execution_flags: &ExecutionFlags, - block_envs: &(BlockEnv, CfgEnv), + block_envs: &(BlockEnv, VersionedConstantsOverrides), tx: ExecutableTxWithHash, ) { use katana_executor::implementation::blockifier::utils::{block_context_from_envs, transact}; - // convert to blockifier block context - let block_context = block_context_from_envs(&block_envs.0, &block_envs.1); + // // convert to blockifier block context + // let block_context = block_context_from_envs(&block_envs.0, &block_envs.1); - group.bench_function("Blockifier.Cold", |b| { - // we need to set up the cached state for each iteration as it's not cloneable - b.iter_batched( - || { - // setup state - let state = provider.latest().expect("failed to get latest state"); - let state = CachedState::new(StateProviderDb::new(state)); + // group.bench_function("Blockifier.Cold", |b| { + // // we need to set up the cached state for each iteration as it's not cloneable + // b.iter_batched( + // || { + // // setup state + // let state = provider.latest().expect("failed to get latest state"); + // let state = CachedState::new(StateProviderDb::new(state)); - (state, &block_context, execution_flags, tx.clone()) - }, - |(mut state, block_context, flags, tx)| { - transact(&mut state, block_context, flags, tx, None) - }, - BatchSize::SmallInput, - ) - }); + // (state, &block_context, execution_flags, tx.clone()) + // }, + // |(mut state, block_context, flags, tx)| { + // transact(&mut state, block_context, flags, tx, None) + // }, + // BatchSize::SmallInput, + // ) + // }); } criterion_group! { diff --git a/crates/executor/benches/utils.rs b/crates/executor/benches/utils.rs index 26085c13c..0636e14fa 100644 --- a/crates/executor/benches/utils.rs +++ b/crates/executor/benches/utils.rs @@ -1,6 +1,6 @@ use katana_genesis::constant::DEFAULT_ETH_FEE_TOKEN_ADDRESS; use katana_primitives::block::GasPrices; -use katana_primitives::env::{BlockEnv, CfgEnv, FeeTokenAddressses}; +use katana_primitives::env::{BlockEnv, FeeTokenAddressses, VersionedConstantsOverrides}; use katana_primitives::transaction::{ExecutableTxWithHash, InvokeTx, InvokeTxV1}; use katana_primitives::Felt; use starknet::macros::{felt, selector}; @@ -23,20 +23,20 @@ pub fn tx() -> ExecutableTxWithHash { ExecutableTxWithHash::new(invoke.into()) } -pub fn envs() -> (BlockEnv, CfgEnv) { +pub fn envs() -> (BlockEnv, VersionedConstantsOverrides) { let block = BlockEnv { l1_gas_prices: GasPrices::MIN, sequencer_address: felt!("0x1337").into(), ..Default::default() }; - let cfg = CfgEnv { + let cfg = VersionedConstantsOverrides { max_recursion_depth: 100, validate_max_n_steps: 4_000_000, invoke_tx_max_n_steps: 4_000_000, - fee_token_addresses: FeeTokenAddressses { - eth: DEFAULT_ETH_FEE_TOKEN_ADDRESS, - strk: DEFAULT_ETH_FEE_TOKEN_ADDRESS, - }, + // fee_token_addresses: FeeTokenAddressses { + // eth: DEFAULT_ETH_FEE_TOKEN_ADDRESS, + // strk: DEFAULT_ETH_FEE_TOKEN_ADDRESS, + // }, ..Default::default() }; diff --git a/crates/executor/src/abstraction/executor.rs b/crates/executor/src/abstraction/executor.rs index a21b5b1d2..c9a0df567 100644 --- a/crates/executor/src/abstraction/executor.rs +++ b/crates/executor/src/abstraction/executor.rs @@ -1,5 +1,5 @@ use katana_primitives::block::ExecutableBlock; -use katana_primitives::env::{BlockEnv, CfgEnv}; +use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::transaction::{ExecutableTxWithHash, TxWithHash}; use katana_provider::api::state::StateProvider; @@ -23,7 +23,7 @@ pub trait ExecutorFactory: Send + Sync + 'static + core::fmt::Debug { P: StateProvider + 'a; /// Returns the configuration environment of the factory. - fn cfg(&self) -> &CfgEnv; + fn cfg(&self) -> &VersionedConstantsOverrides; /// Returns the execution flags set by the factory. fn execution_flags(&self) -> &ExecutionFlags; diff --git a/crates/executor/src/implementation/blockifier/mod.rs b/crates/executor/src/implementation/blockifier/mod.rs index 7dfdbc687..347a32894 100644 --- a/crates/executor/src/implementation/blockifier/mod.rs +++ b/crates/executor/src/implementation/blockifier/mod.rs @@ -13,8 +13,9 @@ pub mod utils; use blockifier::context::BlockContext; use cache::ClassCache; +use katana_chain_spec::ChainSpec; use katana_primitives::block::{ExecutableBlock, GasPrices as KatanaGasPrices, PartialHeader}; -use katana_primitives::env::{BlockEnv, CfgEnv}; +use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::transaction::{ExecutableTx, ExecutableTxWithHash, TxWithHash}; use katana_primitives::version::StarknetVersion; use katana_provider::api::state::StateProvider; @@ -34,22 +35,23 @@ pub(crate) const LOG_TARGET: &str = "katana::executor::blockifier"; #[derive(Debug)] pub struct BlockifierFactory { - cfg: CfgEnv, + cfg: VersionedConstantsOverrides, flags: ExecutionFlags, limits: BlockLimits, - class_cache: ClassCache, + chain_spec: Arc, } impl BlockifierFactory { /// Create a new factory with the given configuration and simulation flags. pub fn new( - cfg: CfgEnv, + cfg: VersionedConstantsOverrides, flags: ExecutionFlags, limits: BlockLimits, class_cache: ClassCache, + chain_spec: Arc, ) -> Self { - Self { cfg, flags, limits, class_cache } + Self { cfg, flags, limits, class_cache, chain_spec } } } @@ -79,10 +81,11 @@ impl ExecutorFactory for BlockifierFactory { flags, limits, self.class_cache.clone(), + self.chain_spec.clone(), )) } - fn cfg(&self) -> &CfgEnv { + fn cfg(&self) -> &VersionedConstantsOverrides { &self.cfg } @@ -101,20 +104,22 @@ pub struct StarknetVMProcessor<'a> { stats: ExecutionStats, bouncer: Bouncer, starknet_version: StarknetVersion, - cfg_env: CfgEnv, + cfg_env: VersionedConstantsOverrides, } impl<'a> StarknetVMProcessor<'a> { pub fn new( state: impl StateProvider + 'a, block_env: BlockEnv, - cfg_env: CfgEnv, + cfg_env: VersionedConstantsOverrides, simulation_flags: ExecutionFlags, limits: BlockLimits, class_cache: ClassCache, + chain_spec: Arc, ) -> Self { let transactions = Vec::new(); - let block_context = Arc::new(utils::block_context_from_envs(&block_env, &cfg_env)); + let block_context = + Arc::new(utils::block_context_from_envs(chain_spec.as_ref(), &block_env, &cfg_env)); let state = state::CachedState::new(state, class_cache); diff --git a/crates/executor/src/implementation/blockifier/utils.rs b/crates/executor/src/implementation/blockifier/utils.rs index dde58960d..adc3bf3c3 100644 --- a/crates/executor/src/implementation/blockifier/utils.rs +++ b/crates/executor/src/implementation/blockifier/utils.rs @@ -18,8 +18,9 @@ use blockifier::transaction::objects::{HasRelatedFeeType, TransactionExecutionIn use blockifier::transaction::transaction_execution::Transaction; use blockifier::transaction::transactions::ExecutableTransaction; use cairo_vm::types::errors::program_errors::ProgramError; +use katana_chain_spec::ChainSpec; use katana_primitives::chain::NamedChainId; -use katana_primitives::env::{BlockEnv, CfgEnv}; +use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::fee::{FeeInfo, PriceUnit, ResourceBoundsMapping}; use katana_primitives::state::{StateUpdates, StateUpdatesWithClasses}; use katana_primitives::transaction::{ @@ -429,10 +430,14 @@ fn set_max_initial_sierra_gas(tx: &mut ExecutableTxWithHash) { } /// Create a block context from the chain environment values. -pub fn block_context_from_envs(block_env: &BlockEnv, cfg_env: &CfgEnv) -> BlockContext { +pub fn block_context_from_envs( + chain_spec: &ChainSpec, + block_env: &BlockEnv, + cfg_env: &VersionedConstantsOverrides, +) -> BlockContext { let fee_token_addresses = FeeTokenAddresses { - eth_fee_token_address: to_blk_address(cfg_env.fee_token_addresses.eth), - strk_fee_token_address: to_blk_address(cfg_env.fee_token_addresses.strk), + eth_fee_token_address: to_blk_address(chain_spec.fee_contracts().eth), + strk_fee_token_address: to_blk_address(chain_spec.fee_contracts().strk), }; let eth_l2_gas_price = NonzeroGasPrice::new(block_env.l2_gas_prices.eth.get().into()) @@ -472,7 +477,7 @@ pub fn block_context_from_envs(block_env: &BlockEnv, cfg_env: &CfgEnv) -> BlockC use_kzg_da: false, }; - let chain_info = ChainInfo { fee_token_addresses, chain_id: to_blk_chain_id(cfg_env.chain_id) }; + let chain_info = ChainInfo { fee_token_addresses, chain_id: to_blk_chain_id(chain_spec.id()) }; // IMPORTANT: // @@ -733,7 +738,7 @@ pub fn is_zero_resource_bounds(resource_bounds: &ResourceBoundsMapping) -> bool // These overrides would potentially make the `snos` run be invalid as it doesn't know about the // new overridden values. pub(crate) fn apply_versioned_constant_overrides( - cfg: &CfgEnv, + cfg: &VersionedConstantsOverrides, versioned_constants: &mut VersionedConstants, ) { versioned_constants.max_recursion_depth = cfg.max_recursion_depth; diff --git a/crates/executor/src/implementation/noop.rs b/crates/executor/src/implementation/noop.rs index 2fd1e4414..218781519 100644 --- a/crates/executor/src/implementation/noop.rs +++ b/crates/executor/src/implementation/noop.rs @@ -1,7 +1,7 @@ use katana_primitives::block::ExecutableBlock; use katana_primitives::class::{ClassHash, CompiledClassHash, ContractClass}; use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; -use katana_primitives::env::{BlockEnv, CfgEnv}; +use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::transaction::{ExecutableTxWithHash, TxWithHash}; use katana_provider::api::contract::ContractClassProvider; use katana_provider::api::state::{StateProofProvider, StateProvider, StateRootProvider}; @@ -16,7 +16,7 @@ use crate::ExecutorError; /// A no-op executor factory. Creates an executor that does nothing. #[derive(Debug, Default)] pub struct NoopExecutorFactory { - cfg: CfgEnv, + cfg: VersionedConstantsOverrides, execution_flags: ExecutionFlags, } @@ -49,7 +49,7 @@ impl ExecutorFactory for NoopExecutorFactory { Box::new(NoopExecutor { block_env }) } - fn cfg(&self) -> &CfgEnv { + fn cfg(&self) -> &VersionedConstantsOverrides { &self.cfg } diff --git a/crates/executor/tests/executor.rs b/crates/executor/tests/executor.rs index 424fbf86a..7a7c2eb42 100644 --- a/crates/executor/tests/executor.rs +++ b/crates/executor/tests/executor.rs @@ -76,29 +76,30 @@ fn test_executor_with_valid_blocks_impl( let nonce = state_provider.nonce(main_account).unwrap().expect("nonce should exist"); assert_eq!(nonce, 2u64.into(), "account nonce is updated"); - let updated_main_acc_balance = state_provider - .storage( - cfg_env.fee_token_addresses.eth, - // the storage slot of the lower half of the fee balance - get_storage_var_address("ERC20_balances", &[main_account.into()]).unwrap(), // felt!("0x6e78596cd9cb5c7ef89ba020ffb848c0926c43c652ac5f9e219d0c8267caefe"), - ) - .unwrap() - .expect("storage should exist"); - - let actual_new_acc_balance = state_provider - .storage( - cfg_env.fee_token_addresses.eth, - // the storage slot of the lower half of the fee balance - get_storage_var_address("ERC20_balances", &[new_acc.into()]).unwrap(), - ) - .unwrap() - .expect("storage should exist"); - - assert!( - updated_main_acc_balance < Felt::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE), - "sender balance should decrease" - ); - assert_eq!(actual_new_acc_balance, felt!("0x9999999999999999"), "account balance is updated"); + // let updated_main_acc_balance = state_provider + // .storage( + // cfg_env.fee_token_addresses.eth, + // // the storage slot of the lower half of the fee balance + // get_storage_var_address("ERC20_balances", &[main_account.into()]).unwrap(), // + // felt!("0x6e78596cd9cb5c7ef89ba020ffb848c0926c43c652ac5f9e219d0c8267caefe"), ) + // .unwrap() + // .expect("storage should exist"); + + // let actual_new_acc_balance = state_provider + // .storage( + // cfg_env.fee_token_addresses.eth, + // // the storage slot of the lower half of the fee balance + // get_storage_var_address("ERC20_balances", &[new_acc.into()]).unwrap(), + // ) + // .unwrap() + // .expect("storage should exist"); + + // assert!( + // updated_main_acc_balance < Felt::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE), + // "sender balance should decrease" + // ); + // assert_eq!(actual_new_acc_balance, felt!("0x9999999999999999"), "account balance is + // updated"); // assert that the sierra class is declared let expected_class_hash = felt!("0x420"); @@ -153,19 +154,19 @@ fn test_executor_with_valid_blocks_impl( ); assert_eq!(actual_new_acc_nonce, Some(1u64.into()), "account nonce is updated"); - let updated_new_acc_balance = state_provider - .storage( - cfg_env.fee_token_addresses.eth, - // the storage slot of the lower half of the fee balance - felt!("0x7c8bacc8c8a7db5e5d4e22ab58750239183ae3e08b17a07a486f85fe8aee391"), - ) - .unwrap() - .expect("storage should exist"); - - assert!( - updated_new_acc_balance < felt!("0x9999999999999999"), - "account balance should be updated" - ); + // let updated_new_acc_balance = state_provider + // .storage( + // cfg_env.fee_token_addresses.eth, + // // the storage slot of the lower half of the fee balance + // felt!("0x7c8bacc8c8a7db5e5d4e22ab58750239183ae3e08b17a07a486f85fe8aee391"), + // ) + // .unwrap() + // .expect("storage should exist"); + + // assert!( + // updated_new_acc_balance < felt!("0x9999999999999999"), + // "account balance should be updated" + // ); // block 3 // diff --git a/crates/executor/tests/fixtures/mod.rs b/crates/executor/tests/fixtures/mod.rs index b248ab943..8f72041a4 100644 --- a/crates/executor/tests/fixtures/mod.rs +++ b/crates/executor/tests/fixtures/mod.rs @@ -17,7 +17,7 @@ use katana_primitives::chain::ChainId; use katana_primitives::class::{CompiledClass, ContractClass}; use katana_primitives::contract::ContractAddress; use katana_primitives::da::L1DataAvailabilityMode; -use katana_primitives::env::{CfgEnv, FeeTokenAddressses}; +use katana_primitives::env::{FeeTokenAddressses, VersionedConstantsOverrides}; use katana_primitives::transaction::{ DeclareTx, DeclareTxV2, DeclareTxWithClass, DeployAccountTx, DeployAccountTxV1, ExecutableTx, ExecutableTxWithHash, InvokeTx, InvokeTxV1, @@ -237,18 +237,18 @@ pub fn valid_blocks() -> [ExecutableBlock; 3] { } #[rstest::fixture] -pub fn cfg() -> CfgEnv { +pub fn cfg() -> VersionedConstantsOverrides { let fee_token_addresses = FeeTokenAddressses { eth: DEFAULT_ETH_FEE_TOKEN_ADDRESS, strk: DEFAULT_STRK_FEE_TOKEN_ADDRESS, }; - CfgEnv { - fee_token_addresses, + VersionedConstantsOverrides { + // fee_token_addresses, max_recursion_depth: usize::MAX, validate_max_n_steps: u32::MAX, invoke_tx_max_n_steps: u32::MAX, - chain_id: ChainId::parse("KATANA").unwrap(), + // chain_id: ChainId::parse("KATANA").unwrap(), } } @@ -275,6 +275,10 @@ use katana_executor::implementation::blockifier::BlockifierFactory; use katana_executor::BlockLimits; #[rstest::fixture] -pub fn factory(cfg: CfgEnv, #[with(true)] flags: ExecutionFlags) -> BlockifierFactory { - BlockifierFactory::new(cfg, flags, BlockLimits::default(), ClassCache::new().unwrap()) +pub fn factory( + cfg: VersionedConstantsOverrides, + #[with(true)] flags: ExecutionFlags, +) -> BlockifierFactory { + // BlockifierFactory::new(cfg, flags, BlockLimits::default(), ClassCache::new().unwrap()) + todo!() } diff --git a/crates/executor/tests/fixtures/transaction.rs b/crates/executor/tests/fixtures/transaction.rs index 43b5ea1e6..71dc58b70 100644 --- a/crates/executor/tests/fixtures/transaction.rs +++ b/crates/executor/tests/fixtures/transaction.rs @@ -4,7 +4,7 @@ use katana_genesis::constant::DEFAULT_ETH_FEE_TOKEN_ADDRESS; use katana_primitives::chain::ChainId; use katana_primitives::contract::{ContractAddress, Nonce}; use katana_primitives::da::DataAvailabilityMode; -use katana_primitives::env::CfgEnv; +use katana_primitives::env::VersionedConstantsOverrides; use katana_primitives::fee::{AllResourceBoundsMapping, ResourceBounds, ResourceBoundsMapping}; use katana_primitives::transaction::ExecutableTxWithHash; use katana_primitives::utils::transaction::compute_invoke_v3_tx_hash; @@ -129,7 +129,11 @@ fn signed() -> bool { } #[rstest::fixture] -pub fn executable_tx(signed: bool, chain: &ChainSpec, cfg: CfgEnv) -> ExecutableTxWithHash { +pub fn executable_tx( + signed: bool, + chain: &ChainSpec, + cfg: VersionedConstantsOverrides, +) -> ExecutableTxWithHash { let (addr, alloc) = chain.genesis().allocations.first_key_value().expect("should have account"); let GenesisAllocation::Account(account) = alloc else { @@ -139,7 +143,7 @@ pub fn executable_tx(signed: bool, chain: &ChainSpec, cfg: CfgEnv) -> Executable invoke_executable_tx( *addr, account.private_key().unwrap(), - cfg.chain_id, + chain.id(), Felt::ZERO, // this is an arbitrary large fee so that it doesn't fail felt!("0x999999999999999"), @@ -151,7 +155,7 @@ pub fn executable_tx(signed: bool, chain: &ChainSpec, cfg: CfgEnv) -> Executable pub fn executable_tx_without_max_fee( signed: bool, chain: &ChainSpec, - cfg: CfgEnv, + cfg: VersionedConstantsOverrides, ) -> ExecutableTxWithHash { let (addr, alloc) = chain.genesis().allocations.first_key_value().expect("should have account"); @@ -162,7 +166,7 @@ pub fn executable_tx_without_max_fee( invoke_executable_tx( *addr, account.private_key().unwrap(), - cfg.chain_id, + chain.id(), Felt::ZERO, Felt::ZERO, signed, diff --git a/crates/gateway/gateway-server/src/handlers.rs b/crates/gateway/gateway-server/src/handlers.rs index 992f7257d..a0df88add 100644 --- a/crates/gateway/gateway-server/src/handlers.rs +++ b/crates/gateway/gateway-server/src/handlers.rs @@ -21,7 +21,7 @@ use starknet::core::types::ResourcePrice; /// Shared application state containing the backend #[derive(Clone)] pub struct AppState { - pub api: StarknetApi>, + pub api: StarknetApi>, } impl AppState { @@ -29,7 +29,7 @@ impl AppState { async fn get_block(&self, id: BlockIdOrTag) -> Result, ApiError> { self.api .on_io_blocking_task(move |this| { - let provider = this.backend().blockchain.provider(); + let provider = this.storage(); if let Some(num) = provider.convert_block_id(id)? { let block = provider.block(num.into())?.unwrap(); diff --git a/crates/gateway/gateway-server/src/lib.rs b/crates/gateway/gateway-server/src/lib.rs index 9ac67100a..99fa95983 100644 --- a/crates/gateway/gateway-server/src/lib.rs +++ b/crates/gateway/gateway-server/src/lib.rs @@ -75,18 +75,13 @@ pub struct GatewayServer { health_check: bool, metered: bool, - starknet_api: - StarknetApi>, + starknet_api: StarknetApi>, } impl GatewayServer { /// Create a new feeder gateway server. pub fn new( - starknet_api: StarknetApi< - BlockifierFactory, - katana_pool::TxPool, - BlockProducer, - >, + starknet_api: StarknetApi>, ) -> Self { Self { timeout: DEFAULT_GATEWAY_TIMEOUT, diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 76be55ef5..b4750e045 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -17,10 +17,12 @@ katana-messaging.workspace = true katana-metrics.workspace = true katana-pipeline.workspace = true katana-pool.workspace = true +katana-pool-api.workspace = true katana-primitives.workspace = true katana-provider.workspace = true -katana-rpc = { workspace = true } +katana-rpc.workspace = true katana-rpc-api.workspace = true +katana-rpc-types.workspace = true katana-rpc-client.workspace = true katana-stage.workspace = true katana-tasks.workspace = true diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index 786dcc14c..e5c37f370 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -4,6 +4,8 @@ use std::future::IntoFuture; use std::sync::Arc; use anyhow::Result; +use http::header::CONTENT_TYPE; +use http::Method; use katana_gateway_client::Client as SequencerGateway; use katana_metrics::exporters::prometheus::PrometheusRecorder; use katana_metrics::{Report, Server as MetricsServer}; @@ -11,8 +13,11 @@ use katana_pipeline::{Pipeline, PipelineHandle}; use katana_pool::ordering::FiFo; use katana_pool::pool::Pool; use katana_pool::validation::NoopValidator; +use katana_pool::TxPool; use katana_primitives::transaction::ExecutableTxWithHash; use katana_provider::providers::db::DbProvider; +use katana_rpc::cors::Cors; +use katana_rpc::{RpcServer, RpcServerHandle}; use katana_stage::blocks::BatchBlockDownloader; use katana_stage::{Blocks, Classes}; use katana_tasks::TaskManager; @@ -23,12 +28,12 @@ use crate::config::metrics::MetricsConfig; mod exit; pub mod tip_watcher; +mod pool; use exit::NodeStoppedFuture; use tip_watcher::ChainTipWatcher; - -type TxPool = - Pool, FiFo>; +use crate::config::rpc::RpcConfig; +use crate::full::pool::{FullNodePool, GatewayProxyValidator}; #[derive(Debug, Clone)] pub enum Network { @@ -39,6 +44,7 @@ pub enum Network { #[derive(Debug)] pub struct Config { pub db: DbConfig, + pub rpc: RpcConfig, pub metrics: Option, pub gateway_api_key: Option, pub eth_rpc_url: String, @@ -48,10 +54,12 @@ pub struct Config { #[derive(Debug)] pub struct Node { pub db: katana_db::Db, - pub pool: TxPool, + pub pool: FullNodePool, pub config: Arc, pub task_manager: TaskManager, pub pipeline: Pipeline, + pub rpc_server: RpcServer, + pub gateway_client: SequencerGateway, } impl Node { @@ -75,26 +83,43 @@ impl Node { let provider = DbProvider::new(db.clone()); - // --- build transaction pool - - let pool = TxPool::new(NoopValidator::new(), FiFo::new()); - - // --- build pipeline + // --- build gateway client - let fgw = if let Some(ref key) = config.gateway_api_key { + let gateway_client = if let Some(ref key) = config.gateway_api_key { SequencerGateway::sepolia().with_api_key(key.clone()) } else { SequencerGateway::sepolia() }; - let (mut pipeline, _) = Pipeline::new(provider.clone(), 64); - let block_downloader = BatchBlockDownloader::new_gateway(fgw.clone(), 3); - pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); - pipeline.add_stage(Classes::new(provider, fgw.clone(), 3)); + // --- build transaction pool - let node = Node { pool, config: Arc::new(config), task_manager, pipeline, db }; + let validator = GatewayProxyValidator::new(gateway_client.clone()); + let pool = FullNodePool::new(validator, FiFo::new()); - Ok(node) + // --- build pipeline + + let (mut pipeline, _) = Pipeline::new(provider.clone(), 64); + let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 3); + pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); + pipeline.add_stage(Classes::new(provider, gateway_client.clone(), 3)); + + let cors = Cors::new() + .allow_origins(config.rpc.cors_origins.clone()) + // Allow `POST` when accessing the resource + .allow_methods([Method::POST, Method::GET]) + .allow_headers([CONTENT_TYPE, "argent-client".parse().unwrap(), "argent-version".parse().unwrap()]); + + let rpc_server = RpcServer::new().metrics(true).health_check(true).cors(cors); + + Ok(Node { + db, + pool, + pipeline, + rpc_server, + task_manager, + gateway_client, + config: Arc::new(config), + }) } pub async fn launch(self) -> Result { @@ -151,12 +176,16 @@ impl Node { } }); + // --- start the rpc server + + let rpc = self.rpc_server.start(self.config.rpc.socket_addr()).await?; + Ok(LaunchedNode { db: self.db, - pipeline_handle, - pool: self.pool, config: self.config, task_manager: self.task_manager, + pipeline: pipeline_handle, + rpc, }) } } @@ -164,14 +193,15 @@ impl Node { #[derive(Debug)] pub struct LaunchedNode { pub db: katana_db::Db, - pub pool: TxPool, pub task_manager: TaskManager, pub config: Arc, - pub pipeline_handle: PipelineHandle, + pub rpc: RpcServerHandle, + pub pipeline: PipelineHandle, } impl LaunchedNode { pub async fn stop(&self) -> Result<()> { + self.rpc.stop()?; self.task_manager.shutdown().await; Ok(()) } diff --git a/crates/node/src/full/pool.rs b/crates/node/src/full/pool.rs new file mode 100644 index 000000000..2eed2e11a --- /dev/null +++ b/crates/node/src/full/pool.rs @@ -0,0 +1,61 @@ +use std::future::Future; + +use katana_pool::ordering::FiFo; +use katana_pool::pool::Pool; +use katana_pool::validation::stateful::TxValidator; +use katana_pool::PoolTransaction; +use katana_pool_api::validation::{ValidationOutcome, ValidationResult, Validator}; +use katana_primitives::chain::ChainId; +use katana_primitives::fee::ResourceBoundsMapping; +use katana_primitives::transaction::{ + DeclareTx, DeployAccountTx, ExecutableTxWithHash, InvokeTx, TxHash, +}; +use katana_primitives::{ContractAddress, Felt}; +use katana_rpc_types::{ + BroadcastedDeclareTx, BroadcastedDeployAccountTx, BroadcastedInvokeTx, BroadcastedTx, +}; + +pub type FullNodePool = + Pool>; + +/// This is an implementation of the [`Validator`] trait that proxies incoming transactions to a +/// Starknet sequencer via the gateway endpoint. +/// +/// Any transaction validation is performed by the Starknet sequencer. +#[derive(Debug)] +pub struct GatewayProxyValidator { + gateway_client: katana_gateway::client::Client, +} + +impl GatewayProxyValidator { + pub fn new(gateway_client: katana_gateway::client::Client) -> Self { + Self { gateway_client } + } +} + +impl Validator for GatewayProxyValidator { + type Transaction = ExecutableTxWithHash; + + fn validate( + &self, + tx: Self::Transaction, + ) -> impl Future> + Send { + let gateway_client = self.gateway_client.clone(); + + async move { + match BroadcastedTx::from(tx.transaction.clone()) { + BroadcastedTx::Invoke(invoke_tx) => { + gateway_client.add_invoke_transaction(invoke_tx).await.unwrap(); + } + BroadcastedTx::Declare(declare_tx) => { + gateway_client.add_declare_transaction(declare_tx).await.unwrap(); + } + BroadcastedTx::DeployAccount(deploy_account_tx) => { + gateway_client.add_deploy_account_transaction(deploy_account_tx).await.unwrap(); + } + } + + ValidationResult::Ok(ValidationOutcome::Valid(tx)) + } + } +} diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 684c45518..2f5271a74 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -31,7 +31,7 @@ use katana_metrics::sys::DiskReporter; use katana_metrics::{Report, Server as MetricsServer}; use katana_pool::ordering::FiFo; use katana_pool::TxPool; -use katana_primitives::env::{CfgEnv, FeeTokenAddressses}; +use katana_primitives::env::{FeeTokenAddressses, VersionedConstantsOverrides}; #[cfg(feature = "cartridge")] use katana_rpc::cartridge::CartridgeApi; use katana_rpc::cors::Cors; @@ -91,18 +91,16 @@ impl Node { // --- build executor factory - let fee_token_addresses = match config.chain.as_ref() { - ChainSpec::Dev(cs) => { - FeeTokenAddressses { eth: cs.fee_contracts.eth, strk: cs.fee_contracts.strk } - } - ChainSpec::Rollup(cs) => { - FeeTokenAddressses { eth: cs.fee_contract.strk, strk: cs.fee_contract.strk } - } - }; + // let fee_token_addresses = match config.chain.as_ref() { + // ChainSpec::Dev(cs) => { + // FeeTokenAddressses { eth: cs.fee_contracts.eth, strk: cs.fee_contracts.strk } + // } + // ChainSpec::Rollup(cs) => { + // FeeTokenAddressses { eth: cs.fee_contracts.eth, strk: cs.fee_contracts.strk } + // } + // }; - let cfg_env = CfgEnv { - fee_token_addresses, - chain_id: config.chain.id(), + let cfg_env = VersionedConstantsOverrides { invoke_tx_max_n_steps: config.execution.invocation_max_steps, validate_max_n_steps: config.execution.validation_max_steps, max_recursion_depth: config.execution.max_recursion_depth, @@ -129,6 +127,7 @@ impl Node { execution_flags, config.sequencing.block_limits(), global_class_cache, + config.chain.clone(), ); Arc::new(factory) @@ -256,13 +255,18 @@ impl Node { max_proof_keys: config.rpc.max_proof_keys, max_call_gas: config.rpc.max_call_gas, max_concurrent_estimate_fee_requests: config.rpc.max_concurrent_estimate_fee_requests, + simulation_flags: ExecutionFlags::default(), #[cfg(feature = "cartridge")] paymaster, }; + let storage_provider = backend.blockchain.provider().clone(); + let chain_spec = backend.chain_spec.clone(); + let starknet_api = if let Some(client) = forked_client { StarknetApi::new_forked( - backend.clone(), + chain_spec.clone(), + storage_provider.clone(), pool.clone(), client, task_spawner.clone(), @@ -271,7 +275,8 @@ impl Node { ) } else { StarknetApi::new( - backend.clone(), + chain_spec.clone(), + storage_provider.clone(), pool.clone(), task_spawner.clone(), starknet_api_cfg, diff --git a/crates/pool/pool/Cargo.toml b/crates/pool/pool/Cargo.toml index 6bbc7f439..b705bc2ae 100644 --- a/crates/pool/pool/Cargo.toml +++ b/crates/pool/pool/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true [dependencies] katana-executor.workspace = true +katana-chain-spec.workspace = true katana-pool-api.workspace = true katana-primitives.workspace = true katana-provider.workspace = true diff --git a/crates/pool/pool/src/pool.rs b/crates/pool/pool/src/pool.rs index 3f60f2c53..682856bf4 100644 --- a/crates/pool/pool/src/pool.rs +++ b/crates/pool/pool/src/pool.rs @@ -141,47 +141,47 @@ where async move { match pool.inner.validator.validate(tx).await { - Ok(outcome) => { - match outcome { - ValidationOutcome::Valid(tx) => { - // get the priority of the validated tx - let priority = pool.inner.ordering.priority(&tx); - let tx = PendingTx::new(id, tx, priority); - - // insert the tx in the pool - pool.inner.transactions.write().insert(tx.clone()); - trace!(target: "pool", "Transaction added to the pool"); - - pool.notify(tx); - - Ok(hash) - } - - // TODO: create a small cache for rejected transactions to respect the rpc spec - // `getTransactionStatus` - ValidationOutcome::Invalid { error, .. } => { - warn!(target: "pool", %error, "Invalid transaction."); - Err(PoolError::InvalidTransaction(Box::new(error))) - } - - // return as error for now but ideally we should kept the tx in a separate - // queue and revalidate it when the parent tx is added to the pool - ValidationOutcome::Dependent { tx, tx_nonce, current_nonce } => { - trace!(target: "pool", %tx_nonce, %current_nonce, "Dependent transaction."); - let err = InvalidTransactionError::InvalidNonce { - address: tx.sender(), - current_nonce, - tx_nonce, - }; - Err(PoolError::InvalidTransaction(Box::new(err))) - } - } - } - - Err(error) => { - error!(target: "pool", %error, "Failed to validate transaction."); - Err(PoolError::Internal(error.error)) - } + Ok(outcome) => { + match outcome { + ValidationOutcome::Valid(tx) => { + // get the priority of the validated tx + let priority = pool.inner.ordering.priority(&tx); + let tx = PendingTx::new(id, tx, priority); + + // insert the tx in the pool + pool.inner.transactions.write().insert(tx.clone()); + trace!(target: "pool", "Transaction added to the pool"); + + pool.notify(tx); + + Ok(hash) + } + + // TODO: create a small cache for rejected transactions to respect the rpc spec + // `getTransactionStatus` + ValidationOutcome::Invalid { error, .. } => { + warn!(target: "pool", %error, "Invalid transaction."); + Err(PoolError::InvalidTransaction(Box::new(error))) + } + + // return as error for now but ideally we should kept the tx in a separate + // queue and revalidate it when the parent tx is added to the pool + ValidationOutcome::Dependent { tx, tx_nonce, current_nonce } => { + trace!(target: "pool", %tx_nonce, %current_nonce, "Dependent transaction."); + let err = InvalidTransactionError::InvalidNonce { + address: tx.sender(), + current_nonce, + tx_nonce, + }; + Err(PoolError::InvalidTransaction(Box::new(err))) + } + } + } + + Err(error) => { + error!(target: "pool", %error, "Failed to validate transaction."); + Err(PoolError::Internal(error.error)) + } } } .instrument(tracing::trace_span!(target: "pool", "pool_add", tx_hash = format!("{hash:#x}"))) diff --git a/crates/pool/pool/src/validation/stateful.rs b/crates/pool/pool/src/validation/stateful.rs index 0f80acafc..e456549f0 100644 --- a/crates/pool/pool/src/validation/stateful.rs +++ b/crates/pool/pool/src/validation/stateful.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::fmt::Debug; use std::sync::Arc; +use katana_chain_spec::ChainSpec; use katana_executor::implementation::blockifier::blockifier::blockifier::stateful_validator::{ StatefulValidator, StatefulValidatorError, }; @@ -23,7 +24,7 @@ use katana_pool_api::validation::{ }; use katana_pool_api::PoolTransaction; use katana_primitives::contract::{ContractAddress, Nonce}; -use katana_primitives::env::{BlockEnv, CfgEnv}; +use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::transaction::{ExecutableTx, ExecutableTxWithHash}; use katana_primitives::Felt; use katana_provider::api::state::StateProvider; @@ -39,24 +40,27 @@ pub struct TxValidator { struct Inner { // execution context - cfg_env: CfgEnv, + cfg_env: VersionedConstantsOverrides, block_env: BlockEnv, execution_flags: ExecutionFlags, state: Arc>, pool_nonces: HashMap, + chain_spec: Arc, } impl TxValidator { pub fn new( state: Box, execution_flags: ExecutionFlags, - cfg_env: CfgEnv, + cfg_env: VersionedConstantsOverrides, block_env: BlockEnv, permit: Arc>, + chain_spec: Arc, ) -> Self { let inner = Arc::new(Mutex::new(Inner { cfg_env, block_env, + chain_spec, execution_flags, state: Arc::new(state), pool_nonces: HashMap::new(), @@ -94,7 +98,7 @@ impl Inner { let state_provider = StateProviderDb::new_with_class_cache(state, class_cache); let cached_state = CachedState::new(state_provider); - let context = block_context_from_envs(&self.block_env, &self.cfg_env); + let context = block_context_from_envs(&self.chain_spec, &self.block_env, &self.cfg_env); StatefulValidator::create(cached_state, context) } diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index dd315353b..e3f8d9a47 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -1,5 +1,4 @@ use crate::block::{BlockNumber, GasPrices}; -use crate::chain::ChainId; use crate::contract::ContractAddress; use crate::version::StarknetVersion; @@ -23,12 +22,12 @@ pub struct BlockEnv { } /// The chain configuration values. -#[derive(Debug, Clone, Default)] -pub struct CfgEnv { - /// The chain id. - pub chain_id: ChainId, - /// The contract addresses of the fee tokens. - pub fee_token_addresses: FeeTokenAddressses, +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct VersionedConstantsOverrides { + // /// The chain id. + // pub chain_id: ChainId, + // /// The contract addresses of the fee tokens. + // pub fee_token_addresses: FeeTokenAddressses, /// The maximum number of steps allowed for an invoke transaction. pub invoke_tx_max_n_steps: u32, /// The maximum number of steps allowed for transaction validation. diff --git a/crates/rpc/rpc-types/src/broadcasted.rs b/crates/rpc/rpc-types/src/broadcasted.rs index d18cf1e72..a82b5bf46 100644 --- a/crates/rpc/rpc-types/src/broadcasted.rs +++ b/crates/rpc/rpc-types/src/broadcasted.rs @@ -6,10 +6,13 @@ use katana_primitives::class::{ }; use katana_primitives::contract::Nonce; use katana_primitives::da::DataAvailabilityMode; -use katana_primitives::fee::{ResourceBoundsMapping, Tip}; +use katana_primitives::fee::{ + L1GasResourceBoundsMapping, ResourceBounds, ResourceBoundsMapping, Tip, +}; use katana_primitives::transaction::{ - DeclareTx, DeclareTxV3, DeclareTxWithClass, DeployAccountTx, DeployAccountTxV3, ExecutableTx, - ExecutableTxWithHash, InvokeTx, InvokeTxV3, TxHash, TxType, + DeclareTx, DeclareTxV0, DeclareTxV1, DeclareTxV2, DeclareTxV3, DeclareTxWithClass, + DeployAccountTx, DeployAccountTxV1, DeployAccountTxV3, ExecutableTx, ExecutableTx, + ExecutableTxWithHash, InvokeTx, InvokeTxV0, InvokeTxV1, InvokeTxV3, TxHash, TxType, }; use katana_primitives::utils::get_contract_address; use katana_primitives::{ContractAddress, Felt}; @@ -322,6 +325,318 @@ impl From for UntypedBroadcastedTx { } } +// From implementations for primitive types to broadcasted types + +impl From for BroadcastedInvokeTx { + fn from(tx: InvokeTx) -> Self { + match tx { + InvokeTx::V0(tx) => tx.into(), + InvokeTx::V1(tx) => tx.into(), + InvokeTx::V3(tx) => tx.into(), + } + } +} + +impl From for BroadcastedInvokeTx { + fn from(tx: InvokeTxV0) -> Self { + BroadcastedInvokeTx { + sender_address: tx.contract_address, + calldata: tx.calldata, + signature: tx.signature, + nonce: Felt::ZERO, + paymaster_data: vec![], + tip: 0.into(), + account_deployment_data: vec![], + resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, + l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, + }), + fee_data_availability_mode: DataAvailabilityMode::L1, + nonce_data_availability_mode: DataAvailabilityMode::L1, + is_query: false, + } + } +} + +impl From for BroadcastedInvokeTx { + fn from(tx: InvokeTxV1) -> Self { + BroadcastedInvokeTx { + sender_address: tx.sender_address, + calldata: tx.calldata, + signature: tx.signature, + nonce: tx.nonce, + paymaster_data: vec![], + tip: 0.into(), + account_deployment_data: vec![], + resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, + l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, + }), + fee_data_availability_mode: DataAvailabilityMode::L1, + nonce_data_availability_mode: DataAvailabilityMode::L1, + is_query: false, + } + } +} + +impl From for BroadcastedInvokeTx { + fn from(tx: InvokeTxV3) -> Self { + BroadcastedInvokeTx { + sender_address: tx.sender_address, + calldata: tx.calldata, + signature: tx.signature, + nonce: tx.nonce, + paymaster_data: tx.paymaster_data, + tip: tx.tip.into(), + account_deployment_data: tx.account_deployment_data, + resource_bounds: tx.resource_bounds, + fee_data_availability_mode: tx.fee_data_availability_mode, + nonce_data_availability_mode: tx.nonce_data_availability_mode, + is_query: false, + } + } +} + +impl From for BroadcastedDeclareTx { + fn from(tx: DeclareTx) -> Self { + match tx { + DeclareTx::V0(tx) => tx.into(), + DeclareTx::V1(tx) => tx.into(), + DeclareTx::V2(tx) => tx.into(), + DeclareTx::V3(tx) => tx.into(), + } + } +} + +impl From for BroadcastedDeclareTx { + fn from(tx: DeclareTxV0) -> Self { + // Note: V0 declare transactions use legacy classes, but BroadcastedDeclareTx expects + // Sierra. The actual contract class needs to be provided separately. + BroadcastedDeclareTx { + sender_address: tx.sender_address, + compiled_class_hash: CompiledClassHash::ZERO, + signature: tx.signature, + nonce: Felt::ZERO, + contract_class: Arc::new(SierraClass::default()), + paymaster_data: vec![], + tip: 0.into(), + account_deployment_data: vec![], + resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, + l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, + }), + fee_data_availability_mode: DataAvailabilityMode::L1, + nonce_data_availability_mode: DataAvailabilityMode::L1, + is_query: false, + } + } +} + +impl From for BroadcastedDeclareTx { + fn from(tx: DeclareTxV1) -> Self { + // Note: V1 declare transactions use legacy classes, but BroadcastedDeclareTx expects + // Sierra. The actual contract class needs to be provided separately. + BroadcastedDeclareTx { + sender_address: tx.sender_address, + compiled_class_hash: CompiledClassHash::ZERO, + signature: tx.signature, + nonce: tx.nonce, + contract_class: Arc::new(SierraClass::default()), + paymaster_data: vec![], + tip: 0.into(), + account_deployment_data: vec![], + resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, + l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, + }), + fee_data_availability_mode: DataAvailabilityMode::L1, + nonce_data_availability_mode: DataAvailabilityMode::L1, + is_query: false, + } + } +} + +impl From for BroadcastedDeclareTx { + fn from(tx: DeclareTxV2) -> Self { + BroadcastedDeclareTx { + sender_address: tx.sender_address, + compiled_class_hash: tx.compiled_class_hash, + signature: tx.signature, + nonce: tx.nonce, + contract_class: Arc::new(SierraClass::default()), + paymaster_data: vec![], + tip: 0.into(), + account_deployment_data: vec![], + resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, + l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, + }), + fee_data_availability_mode: DataAvailabilityMode::L1, + nonce_data_availability_mode: DataAvailabilityMode::L1, + is_query: false, + } + } +} + +impl From for BroadcastedDeclareTx { + fn from(tx: DeclareTxV3) -> Self { + BroadcastedDeclareTx { + sender_address: tx.sender_address, + compiled_class_hash: tx.compiled_class_hash, + signature: tx.signature, + nonce: tx.nonce, + contract_class: Arc::new(SierraClass::default()), + paymaster_data: tx.paymaster_data, + tip: tx.tip.into(), + account_deployment_data: tx.account_deployment_data, + resource_bounds: tx.resource_bounds, + fee_data_availability_mode: tx.fee_data_availability_mode, + nonce_data_availability_mode: tx.nonce_data_availability_mode, + is_query: false, + } + } +} + +impl From for BroadcastedDeclareTx { + fn from(tx: DeclareTxWithClass) -> Self { + // Convert ContractClass to SierraClass + // Note: This conversion may lose information for legacy classes + let contract_class = match tx.class.as_ref() { + ContractClass::Class(sierra) => { + Arc::new(SierraClass::try_from(sierra.clone()).unwrap_or_default()) + } + ContractClass::Legacy(_) => Arc::new(SierraClass::default()), + }; + + match tx.transaction { + DeclareTx::V0(tx) => BroadcastedDeclareTx { + sender_address: tx.sender_address, + compiled_class_hash: CompiledClassHash::ZERO, + signature: tx.signature, + nonce: Felt::ZERO, + contract_class, + paymaster_data: vec![], + tip: 0.into(), + account_deployment_data: vec![], + resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, + l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, + }), + fee_data_availability_mode: DataAvailabilityMode::L1, + nonce_data_availability_mode: DataAvailabilityMode::L1, + is_query: false, + }, + DeclareTx::V1(tx) => BroadcastedDeclareTx { + sender_address: tx.sender_address, + compiled_class_hash: CompiledClassHash::ZERO, + signature: tx.signature, + nonce: tx.nonce, + contract_class, + paymaster_data: vec![], + tip: 0.into(), + account_deployment_data: vec![], + resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, + l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, + }), + fee_data_availability_mode: DataAvailabilityMode::L1, + nonce_data_availability_mode: DataAvailabilityMode::L1, + is_query: false, + }, + DeclareTx::V2(tx) => BroadcastedDeclareTx { + sender_address: tx.sender_address, + compiled_class_hash: tx.compiled_class_hash, + signature: tx.signature, + nonce: tx.nonce, + contract_class, + paymaster_data: vec![], + tip: 0.into(), + account_deployment_data: vec![], + resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, + l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, + }), + fee_data_availability_mode: DataAvailabilityMode::L1, + nonce_data_availability_mode: DataAvailabilityMode::L1, + is_query: false, + }, + DeclareTx::V3(tx) => BroadcastedDeclareTx { + sender_address: tx.sender_address, + compiled_class_hash: tx.compiled_class_hash, + signature: tx.signature, + nonce: tx.nonce, + contract_class, + paymaster_data: tx.paymaster_data, + tip: tx.tip.into(), + account_deployment_data: tx.account_deployment_data, + resource_bounds: tx.resource_bounds, + fee_data_availability_mode: tx.fee_data_availability_mode, + nonce_data_availability_mode: tx.nonce_data_availability_mode, + is_query: false, + }, + } + } +} + +impl From for BroadcastedDeployAccountTx { + fn from(tx: DeployAccountTx) -> Self { + match tx { + DeployAccountTx::V1(tx) => tx.into(), + DeployAccountTx::V3(tx) => tx.into(), + } + } +} + +impl From for BroadcastedDeployAccountTx { + fn from(tx: DeployAccountTxV1) -> Self { + BroadcastedDeployAccountTx { + signature: tx.signature, + nonce: tx.nonce, + contract_address_salt: tx.contract_address_salt, + constructor_calldata: tx.constructor_calldata, + class_hash: tx.class_hash, + paymaster_data: vec![], + tip: 0.into(), + resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, + l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, + }), + fee_data_availability_mode: DataAvailabilityMode::L1, + nonce_data_availability_mode: DataAvailabilityMode::L1, + is_query: false, + } + } +} + +impl From for BroadcastedDeployAccountTx { + fn from(tx: DeployAccountTxV3) -> Self { + BroadcastedDeployAccountTx { + signature: tx.signature, + nonce: tx.nonce, + contract_address_salt: tx.contract_address_salt, + constructor_calldata: tx.constructor_calldata, + class_hash: tx.class_hash, + paymaster_data: tx.paymaster_data, + tip: tx.tip.into(), + resource_bounds: tx.resource_bounds, + fee_data_availability_mode: tx.fee_data_availability_mode, + nonce_data_availability_mode: tx.nonce_data_availability_mode, + is_query: false, + } + } +} + +impl From for BroadcastedTx { + fn from(tx: ExecutableTx) -> Self { + match tx { + ExecutableTx::Invoke(tx) => BroadcastedTx::Invoke(tx.into()), + ExecutableTx::Declare(tx) => BroadcastedTx::Declare(tx.into()), + ExecutableTx::DeployAccount(tx) => BroadcastedTx::DeployAccount(tx.into()), + ExecutableTx::L1Handler(_) => unimplemented!(), + } + } +} + /// A broadcasted transaction. #[derive(Debug, Clone, Serialize)] pub struct BroadcastedTxWithChainId { diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 8e96bc8c8..011a0aec8 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true [dependencies] katana-core.workspace = true +katana-chain-spec.workspace = true katana-executor.workspace = true katana-explorer = { workspace = true, features = [ "jsonrpsee" ], optional = true } katana-metrics.workspace = true diff --git a/crates/rpc/rpc/src/starknet/blockifier.rs b/crates/rpc/rpc/src/starknet/blockifier.rs index 454b0f23c..d9f5357c5 100644 --- a/crates/rpc/rpc/src/starknet/blockifier.rs +++ b/crates/rpc/rpc/src/starknet/blockifier.rs @@ -1,13 +1,14 @@ use std::sync::Arc; +use katana_chain_spec::ChainSpec; use katana_executor::implementation::blockifier::cache::ClassCache; use katana_executor::implementation::blockifier::call::execute_call; use katana_executor::implementation::blockifier::state::CachedState; use katana_executor::implementation::blockifier::utils::{self, block_context_from_envs}; use katana_executor::{ExecutionError, ExecutionFlags, ExecutionResult, ResultAndStates}; -use katana_primitives::env::{BlockEnv, CfgEnv}; +use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::transaction::ExecutableTxWithHash; -use katana_primitives::Felt; +use katana_primitives::{chain, Felt}; use katana_provider::api::state::StateProvider; use katana_rpc_api::error::starknet::{ContractErrorData, StarknetApiError}; use katana_rpc_types::{FeeEstimate, FunctionCall}; @@ -16,13 +17,14 @@ use crate::starknet::StarknetApiResult; #[tracing::instrument(level = "trace", target = "rpc", skip_all, fields(total_txs = transactions.len()))] pub fn simulate( + chain_spec: &ChainSpec, state: impl StateProvider, block_env: BlockEnv, - cfg_env: CfgEnv, + cfg_env: &VersionedConstantsOverrides, transactions: Vec, flags: ExecutionFlags, ) -> Vec { - let block_context = Arc::new(block_context_from_envs(&block_env, &cfg_env)); + let block_context = Arc::new(block_context_from_envs(chain_spec, &block_env, &cfg_env)); let state = CachedState::new(state, ClassCache::global().clone()); let mut results = Vec::with_capacity(transactions.len()); @@ -54,14 +56,15 @@ pub fn simulate( /// [specification]: https://github.com/starkware-libs/starknet-specs/blob/c2e93098b9c2ca0423b7f4d15b201f52f22d8c36/api/starknet_api_openrpc.json#L623 #[tracing::instrument(level = "trace", target = "rpc", skip_all, fields(total_txs = transactions.len()))] pub fn estimate_fees( + chain_spec: &ChainSpec, state: impl StateProvider, block_env: BlockEnv, - cfg_env: CfgEnv, + cfg_env: &VersionedConstantsOverrides, transactions: Vec, flags: ExecutionFlags, ) -> StarknetApiResult> { let flags = flags.with_fee(false); - let block_context = block_context_from_envs(&block_env, &cfg_env); + let block_context = block_context_from_envs(chain_spec, &block_env, cfg_env); let state = CachedState::new(state, ClassCache::global().clone()); state.with_mut_cached_state(|state| { @@ -110,13 +113,14 @@ pub fn estimate_fees( #[tracing::instrument(level = "trace", target = "rpc", skip_all)] pub fn call( + chain_spec: &ChainSpec, state: P, block_env: BlockEnv, - cfg_env: CfgEnv, + cfg_env: &VersionedConstantsOverrides, call: FunctionCall, max_call_gas: u64, ) -> Result, StarknetApiError> { - let block_context = Arc::new(block_context_from_envs(&block_env, &cfg_env)); + let block_context = Arc::new(block_context_from_envs(chain_spec, &block_env, &cfg_env)); // `ClassCache::try_global` could only fail if the global cache has not been initialized. // This won't happen in a normal execution flow as we guarantee that the global cache is @@ -147,22 +151,24 @@ fn to_api_error(error: ExecutionError) -> StarknetApiError { mod tests { use std::usize; - use katana_primitives::env::{BlockEnv, CfgEnv}; + use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::{address, ContractAddress}; use katana_provider::api::state::StateFactoryProvider; - use katana_provider::test_utils::test_provider; + use katana_provider::test_utils::{get_chain_for_testing, test_provider}; use katana_rpc_api::error::starknet::StarknetApiError; use katana_rpc_types::FunctionCall; use starknet::macros::selector; #[test] fn call_on_contract_not_deployed() { + let chain_spec = get_chain_for_testing(); let provider = test_provider(); let state = provider.latest().unwrap(); let max_call_gas = 1_000_000_000; let block_env = BlockEnv::default(); - let cfg_env = CfgEnv { max_recursion_depth: usize::MAX, ..Default::default() }; + let cfg_env = + VersionedConstantsOverrides { max_recursion_depth: usize::MAX, ..Default::default() }; let call = FunctionCall { calldata: Vec::new(), @@ -170,18 +176,20 @@ mod tests { entry_point_selector: selector!("foo"), }; - let result = super::call(state, block_env, cfg_env, call, max_call_gas); + let result = super::call(&chain_spec, state, block_env, cfg_env, call, max_call_gas); assert!(matches!(result, Err(StarknetApiError::ContractNotFound))); } #[test] fn call_on_entry_point_not_found() { + let chain_spec = get_chain_for_testing(); let provider = test_provider(); let state = provider.latest().unwrap(); let max_call_gas = 1_000_000_000; let block_env = BlockEnv::default(); - let cfg_env = CfgEnv { max_recursion_depth: usize::MAX, ..Default::default() }; + let cfg_env = + VersionedConstantsOverrides { max_recursion_depth: usize::MAX, ..Default::default() }; let call = FunctionCall { calldata: Vec::new(), @@ -189,7 +197,7 @@ mod tests { entry_point_selector: selector!("foobar"), }; - let result = super::call(state, block_env, cfg_env, call, max_call_gas); + let result = super::call(&chain_spec, state, block_env, cfg_env, call, max_call_gas); assert!(matches!(result, Err(StarknetApiError::EntrypointNotFound))); } } diff --git a/crates/rpc/rpc/src/starknet/config.rs b/crates/rpc/rpc/src/starknet/config.rs index f7e789e8e..748473c66 100644 --- a/crates/rpc/rpc/src/starknet/config.rs +++ b/crates/rpc/rpc/src/starknet/config.rs @@ -1,3 +1,5 @@ +use katana_executor::ExecutionFlags; + #[derive(Debug, Clone)] pub struct StarknetApiConfig { /// The max chunk size that can be served from the `getEvents` method. @@ -29,6 +31,10 @@ pub struct StarknetApiConfig { /// If `None`, defaults to [`DEFAULT_ESTIMATE_FEE_MAX_CONCURRENT_REQUESTS`]. pub max_concurrent_estimate_fee_requests: Option, + /// Simulation flags used for fee simulation and estimation. (ie starknet_estimateFee and + /// starknet_simulateTransactions) + pub simulation_flags: ExecutionFlags, + #[cfg(feature = "cartridge")] pub paymaster: Option, } diff --git a/crates/rpc/rpc/src/starknet/list.rs b/crates/rpc/rpc/src/starknet/list.rs index 138bf64d3..dc8a900a8 100644 --- a/crates/rpc/rpc/src/starknet/list.rs +++ b/crates/rpc/rpc/src/starknet/list.rs @@ -13,8 +13,10 @@ use super::StarknetApi; use crate::starknet::pending::PendingBlockProvider; #[async_trait] -impl - StarknetApiExtServer for StarknetApi +impl StarknetApiExtServer for StarknetApi +where + Pool: TransactionPool + 'static, + PP: PendingBlockProvider { async fn get_blocks(&self, request: GetBlocksRequest) -> RpcResult { Ok(self.blocks(request).await?) diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index a18776d0b..0fcab986c 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -6,6 +6,8 @@ use std::sync::Arc; use katana_core::backend::Backend; use katana_executor::ExecutorFactory; +use katana_chain_spec::ChainSpec; +use katana_core::backend::storage::Database; use katana_pool::TransactionPool; use katana_primitives::block::{BlockHashOrNumber, BlockIdOrTag, FinalityStatus, GasPrices}; use katana_primitives::class::{ClassHash, CompiledClass}; @@ -22,6 +24,7 @@ use katana_provider::api::transaction::{ ReceiptProvider, TransactionProvider, TransactionStatusProvider, TransactionsProviderExt, }; use katana_provider::api::ProviderError; +use katana_provider::BlockchainProvider; use katana_rpc_api::error::starknet::{ CompilationErrorData, PageSizeTooBigData, ProofLimitExceededData, StarknetApiError, }; @@ -74,24 +77,23 @@ type StarknetApiResult = Result; /// [write](katana_rpc_api::starknet::StarknetWriteApi), and /// [trace](katana_rpc_api::starknet::StarknetTraceApi) APIs. #[derive(Debug)] -pub struct StarknetApi +pub struct StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool, PP: PendingBlockProvider, { - inner: Arc>, + inner: Arc>, } #[derive(Debug)] -struct StarknetApiInner +struct StarknetApiInner where - EF: ExecutorFactory, Pool: TransactionPool, PP: PendingBlockProvider, { pool: Pool, - backend: Arc>, + chain_spec: Arc, + storage: BlockchainProvider>, forked_client: Option, task_spawner: TaskSpawner, estimate_fee_permit: Permits, @@ -99,9 +101,8 @@ where pending_block_provider: PP, } -impl StarknetApi +impl StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool, PP: PendingBlockProvider, { @@ -109,8 +110,8 @@ where &self.inner.pool } - pub fn backend(&self) -> &Arc> { - &self.inner.backend + pub fn storage(&self) -> &BlockchainProvider> { + &self.inner.storage } pub fn forked_client(&self) -> Option<&ForkedClient> { @@ -126,42 +127,37 @@ where } } -impl StarknetApi +impl StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool + 'static, PP: PendingBlockProvider, { pub fn new( - backend: Arc>, + chain_spec: Arc, + storage: BlockchainProvider>, pool: Pool, task_spawner: TaskSpawner, config: StarknetApiConfig, pending_block_provider: PP, ) -> Self { - Self::new_inner(backend, pool, None, task_spawner, config, pending_block_provider) + Self::new_inner(chain_spec, storage, pool, None, task_spawner, config, pending_block_provider) } pub fn new_forked( - backend: Arc>, + chain_spec: Arc, + storage: BlockchainProvider>, pool: Pool, forked_client: ForkedClient, task_spawner: TaskSpawner, config: StarknetApiConfig, pending_block_provider: PP, ) -> Self { - Self::new_inner( - backend, - pool, - Some(forked_client), - task_spawner, - config, - pending_block_provider, - ) + Self::new_inner(chain_spec, storage, pool, Some(forked_client), task_spawner, config, pending_block_provider) } fn new_inner( - backend: Arc>, + chain_spec: Arc, + storage: BlockchainProvider>, pool: Pool, forked_client: Option, task_spawner: TaskSpawner, @@ -174,8 +170,9 @@ where let estimate_fee_permit = Permits::new(total_permits); let inner = StarknetApiInner { + chain_spec, + storage, pool, - backend, task_spawner, forked_client, estimate_fee_permit, @@ -249,14 +246,21 @@ where // get the state and block env at the specified block for execution let state = self.state(&block_id)?; let env = self.block_env_at(&block_id)?; - let cfg_env = self.inner.backend.executor_factory.cfg().clone(); + let cfg_env = self.inner.chain_spec.versioned_constants_overrides().unwrap(); // do estimations - blockifier::estimate_fees(state, env, cfg_env, transactions, flags) + blockifier::estimate_fees( + self.inner.chain_spec.as_ref(), + state, + env, + cfg_env, + transactions, + flags, + ) } pub fn state(&self, block_id: &BlockIdOrTag) -> StarknetApiResult> { - let provider = self.inner.backend.blockchain.provider(); + let provider = &self.inner.storage; let state = match block_id { BlockIdOrTag::PreConfirmed => { @@ -279,7 +283,7 @@ where } fn block_env_at(&self, block_id: &BlockIdOrTag) -> StarknetApiResult { - let provider = self.inner.backend.blockchain.provider(); + let provider = &self.inner.storage; let env = match block_id { BlockIdOrTag::PreConfirmed => { @@ -325,7 +329,7 @@ where } fn block_hash_and_number(&self) -> StarknetApiResult { - let provider = self.inner.backend.blockchain.provider(); + let provider = &self.inner.storage; let hash = provider.latest_hash()?; let number = provider.latest_number()?; Ok(BlockHashAndNumberResponse::new(hash, number)) @@ -422,7 +426,7 @@ where pub async fn block_tx_count(&self, block_id: BlockIdOrTag) -> StarknetApiResult { let count = self .on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; let block_id: BlockHashOrNumber = match block_id { BlockIdOrTag::L1Accepted => return Ok(None), @@ -458,7 +462,7 @@ where async fn latest_block_number(&self) -> StarknetApiResult { self.on_io_blocking_task(move |this| { - let block_number = this.inner.backend.blockchain.provider().latest_number()?; + let block_number = this.inner.storage.latest_number()?; Ok(BlockNumberResponse { block_number }) }) .await? @@ -498,7 +502,7 @@ where let tx = if BlockIdOrTag::PreConfirmed == block_id { this.inner.pending_block_provider.get_pending_transaction_by_index(index)? } else { - let provider = &this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; let block_num = provider .convert_block_id(block_id)? @@ -579,7 +583,7 @@ where async fn transaction_status(&self, hash: TxHash) -> StarknetApiResult { let status = self .on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; let status = provider.transaction_status(hash)?; if let Some(status) = status { @@ -634,7 +638,7 @@ where ) -> StarknetApiResult { let block = self .on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; if BlockIdOrTag::PreConfirmed == block_id { if let Some(block) = @@ -671,7 +675,7 @@ where ) -> StarknetApiResult { let block = self .on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; if BlockIdOrTag::PreConfirmed == block_id { if let Some(block) = @@ -708,7 +712,7 @@ where ) -> StarknetApiResult { let block = self .on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; if BlockIdOrTag::PreConfirmed == block_id { if let Some(block) = @@ -742,7 +746,7 @@ where pub async fn state_update(&self, block_id: BlockIdOrTag) -> StarknetApiResult { let state_update = self .on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; let block_id = match block_id { BlockIdOrTag::Number(num) => BlockHashOrNumber::Num(num), @@ -839,7 +843,7 @@ where continuation_token: Option, chunk_size: u64, ) -> StarknetApiResult { - let provider = self.inner.backend.blockchain.provider(); + let provider = &self.inner.storage; let from = self.resolve_event_block_id_if_forked(from_block)?; let to = self.resolve_event_block_id_if_forked(to_block)?; @@ -1038,9 +1042,8 @@ where let latest = provider.latest_number()?; let new_cursor = Cursor::new_block(latest); - let continuation_token = Some(new_cursor.into_rpc_cursor().to_string()); - Ok(GetEventsResponse { events, continuation_token }) - } + let continuation_token = Some(new_cursor.into_rpc_cursor().to_string()); + Ok(GetEventsResponse { events, continuation_token }) } (EventBlockId::Pending, EventBlockId::Num(_)) => Err(StarknetApiError::unexpected( @@ -1056,7 +1059,7 @@ where &self, id: BlockIdOrTag, ) -> StarknetApiResult { - let provider = self.inner.backend.blockchain.provider(); + let provider = &self.inner.storage; let id = match id { BlockIdOrTag::L1Accepted => EventBlockId::Pending, @@ -1096,7 +1099,7 @@ where contracts_storage_keys: Option>, ) -> StarknetApiResult { self.on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; let Some(block_num) = provider.convert_block_id(block_id)? else { return Err(StarknetApiError::BlockNotFound); @@ -1184,15 +1187,14 @@ where // `StarknetApiExt` Implementations ///////////////////////////////////////////////////// -impl StarknetApi +impl StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool + 'static, PP: PendingBlockProvider, { async fn blocks(&self, request: GetBlocksRequest) -> StarknetApiResult { self.on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; // Parse continuation token to get starting point let start_from = if let Some(token_str) = request.result_page_request.continuation_token @@ -1268,7 +1270,7 @@ where request: GetTransactionsRequest, ) -> StarknetApiResult { self.on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; // Resolve the starting point for this query. let start_from = if let Some(token_str) = request.result_page_request.continuation_token @@ -1338,7 +1340,7 @@ where async fn total_transactions(&self) -> StarknetApiResult { self.on_io_blocking_task(move |this| { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; let total = provider.total_transactions()? as TxNumber; Ok(total) }) @@ -1346,9 +1348,8 @@ where } } -impl Clone for StarknetApi +impl Clone for StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool, PP: PendingBlockProvider, { diff --git a/crates/rpc/rpc/src/starknet/read.rs b/crates/rpc/rpc/src/starknet/read.rs index 7e4eaa36e..70b439602 100644 --- a/crates/rpc/rpc/src/starknet/read.rs +++ b/crates/rpc/rpc/src/starknet/read.rs @@ -40,15 +40,14 @@ use crate::cartridge; use crate::starknet::pending::PendingBlockProvider; #[async_trait] -impl StarknetApiServer for StarknetApi +impl StarknetApiServer for StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool + Send + Sync + 'static, PoolTx: From, Pending: PendingBlockProvider, { async fn chain_id(&self) -> RpcResult { - Ok(self.inner.backend.chain_spec.id().id()) + Ok(self.inner.chain_spec.id().id()) } async fn get_nonce( @@ -149,10 +148,18 @@ where // get the state and block env at the specified block for function call execution let state = this.state(&block_id)?; let env = this.block_env_at(&block_id)?; - let cfg_env = this.inner.backend.executor_factory.cfg().clone(); + // let cfg_env = this.inner.backend.executor_factory.cfg().clone(); + let cfg_env = this.inner.chain_spec.versioned_constants_overrides().unwrap(); let max_call_gas = this.inner.config.max_call_gas.unwrap_or(1_000_000_000); - let result = super::blockifier::call(state, env, cfg_env, request, max_call_gas)?; + let result = super::blockifier::call( + this.inner.chain_spec.as_ref(), + state, + env, + cfg_env, + request, + max_call_gas, + )?; Ok(CallResponse { result }) }) .await? @@ -177,7 +184,7 @@ where simulation_flags: Vec, block_id: BlockIdOrTag, ) -> RpcResult> { - let chain = self.inner.backend.chain_spec.id(); + let chain = self.inner.chain_spec.id(); let transactions = request .into_iter() @@ -193,7 +200,8 @@ where // If the node is run with transaction validation disabled, then we should not validate // transactions when estimating the fee even if the `SKIP_VALIDATE` flag is not set. let should_validate = !skip_validate - && self.inner.backend.executor_factory.execution_flags().account_validation(); + // && self.inner.backend.executor_factory.execution_flags().account_validation(); + && self.inner.config.simulation_flags.account_validation(); // We don't care about the nonce when estimating the fee as the nonce value // doesn't affect transaction execution. @@ -213,7 +221,6 @@ where // Paymaster is the first dev account in the genesis. let (paymaster_address, paymaster_alloc) = self .inner - .backend .chain_spec .genesis() .accounts() @@ -228,14 +235,8 @@ where return Err(StarknetApiError::unexpected("Paymaster is not a dev account").into()); }; - let state = self - .inner - .backend - .blockchain - .provider() - .latest() - .map(Arc::new) - .map_err(StarknetApiError::from)?; + let state = + self.inner.storage.latest().map(Arc::new).map_err(StarknetApiError::from)?; let mut ctrl_deploy_txs = Vec::new(); @@ -267,7 +268,7 @@ where paymaster_private_key, paymaster_nonce, tx, - self.inner.backend.chain_spec.id(), + self.inner.chain_spec.id(), state.clone(), &api, ) @@ -308,7 +309,7 @@ where block_id: BlockIdOrTag, ) -> RpcResult { self.on_cpu_blocking_task(move |this| async move { - let chain_id = this.inner.backend.chain_spec.id(); + let chain_id = this.inner.chain_spec.id(); let tx = message.into_tx_with_chain_id(chain_id); let hash = tx.calculate_hash(); diff --git a/crates/rpc/rpc/src/starknet/trace.rs b/crates/rpc/rpc/src/starknet/trace.rs index 50b313a71..9734d11cc 100644 --- a/crates/rpc/rpc/src/starknet/trace.rs +++ b/crates/rpc/rpc/src/starknet/trace.rs @@ -1,5 +1,5 @@ use jsonrpsee::core::{async_trait, RpcResult}; -use katana_executor::{ExecutionResult, ExecutorFactory, ResultAndStates}; +use katana_executor::{ExecutionResult, ResultAndStates}; use katana_pool::TransactionPool; use katana_primitives::block::{BlockHashOrNumber, BlockIdOrTag, ConfirmedBlockIdOrTag}; use katana_primitives::execution::TypedTransactionExecutionInfo; @@ -18,9 +18,8 @@ use katana_rpc_types::{BroadcastedTxWithChainId, SimulationFlag}; use super::StarknetApi; use crate::starknet::pending::PendingBlockProvider; -impl StarknetApi +impl StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool + Send + Sync + 'static, PoolTx: From, Pending: PendingBlockProvider, @@ -31,7 +30,7 @@ where transactions: Vec, simulation_flags: Vec, ) -> Result, StarknetApiError> { - let chain = self.inner.backend.chain_spec.id(); + let chain = self.inner.chain_spec.id(); let executables = transactions .into_iter() @@ -45,12 +44,14 @@ where // If the node is run with transaction validation disabled, then we should not validate // even if the `SKIP_VALIDATE` flag is not set. let should_validate = !simulation_flags.contains(&SimulationFlag::SkipValidate) - && self.inner.backend.executor_factory.execution_flags().account_validation(); + // && self.inner.backend.executor_factory.execution_flags().account_validation(); + && self.inner.config.simulation_flags.account_validation(); // If the node is run with fee charge disabled, then we should disable charing fees even // if the `SKIP_FEE_CHARGE` flag is not set. let should_charge_fee = !simulation_flags.contains(&SimulationFlag::SkipFeeCharge) - && self.inner.backend.executor_factory.execution_flags().fee(); + // && self.inner.backend.executor_factory.execution_flags().fee(); + && self.inner.config.simulation_flags.fee(); let flags = katana_executor::ExecutionFlags::new() .with_account_validation(should_validate) @@ -62,8 +63,11 @@ where let env = self.block_env_at(&block_id)?; // use the blockifier utils function - let cfg_env = self.inner.backend.executor_factory.cfg().clone(); - let results = super::blockifier::simulate(state, env, cfg_env, executables, flags); + // let cfg_env = self.inner.backend.executor_factory.cfg().clone(); + let chain_spec = self.inner.chain_spec.as_ref(); + let cfg_env = self.inner.chain_spec.versioned_constants_overrides().unwrap(); + let results = + super::blockifier::simulate(chain_spec, state, env, cfg_env, executables, flags); let mut simulated = Vec::with_capacity(results.len()); for (i, ResultAndStates { result, .. }) in results.into_iter().enumerate() { @@ -97,7 +101,7 @@ where ) -> Result, StarknetApiError> { use StarknetApiError::BlockNotFound; - let provider = self.inner.backend.blockchain.provider(); + let provider = &self.inner.storage; let block_id: BlockHashOrNumber = match block_id { ConfirmedBlockIdOrTag::L1Accepted => { @@ -139,9 +143,8 @@ where } #[async_trait] -impl StarknetTraceApiServer for StarknetApi +impl StarknetTraceApiServer for StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool + Send + Sync + 'static, PoolTx: From, Pending: PendingBlockProvider, diff --git a/crates/rpc/rpc/src/starknet/write.rs b/crates/rpc/rpc/src/starknet/write.rs index f33092ff1..691f1b810 100644 --- a/crates/rpc/rpc/src/starknet/write.rs +++ b/crates/rpc/rpc/src/starknet/write.rs @@ -1,5 +1,4 @@ use jsonrpsee::core::{async_trait, RpcResult}; -use katana_executor::ExecutorFactory; use katana_pool::TransactionPool; use katana_rpc_api::error::starknet::StarknetApiError; use katana_rpc_api::starknet::StarknetWriteApiServer; @@ -13,9 +12,8 @@ use katana_rpc_types::{BroadcastedTx, BroadcastedTxWithChainId}; use super::StarknetApi; use crate::starknet::pending::PendingBlockProvider; -impl StarknetApi +impl StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool + Send + Sync + 'static, PoolTx: From, Pending: PendingBlockProvider, @@ -29,7 +27,7 @@ where return Err(StarknetApiError::UnsupportedTransactionVersion); } - let chain_id = this.inner.backend.chain_spec.id(); + let chain_id = this.inner.chain_spec.id(); let tx = BroadcastedTxWithChainId { tx: BroadcastedTx::Invoke(tx), chain: chain_id }; let transaction_hash = this.inner.pool.add_transaction(tx.into()).await?; @@ -47,7 +45,7 @@ where return Err(StarknetApiError::UnsupportedTransactionVersion); } - let chain_id = this.inner.backend.chain_spec.id(); + let chain_id = this.inner.chain_spec.id(); let class_hash = tx.contract_class.hash().map_err(|_| StarknetApiError::InvalidContractClass)?; @@ -68,7 +66,7 @@ where return Err(StarknetApiError::UnsupportedTransactionVersion); } - let chain_id = this.inner.backend.chain_spec.id(); + let chain_id = this.inner.chain_spec.id(); let contract_address = tx.contract_address(); let tx = BroadcastedTxWithChainId { tx: BroadcastedTx::DeployAccount(tx), chain: chain_id }; @@ -81,9 +79,8 @@ where } #[async_trait] -impl StarknetWriteApiServer for StarknetApi +impl StarknetWriteApiServer for StarknetApi where - EF: ExecutorFactory, Pool: TransactionPool + Send + Sync + 'static, PoolTx: From, Pending: PendingBlockProvider, diff --git a/crates/rpc/rpc/src/utils/events.rs b/crates/rpc/rpc/src/utils/events.rs index a36e70e44..9c753aee3 100644 --- a/crates/rpc/rpc/src/utils/events.rs +++ b/crates/rpc/rpc/src/utils/events.rs @@ -85,6 +85,7 @@ impl PartialCursor { } } +#[allow(unused)] pub fn fetch_pending_events( pending_block: &PreConfirmedBlockWithReceipts, filter: &Filter, diff --git a/crates/storage/provider/provider-api/src/lib.rs b/crates/storage/provider/provider-api/src/lib.rs index 31fdfcfe7..3d21f920a 100644 --- a/crates/storage/provider/provider-api/src/lib.rs +++ b/crates/storage/provider/provider-api/src/lib.rs @@ -6,6 +6,7 @@ pub mod block; pub mod contract; pub mod env; mod error; +pub mod pending; pub mod stage; pub mod state; pub mod state_update; diff --git a/crates/storage/provider/provider-api/src/pending.rs b/crates/storage/provider/provider-api/src/pending.rs new file mode 100644 index 000000000..e16398fc5 --- /dev/null +++ b/crates/storage/provider/provider-api/src/pending.rs @@ -0,0 +1,27 @@ +use katana_primitives::block::PartialHeader; +use katana_primitives::env::BlockEnv; +use katana_primitives::receipt::Receipt; +use katana_primitives::transaction::{TxHash, TxWithHash}; + +use crate::state::StateProvider; +use crate::ProviderResult; + +#[auto_impl::auto_impl(&, Box, Arc)] +pub trait PendingDataProvider: Send + Sync { + fn state(&self) -> ProviderResult>>; + + // returns block header, transactions, and receipts + fn block_header(&self) -> ProviderResult>; + + fn block_env(&self) -> ProviderResult>; + + fn block_transaction_count(&self) -> ProviderResult>; + + fn transaction_by_block_id_and_index(&self) -> ProviderResult>; + + fn transaction(&self, hash: TxHash) -> ProviderResult>; + + fn receipt(&self, hash: TxHash) -> ProviderResult>; + + fn state_update(&self) -> ProviderResult>; +} diff --git a/crates/storage/provider/provider/src/test_utils.rs b/crates/storage/provider/provider/src/test_utils.rs index fa28f7f14..11170b8ec 100644 --- a/crates/storage/provider/provider/src/test_utils.rs +++ b/crates/storage/provider/provider/src/test_utils.rs @@ -19,7 +19,7 @@ pub fn test_provider() -> DbProvider { /// Initializes the provider with a genesis block and states. fn initialize_test_provider(provider: &P) { - let chain = create_chain_for_testing(); + let chain = get_chain_for_testing(); let hash = BlockHash::ZERO; let status = FinalityStatus::AcceptedOnL2; @@ -34,7 +34,7 @@ fn initialize_test_provider(provider: &P) { /// Creates a genesis config specifically for testing purposes. /// This includes: /// - An account with simple `__execute__` function, deployed at address `0x1`. -fn create_chain_for_testing() -> katana_chain_spec::dev::ChainSpec { +pub fn get_chain_for_testing() -> katana_chain_spec::dev::ChainSpec { let mut chain = katana_chain_spec::dev::DEV_UNALLOCATED.clone(); let class_hash = felt!("0x111"); From 44fa6347ee10ad09faae28d908c4b4c026adbae7 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 6 Oct 2025 17:36:02 -0400 Subject: [PATCH 02/49] hook up node impleemntion to the cli --- crates/cli/src/full.rs | 105 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/full.rs b/crates/cli/src/full.rs index 13488c494..fa905acaf 100644 --- a/crates/cli/src/full.rs +++ b/crates/cli/src/full.rs @@ -1,11 +1,18 @@ use std::path::PathBuf; -use anyhow::{anyhow, Result}; +use anyhow::{Context, Result}; pub use clap::Parser; +use katana_node::config::db::DbConfig; +use katana_node::config::metrics::MetricsConfig; +use katana_node::config::rpc::RpcConfig; +use katana_node::full; use serde::{Deserialize, Serialize}; +use tracing::info; use crate::options::*; +pub(crate) const LOG_TARGET: &str = "katana::cli::full"; + #[derive(Parser, Debug, Serialize, Deserialize, Default, Clone, PartialEq)] #[command(next_help_heading = "Full node options")] pub struct FullNodeArgs { @@ -21,6 +28,11 @@ pub struct FullNodeArgs { #[arg(value_name = "PATH")] pub db_dir: Option, + /// Gateway API key for accessing the sequencer gateway. + #[arg(long)] + #[arg(value_name = "KEY")] + pub gateway_api_key: Option, + #[command(flatten)] pub logging: LoggingOptions, @@ -42,6 +54,95 @@ pub struct FullNodeArgs { impl FullNodeArgs { pub async fn execute(&self) -> Result<()> { - Err(anyhow!("Full node is not implemented yet!")) + // Initialize logging with tracer + let tracer_config = self.tracer_config(); + katana_tracing::init(self.logging.log_format, tracer_config).await?; + self.start_node().await + } + + async fn start_node(&self) -> Result<()> { + // Build the node + let config = self.config()?; + let node = full::Node::build(config).context("failed to build full node")?; + + if !self.silent { + info!(target: LOG_TARGET, "Starting full node"); + } + + // Launch the node + let handle = node.launch().await.context("failed to launch full node")?; + + // Wait until an OS signal (ie SIGINT, SIGTERM) is received or the node is shutdown. + tokio::select! { + _ = katana_utils::wait_shutdown_signals() => { + // Gracefully shutdown the node before exiting + handle.stop().await?; + }, + + _ = handle.stopped() => { } + } + + info!("Shutting down."); + + Ok(()) + } + + fn config(&self) -> Result { + let db = self.db_config(); + let rpc = self.rpc_config()?; + let metrics = self.metrics_config(); + + Ok(full::Config { db, rpc, metrics, gateway_api_key: self.gateway_api_key.clone() }) + } + + fn db_config(&self) -> DbConfig { + DbConfig { dir: self.db_dir.clone() } + } + + fn rpc_config(&self) -> Result { + #[cfg(feature = "server")] + { + use std::time::Duration; + + let cors_origins = self.server.http_cors_origins.clone(); + + Ok(RpcConfig { + apis: Default::default(), + port: self.server.http_port, + addr: self.server.http_addr, + max_connections: self.server.max_connections, + max_concurrent_estimate_fee_requests: None, + max_request_body_size: None, + max_response_body_size: None, + timeout: self.server.timeout.map(Duration::from_secs), + cors_origins, + #[cfg(feature = "explorer")] + explorer: self.explorer.explorer, + max_event_page_size: Some(self.server.max_event_page_size), + max_proof_keys: Some(self.server.max_proof_keys), + max_call_gas: Some(self.server.max_call_gas), + }) + } + + #[cfg(not(feature = "server"))] + { + Ok(RpcConfig::default()) + } + } + + fn metrics_config(&self) -> Option { + #[cfg(feature = "server")] + if self.metrics.metrics { + Some(MetricsConfig { addr: self.metrics.metrics_addr, port: self.metrics.metrics_port }) + } else { + None + } + + #[cfg(not(feature = "server"))] + None + } + + fn tracer_config(&self) -> Option { + self.tracer.config() } } From 428a4b101f4fb6fc22e9b6e092d0b887f2036903 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 6 Oct 2025 19:06:29 -0400 Subject: [PATCH 03/49] fix some stuff (i think) --- bin/katana/src/cli/init/mod.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bin/katana/src/cli/init/mod.rs b/bin/katana/src/cli/init/mod.rs index 052635fdd..d98305cc3 100644 --- a/bin/katana/src/cli/init/mod.rs +++ b/bin/katana/src/cli/init/mod.rs @@ -62,7 +62,8 @@ use anyhow::Context; use clap::builder::NonEmptyStringValueParser; use clap::{Args, Subcommand}; use deployment::DeploymentOutcome; -use katana_chain_spec::rollup::{ChainConfigDir, FeeContract}; +use katana_chain_spec::rollup::ChainConfigDir; +use katana_chain_spec::FeeContracts; use katana_chain_spec::{rollup, SettlementLayer}; use katana_genesis::allocation::DevAllocationsGenerator; use katana_genesis::constant::DEFAULT_PREFUNDED_ACCOUNT_BALANCE; @@ -246,8 +247,9 @@ impl RollupArgs { slot::add_paymasters_to_genesis(&mut genesis, &output.slot_paymasters.unwrap_or_default()); // At the moment, the fee token is limited to a predefined token. - let fee_contract = FeeContract::default(); - let chain_spec = rollup::ChainSpec { id, genesis, settlement, fee_contract }; + let fee_contracts = FeeContracts::default(); + let versioned_constants_overrides = None; + let chain_spec = rollup::ChainSpec { id, genesis, settlement, fee_contracts, versioned_constants_overrides }; if let Some(path) = self.output_path { let dir = ChainConfigDir::create(path)?; @@ -384,8 +386,9 @@ impl SovereignArgs { slot::add_paymasters_to_genesis(&mut genesis, &output.slot_paymasters.unwrap_or_default()); // At the moment, the fee token is limited to a predefined token. - let fee_contract = FeeContract::default(); - let chain_spec = rollup::ChainSpec { id, genesis, settlement, fee_contract }; + let fee_contracts = FeeContracts::default(); + let versioned_constants_overrides = None; + let chain_spec = rollup::ChainSpec { id, genesis, settlement, fee_contracts, versioned_constants_overrides }; if let Some(path) = self.output_path { let dir = ChainConfigDir::create(path)?; From 35d895419f56b7a9d381586ee103d9c3f4653802 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 7 Oct 2025 16:54:53 -0400 Subject: [PATCH 04/49] wip --- crates/cli/src/full.rs | 4 ++-- crates/node/src/full/mod.rs | 2 ++ crates/sync/pipeline/src/lib.rs | 3 ++- crates/sync/stage/src/blocks/downloader.rs | 11 ++++++++--- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/cli/src/full.rs b/crates/cli/src/full.rs index fa905acaf..5cdd74cfe 100644 --- a/crates/cli/src/full.rs +++ b/crates/cli/src/full.rs @@ -26,7 +26,7 @@ pub struct FullNodeArgs { /// previously initialized Katana database. #[arg(long)] #[arg(value_name = "PATH")] - pub db_dir: Option, + pub db_dir: PathBuf, /// Gateway API key for accessing the sequencer gateway. #[arg(long)] @@ -96,7 +96,7 @@ impl FullNodeArgs { } fn db_config(&self) -> DbConfig { - DbConfig { dir: self.db_dir.clone() } + DbConfig { dir: Some(self.db_dir.clone()) } } fn rpc_config(&self) -> Result { diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index e5c37f370..104b5030c 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -123,6 +123,8 @@ impl Node { } pub async fn launch(self) -> Result { + println!("Launching node"); + if let Some(ref cfg) = self.config.metrics { let reports: Vec> = vec![Box::new(self.db.clone()) as Box]; let exporter = PrometheusRecorder::current().expect("qed; should exist at this point"); diff --git a/crates/sync/pipeline/src/lib.rs b/crates/sync/pipeline/src/lib.rs index ea184b82a..d9d789177 100644 --- a/crates/sync/pipeline/src/lib.rs +++ b/crates/sync/pipeline/src/lib.rs @@ -75,6 +75,7 @@ //! [Erigon]: https://github.com/erigontech/erigon use core::future::IntoFuture; +use std::sync::atomic::{AtomicU64, Ordering}; use futures::future::BoxFuture; use katana_primitives::block::BlockNumber; @@ -83,7 +84,7 @@ use katana_provider_api::ProviderError; use katana_stage::{Stage, StageExecutionInput, StageExecutionOutput}; use tokio::sync::watch; use tokio::task::yield_now; -use tracing::{debug, error, info, info_span, Instrument}; +use tracing::{debug, error, info, info_span, Instrument, Instrument}; /// The result of a pipeline execution. pub type PipelineResult = Result; diff --git a/crates/sync/stage/src/blocks/downloader.rs b/crates/sync/stage/src/blocks/downloader.rs index 3677c5b8d..79de2d799 100644 --- a/crates/sync/stage/src/blocks/downloader.rs +++ b/crates/sync/stage/src/blocks/downloader.rs @@ -16,6 +16,7 @@ use anyhow::Result; use katana_gateway_client::Client as GatewayClient; use katana_gateway_types::StateUpdateWithBlock; use katana_primitives::block::BlockNumber; +use tracing::{info, info_span, trace, Instrument}; use crate::downloader::{BatchDownloader, Downloader}; @@ -86,9 +87,12 @@ where to: BlockNumber, ) -> impl Future, katana_gateway_client::Error>> + Send { - // convert the range to a list of block keys - let block_keys = (from..=to).collect::>(); - self.inner.download(block_keys) + async move { + // convert the range to a list of block keys + let block_keys = (from..=to).collect::>(); + self.inner.download(block_keys).await + } + .instrument(info_span!("download_blocks", %from, %to)) } } @@ -125,6 +129,7 @@ mod impls { &self, key: &Self::Key, ) -> impl Future> { + info!(block = %key, "Downloading block."); async { match self.gateway.get_state_update_with_block((*key).into()).await.inspect_err( |error| error!(block = %*key, ?error, "Error downloading block from gateway."), From c1f26b1b173c0d5b547e30e7a8fa154c05e20185 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 7 Oct 2025 18:48:18 -0400 Subject: [PATCH 05/49] wip --- crates/sync/pipeline/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sync/pipeline/src/lib.rs b/crates/sync/pipeline/src/lib.rs index d9d789177..204f45b4e 100644 --- a/crates/sync/pipeline/src/lib.rs +++ b/crates/sync/pipeline/src/lib.rs @@ -84,7 +84,7 @@ use katana_provider_api::ProviderError; use katana_stage::{Stage, StageExecutionInput, StageExecutionOutput}; use tokio::sync::watch; use tokio::task::yield_now; -use tracing::{debug, error, info, info_span, Instrument, Instrument}; +use tracing::{debug, error, info, info_span, Instrument}; /// The result of a pipeline execution. pub type PipelineResult = Result; From 16409a2070fde91225666a4e058719dfec91158d Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 7 Oct 2025 23:44:38 -0400 Subject: [PATCH 06/49] wip --- .tool-versions | 1 + crates/node/src/full/mod.rs | 4 +++ crates/rpc/rpc/src/starknet/blockifier.rs | 4 +-- .../provider/provider-api/src/state_update.rs | 6 ++++ crates/storage/provider/provider/src/lib.rs | 7 +++++ .../provider/provider/src/providers/db/mod.rs | 28 +++++++++++++++++++ .../provider/src/providers/fork/mod.rs | 7 +++++ crates/sync/stage/src/blocks/downloader.rs | 4 +-- 8 files changed, 57 insertions(+), 4 deletions(-) diff --git a/.tool-versions b/.tool-versions index cc60fd625..1a66fa783 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1,2 @@ scarb 2.8.2 +katana 1.7.0 diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index 104b5030c..b8180faca 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -204,7 +204,11 @@ pub struct LaunchedNode { impl LaunchedNode { pub async fn stop(&self) -> Result<()> { self.rpc.stop()?; + self.pipeline.stop(); + + self.pipeline.stopped().await; self.task_manager.shutdown().await; + Ok(()) } diff --git a/crates/rpc/rpc/src/starknet/blockifier.rs b/crates/rpc/rpc/src/starknet/blockifier.rs index d9f5357c5..5fbb44bc0 100644 --- a/crates/rpc/rpc/src/starknet/blockifier.rs +++ b/crates/rpc/rpc/src/starknet/blockifier.rs @@ -176,7 +176,7 @@ mod tests { entry_point_selector: selector!("foo"), }; - let result = super::call(&chain_spec, state, block_env, cfg_env, call, max_call_gas); + let result = super::call(&chain_spec, state, block_env, &cfg_env, call, max_call_gas); assert!(matches!(result, Err(StarknetApiError::ContractNotFound))); } @@ -197,7 +197,7 @@ mod tests { entry_point_selector: selector!("foobar"), }; - let result = super::call(&chain_spec, state, block_env, cfg_env, call, max_call_gas); + let result = super::call(&chain_spec, state, block_env, &cfg_env, call, max_call_gas); assert!(matches!(result, Err(StarknetApiError::EntrypointNotFound))); } } diff --git a/crates/storage/provider/provider-api/src/state_update.rs b/crates/storage/provider/provider-api/src/state_update.rs index 7d706a285..143a55b16 100644 --- a/crates/storage/provider/provider-api/src/state_update.rs +++ b/crates/storage/provider/provider-api/src/state_update.rs @@ -18,6 +18,12 @@ pub trait StateUpdateProvider: Send + Sync { block_id: BlockHashOrNumber, ) -> ProviderResult>>; + /// Returns all declared deprecated class hashes at the given block. + fn declared_deprecated_classes( + &self, + block_id: BlockHashOrNumber, + ) -> ProviderResult>>; + /// Returns all deployed contracts at the given block. fn deployed_contracts( &self, diff --git a/crates/storage/provider/provider/src/lib.rs b/crates/storage/provider/provider/src/lib.rs index c634ff4bb..c9445f614 100644 --- a/crates/storage/provider/provider/src/lib.rs +++ b/crates/storage/provider/provider/src/lib.rs @@ -285,6 +285,13 @@ where self.provider.declared_classes(block_id) } + fn declared_deprecated_classes( + &self, + block_id: BlockHashOrNumber, + ) -> ProviderResult>> { + self.provider.declared_deprecated_classes(block_id) + } + fn deployed_contracts( &self, block_id: BlockHashOrNumber, diff --git a/crates/storage/provider/provider/src/providers/db/mod.rs b/crates/storage/provider/provider/src/providers/db/mod.rs index ff822dcd5..253128a46 100644 --- a/crates/storage/provider/provider/src/providers/db/mod.rs +++ b/crates/storage/provider/provider/src/providers/db/mod.rs @@ -409,6 +409,34 @@ impl StateUpdateProvider for DbProvider { } } + fn declared_deprecated_classes( + &self, + block_id: BlockHashOrNumber, + ) -> ProviderResult>> { + let db_tx = self.0.tx()?; + let block_num = self.block_number_by_id(block_id)?; + + if let Some(block_num) = block_num { + let declared_classes = dup_entries::, _>( + &db_tx, + block_num, + |entry| { + let (_, class_hash) = entry?; + if db_tx.get::(class_hash)?.is_none() { + Ok(Some(class_hash)) + } else { + Ok(None) + } + }, + )?; + + db_tx.commit()?; + Ok(Some(declared_classes)) + } else { + Ok(None) + } + } + fn deployed_contracts( &self, block_id: BlockHashOrNumber, diff --git a/crates/storage/provider/provider/src/providers/fork/mod.rs b/crates/storage/provider/provider/src/providers/fork/mod.rs index 8a88ffe78..3f6e1d2df 100644 --- a/crates/storage/provider/provider/src/providers/fork/mod.rs +++ b/crates/storage/provider/provider/src/providers/fork/mod.rs @@ -135,6 +135,13 @@ impl StateUpdateProvider for ForkedProvider { self.provider.declared_classes(block_id) } + fn declared_deprecated_classes( + &self, + block_id: BlockHashOrNumber, + ) -> ProviderResult>> { + self.provider.declared_deprecated_classes(block_id) + } + fn deployed_contracts( &self, block_id: BlockHashOrNumber, diff --git a/crates/sync/stage/src/blocks/downloader.rs b/crates/sync/stage/src/blocks/downloader.rs index 79de2d799..a8e3e8c52 100644 --- a/crates/sync/stage/src/blocks/downloader.rs +++ b/crates/sync/stage/src/blocks/downloader.rs @@ -16,7 +16,7 @@ use anyhow::Result; use katana_gateway_client::Client as GatewayClient; use katana_gateway_types::StateUpdateWithBlock; use katana_primitives::block::BlockNumber; -use tracing::{info, info_span, trace, Instrument}; +use tracing::{info_span, Instrument}; use crate::downloader::{BatchDownloader, Downloader}; @@ -129,7 +129,7 @@ mod impls { &self, key: &Self::Key, ) -> impl Future> { - info!(block = %key, "Downloading block."); + trace!(block = %key, "Downloading block."); async { match self.gateway.get_state_update_with_block((*key).into()).await.inspect_err( |error| error!(block = %*key, ?error, "Error downloading block from gateway."), From f365435743bd3c296ad82fdfa4609cb614a4a6cb Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 8 Oct 2025 01:51:25 -0400 Subject: [PATCH 07/49] wip --- crates/node/src/full/mod.rs | 80 +++++++++++++++++++++++++--- crates/rpc/rpc/src/starknet/list.rs | 1 - crates/rpc/rpc/src/starknet/write.rs | 1 - 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index b8180faca..6ace114be 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -6,6 +6,8 @@ use std::sync::Arc; use anyhow::Result; use http::header::CONTENT_TYPE; use http::Method; +use jsonrpsee::RpcModule; +use katana_executor::ExecutionFlags; use katana_gateway_client::Client as SequencerGateway; use katana_metrics::exporters::prometheus::PrometheusRecorder; use katana_metrics::{Report, Server as MetricsServer}; @@ -17,7 +19,9 @@ use katana_pool::TxPool; use katana_primitives::transaction::ExecutableTxWithHash; use katana_provider::providers::db::DbProvider; use katana_rpc::cors::Cors; +use katana_rpc::starknet::{StarknetApi, StarknetApiConfig}; use katana_rpc::{RpcServer, RpcServerHandle}; +use katana_rpc_api::starknet::{StarknetApiServer, StarknetTraceApiServer, StarknetWriteApiServer}; use katana_stage::blocks::BatchBlockDownloader; use katana_stage::{Blocks, Classes}; use katana_tasks::TaskManager; @@ -32,7 +36,7 @@ mod pool; use exit::NodeStoppedFuture; use tip_watcher::ChainTipWatcher; -use crate::config::rpc::RpcConfig; +use crate::config::rpc::{RpcConfig, RpcModuleKind}; use crate::full::pool::{FullNodePool, GatewayProxyValidator}; #[derive(Debug, Clone)] @@ -73,6 +77,7 @@ impl Node { // -- build task manager let task_manager = TaskManager::current(); + let task_spawner = task_manager.task_spawner(); // -- build db and storage provider @@ -101,15 +106,76 @@ impl Node { let (mut pipeline, _) = Pipeline::new(provider.clone(), 64); let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 3); pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); - pipeline.add_stage(Classes::new(provider, gateway_client.clone(), 3)); + pipeline.add_stage(Classes::new(provider.clone(), gateway_client.clone(), 3)); + + // --- build rpc server + + let mut rpc_modules = RpcModule::new(()); let cors = Cors::new() - .allow_origins(config.rpc.cors_origins.clone()) - // Allow `POST` when accessing the resource - .allow_methods([Method::POST, Method::GET]) - .allow_headers([CONTENT_TYPE, "argent-client".parse().unwrap(), "argent-version".parse().unwrap()]); + .allow_origins(config.rpc.cors_origins.clone()) + // Allow `POST` when accessing the resource + .allow_methods([Method::POST, Method::GET]) + .allow_headers([CONTENT_TYPE, "argent-client".parse().unwrap(), "argent-version".parse().unwrap()]); + + // // --- build starknet api + + let starknet_api_cfg = StarknetApiConfig { + max_event_page_size: config.rpc.max_event_page_size, + max_proof_keys: config.rpc.max_proof_keys, + max_call_gas: config.rpc.max_call_gas, + max_concurrent_estimate_fee_requests: config.rpc.max_concurrent_estimate_fee_requests, + simulation_flags: ExecutionFlags::default(), + #[cfg(feature = "cartridge")] + paymaster: None, + }; + + let chain_spec = config.chain_spec.clone(); + + let starknet_api = StarknetApi::new( + chain_spec.clone(), + provider.clone(), + pool.clone(), + task_spawner.clone(), + starknet_api_cfg, + ); + + if config.rpc.apis.contains(&RpcModuleKind::Starknet) { + #[cfg(feature = "explorer")] + if config.rpc.explorer { + use katana_rpc_api::starknet_ext::StarknetApiExtServer; + rpc_modules.merge(StarknetApiExtServer::into_rpc(starknet_api.clone()))?; + } - let rpc_server = RpcServer::new().metrics(true).health_check(true).cors(cors); + rpc_modules.merge(StarknetApiServer::into_rpc(starknet_api.clone()))?; + rpc_modules.merge(StarknetWriteApiServer::into_rpc(starknet_api.clone()))?; + rpc_modules.merge(StarknetTraceApiServer::into_rpc(starknet_api.clone()))?; + } + + #[allow(unused_mut)] + let mut rpc_server = + RpcServer::new().metrics(true).health_check(true).cors(cors).module(rpc_modules)?; + + #[cfg(feature = "explorer")] + { + rpc_server = rpc_server.explorer(config.rpc.explorer); + } + + if let Some(timeout) = config.rpc.timeout { + rpc_server = rpc_server.timeout(timeout); + }; + + if let Some(max_connections) = config.rpc.max_connections { + rpc_server = rpc_server.max_connections(max_connections); + } + + if let Some(max_request_body_size) = config.rpc.max_request_body_size { + rpc_server = rpc_server.max_request_body_size(max_request_body_size); + } + + if let Some(max_response_body_size) = config.rpc.max_response_body_size { + rpc_server = rpc_server.max_response_body_size(max_response_body_size); + } Ok(Node { db, diff --git a/crates/rpc/rpc/src/starknet/list.rs b/crates/rpc/rpc/src/starknet/list.rs index dc8a900a8..ea346f479 100644 --- a/crates/rpc/rpc/src/starknet/list.rs +++ b/crates/rpc/rpc/src/starknet/list.rs @@ -1,7 +1,6 @@ //! Implementation of list endpoints for the Starknet API. use jsonrpsee::core::{async_trait, RpcResult}; -use katana_executor::ExecutorFactory; use katana_pool::TransactionPool; use katana_primitives::transaction::TxNumber; use katana_rpc_api::starknet_ext::StarknetApiExtServer; diff --git a/crates/rpc/rpc/src/starknet/write.rs b/crates/rpc/rpc/src/starknet/write.rs index 691f1b810..e4b6615a7 100644 --- a/crates/rpc/rpc/src/starknet/write.rs +++ b/crates/rpc/rpc/src/starknet/write.rs @@ -9,7 +9,6 @@ use katana_rpc_types::broadcasted::{ }; use katana_rpc_types::{BroadcastedTx, BroadcastedTxWithChainId}; -use super::StarknetApi; use crate::starknet::pending::PendingBlockProvider; impl StarknetApi From fc01f9615f05546814b0637aaf346e6ca33cd14e Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Thu, 9 Oct 2025 00:41:00 -0400 Subject: [PATCH 08/49] working version of rpc server integration --- Cargo.lock | 1 + bin/katana/src/cli/init/mod.rs | 19 ++++++-- crates/gateway/gateway-server/Cargo.toml | 1 + crates/gateway/gateway-server/src/handlers.rs | 47 ++++++++++++------- crates/gateway/gateway-server/src/lib.rs | 12 +++-- crates/node/src/full/mod.rs | 12 +++-- crates/node/src/lib.rs | 2 +- crates/rpc/rpc-types/src/broadcasted.rs | 2 +- crates/rpc/rpc/src/starknet/mod.rs | 5 +- 9 files changed, 68 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94d311084..30bd20c1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6182,6 +6182,7 @@ dependencies = [ "katana-gateway-types", "katana-metrics", "katana-pool", + "katana-pool-api", "katana-primitives", "katana-provider-api", "katana-rpc", diff --git a/bin/katana/src/cli/init/mod.rs b/bin/katana/src/cli/init/mod.rs index d98305cc3..197ea8ba9 100644 --- a/bin/katana/src/cli/init/mod.rs +++ b/bin/katana/src/cli/init/mod.rs @@ -63,8 +63,7 @@ use clap::builder::NonEmptyStringValueParser; use clap::{Args, Subcommand}; use deployment::DeploymentOutcome; use katana_chain_spec::rollup::ChainConfigDir; -use katana_chain_spec::FeeContracts; -use katana_chain_spec::{rollup, SettlementLayer}; +use katana_chain_spec::{rollup, FeeContracts, SettlementLayer}; use katana_genesis::allocation::DevAllocationsGenerator; use katana_genesis::constant::DEFAULT_PREFUNDED_ACCOUNT_BALANCE; use katana_genesis::Genesis; @@ -249,7 +248,13 @@ impl RollupArgs { // At the moment, the fee token is limited to a predefined token. let fee_contracts = FeeContracts::default(); let versioned_constants_overrides = None; - let chain_spec = rollup::ChainSpec { id, genesis, settlement, fee_contracts, versioned_constants_overrides }; + let chain_spec = rollup::ChainSpec { + id, + genesis, + settlement, + fee_contracts, + versioned_constants_overrides, + }; if let Some(path) = self.output_path { let dir = ChainConfigDir::create(path)?; @@ -388,7 +393,13 @@ impl SovereignArgs { // At the moment, the fee token is limited to a predefined token. let fee_contracts = FeeContracts::default(); let versioned_constants_overrides = None; - let chain_spec = rollup::ChainSpec { id, genesis, settlement, fee_contracts, versioned_constants_overrides }; + let chain_spec = rollup::ChainSpec { + id, + genesis, + settlement, + fee_contracts, + versioned_constants_overrides, + }; if let Some(path) = self.output_path { let dir = ChainConfigDir::create(path)?; diff --git a/crates/gateway/gateway-server/Cargo.toml b/crates/gateway/gateway-server/Cargo.toml index ddebcf829..5e68497cb 100644 --- a/crates/gateway/gateway-server/Cargo.toml +++ b/crates/gateway/gateway-server/Cargo.toml @@ -13,6 +13,7 @@ katana-primitives.workspace = true katana-metrics.workspace = true katana-pool.workspace = true katana-rpc.workspace = true +katana-pool-api.workspace = true katana-rpc-api.workspace = true katana-provider-api.workspace = true serde-utils.workspace = true diff --git a/crates/gateway/gateway-server/src/handlers.rs b/crates/gateway/gateway-server/src/handlers.rs index a0df88add..640639cea 100644 --- a/crates/gateway/gateway-server/src/handlers.rs +++ b/crates/gateway/gateway-server/src/handlers.rs @@ -7,7 +7,7 @@ use katana_gateway_types::{ Block, ConfirmedReceipt, ConfirmedTransaction, ContractClass, ErrorCode, GatewayError, ReceiptBody, StateUpdate, StateUpdateWithBlock, }; -use katana_pool::TxPool; +use katana_pool_api::TransactionPool; use katana_primitives::block::{BlockHash, BlockIdOrTag, BlockNumber}; use katana_primitives::class::{ClassHash, CompiledClass, ContractClassCompilationError}; use katana_provider_api::block::{BlockIdReader, BlockProvider, BlockStatusProvider}; @@ -20,11 +20,14 @@ use starknet::core::types::ResourcePrice; /// Shared application state containing the backend #[derive(Clone)] -pub struct AppState { - pub api: StarknetApi>, +pub struct AppState { + pub api: StarknetApi>, } -impl AppState { +impl

AppState

+where + P: TransactionPool + Send + Sync + 'static, +{ // TODO(kariy): support preconfirmed blocks async fn get_block(&self, id: BlockIdOrTag) -> Result, ApiError> { self.api @@ -154,10 +157,13 @@ pub async fn health() -> Json { /// Handler for `/feeder_gateway/get_block` endpoint /// /// Returns block information for the specified block. -pub async fn get_block( - State(state): State, +pub async fn get_block

( + State(state): State>, Query(params): Query, -) -> Result, ApiError> { +) -> Result, ApiError> +where + P: TransactionPool + Send + Sync + 'static, +{ let block_id = params.block_id()?; let block = state.get_block(block_id).await?.unwrap(); Ok(Json(block)) @@ -175,10 +181,13 @@ pub enum GetStateUpdateResponse { /// Handler for `/feeder_gateway/get_state_update` endpoint /// /// Returns state update information for the specified block. -pub async fn get_state_update( - State(state): State, +pub async fn get_state_update

( + State(state): State>, Query(params): Query, -) -> Result, ApiError> { +) -> Result, ApiError> +where + P: TransactionPool + Send + Sync + 'static, +{ let include_block = params.include_block; let block_id = params.block_query.block_id()?; @@ -197,10 +206,13 @@ pub async fn get_state_update( /// Handler for `/feeder_gateway/get_class_by_hash` endpoint /// /// Returns the contract class definition for a given class hash. -pub async fn get_class_by_hash( - State(state): State, +pub async fn get_class_by_hash

( + State(state): State>, Query(params): Query, -) -> Result, ApiError> { +) -> Result, ApiError> +where + P: TransactionPool + Send + Sync + 'static, +{ let class_hash = params.class_hash; let block_id = params.block_query.block_id()?; let class = state.api.class_at_hash(block_id, class_hash).await?; @@ -210,10 +222,13 @@ pub async fn get_class_by_hash( /// Handler for `/feeder_gateway/get_compiled_class_by_class_hash` endpoint /// /// Returns the compiled (CASM) contract class for a given class hash. -pub async fn get_compiled_class_by_class_hash( - State(state): State, +pub async fn get_compiled_class_by_class_hash

( + State(state): State>, Query(params): Query, -) -> Result, ApiError> { +) -> Result, ApiError> +where + P: TransactionPool + Send + Sync + 'static, +{ let class_hash = params.class_hash; let block_id = params.block_query.block_id()?; diff --git a/crates/gateway/gateway-server/src/lib.rs b/crates/gateway/gateway-server/src/lib.rs index 99fa95983..00862617f 100644 --- a/crates/gateway/gateway-server/src/lib.rs +++ b/crates/gateway/gateway-server/src/lib.rs @@ -6,6 +6,7 @@ use axum::routing::get; use axum::Router; use katana_core::service::block_producer::BlockProducer; use katana_executor::implementation::blockifier::BlockifierFactory; +use katana_pool_api::TransactionPool; use katana_rpc::cors::Cors; use katana_rpc::starknet::StarknetApi; use tokio::net::TcpListener; @@ -69,19 +70,22 @@ impl GatewayServerHandle { /// The feeder gateway server. #[derive(Debug)] -pub struct GatewayServer { +pub struct GatewayServer { timeout: Duration, cors: Option, health_check: bool, metered: bool, - starknet_api: StarknetApi>, + starknet_api: StarknetApi>, } -impl GatewayServer { +impl GatewayServer +where + Pool: TransactionPool + Clone + Send + Sync + 'static, +{ /// Create a new feeder gateway server. pub fn new( - starknet_api: StarknetApi>, + starknet_api: StarknetApi>, ) -> Self { Self { timeout: DEFAULT_GATEWAY_TIMEOUT, diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index 6ace114be..cf48ed379 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -7,6 +7,8 @@ use anyhow::Result; use http::header::CONTENT_TYPE; use http::Method; use jsonrpsee::RpcModule; +use katana_chain_spec::ChainSpec; +use katana_core::backend::storage::Database; use katana_executor::ExecutionFlags; use katana_gateway_client::Client as SequencerGateway; use katana_metrics::exporters::prometheus::PrometheusRecorder; @@ -18,12 +20,13 @@ use katana_pool::validation::NoopValidator; use katana_pool::TxPool; use katana_primitives::transaction::ExecutableTxWithHash; use katana_provider::providers::db::DbProvider; +use katana_provider::BlockchainProvider; use katana_rpc::cors::Cors; use katana_rpc::starknet::{StarknetApi, StarknetApiConfig}; use katana_rpc::{RpcServer, RpcServerHandle}; use katana_rpc_api::starknet::{StarknetApiServer, StarknetTraceApiServer, StarknetWriteApiServer}; use katana_stage::blocks::BatchBlockDownloader; -use katana_stage::{Blocks, Classes}; +use katana_stage::{Blocks, Classes, StateTrie}; use katana_tasks::TaskManager; use tracing::{error, info}; @@ -107,6 +110,7 @@ impl Node { let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 3); pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); pipeline.add_stage(Classes::new(provider.clone(), gateway_client.clone(), 3)); + pipeline.add_stage(StateTrie::new(provider.clone())); // --- build rpc server @@ -130,11 +134,9 @@ impl Node { paymaster: None, }; - let chain_spec = config.chain_spec.clone(); - let starknet_api = StarknetApi::new( - chain_spec.clone(), - provider.clone(), + Arc::new(ChainSpec::dev()), + BlockchainProvider::new(Box::new(provider.clone())), pool.clone(), task_spawner.clone(), starknet_api_cfg, diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 2f5271a74..c2257d1a8 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -67,7 +67,7 @@ pub struct Node { task_manager: TaskManager, backend: Arc>, block_producer: BlockProducer, - gateway_server: Option, + gateway_server: Option>, } impl Node { diff --git a/crates/rpc/rpc-types/src/broadcasted.rs b/crates/rpc/rpc-types/src/broadcasted.rs index a82b5bf46..4505e38e7 100644 --- a/crates/rpc/rpc-types/src/broadcasted.rs +++ b/crates/rpc/rpc-types/src/broadcasted.rs @@ -11,7 +11,7 @@ use katana_primitives::fee::{ }; use katana_primitives::transaction::{ DeclareTx, DeclareTxV0, DeclareTxV1, DeclareTxV2, DeclareTxV3, DeclareTxWithClass, - DeployAccountTx, DeployAccountTxV1, DeployAccountTxV3, ExecutableTx, ExecutableTx, + DeployAccountTx, DeployAccountTxV1, DeployAccountTxV3, ExecutableTx, ExecutableTxWithHash, InvokeTx, InvokeTxV0, InvokeTxV1, InvokeTxV3, TxHash, TxType, }; use katana_primitives::utils::get_contract_address; diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index 0fcab986c..701e05b43 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -1042,8 +1042,9 @@ where let latest = provider.latest_number()?; let new_cursor = Cursor::new_block(latest); - let continuation_token = Some(new_cursor.into_rpc_cursor().to_string()); - Ok(GetEventsResponse { events, continuation_token }) + let continuation_token = Some(new_cursor.into_rpc_cursor().to_string()); + Ok(GetEventsResponse { events, continuation_token }) + } } (EventBlockId::Pending, EventBlockId::Num(_)) => Err(StarknetApiError::unexpected( From 10e062eac647911b0c81a9a061daf49901339541 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Thu, 9 Oct 2025 16:35:08 -0400 Subject: [PATCH 09/49] wip --- crates/node/src/full/mod.rs | 4 +--- crates/sync/pipeline/src/lib.rs | 2 +- crates/sync/stage/src/blocks/mod.rs | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index cf48ed379..55c0b198d 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -110,7 +110,7 @@ impl Node { let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 3); pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); pipeline.add_stage(Classes::new(provider.clone(), gateway_client.clone(), 3)); - pipeline.add_stage(StateTrie::new(provider.clone())); + // pipeline.add_stage(StateTrie::new(provider.clone())); // --- build rpc server @@ -191,8 +191,6 @@ impl Node { } pub async fn launch(self) -> Result { - println!("Launching node"); - if let Some(ref cfg) = self.config.metrics { let reports: Vec> = vec![Box::new(self.db.clone()) as Box]; let exporter = PrometheusRecorder::current().expect("qed; should exist at this point"); diff --git a/crates/sync/pipeline/src/lib.rs b/crates/sync/pipeline/src/lib.rs index 204f45b4e..f03f0416b 100644 --- a/crates/sync/pipeline/src/lib.rs +++ b/crates/sync/pipeline/src/lib.rs @@ -84,7 +84,7 @@ use katana_provider_api::ProviderError; use katana_stage::{Stage, StageExecutionInput, StageExecutionOutput}; use tokio::sync::watch; use tokio::task::yield_now; -use tracing::{debug, error, info, info_span, Instrument}; +use tracing::{debug, error, info, info_span, Instrument, Span}; /// The result of a pipeline execution. pub type PipelineResult = Result; diff --git a/crates/sync/stage/src/blocks/mod.rs b/crates/sync/stage/src/blocks/mod.rs index b72f7164e..5cc3de551 100644 --- a/crates/sync/stage/src/blocks/mod.rs +++ b/crates/sync/stage/src/blocks/mod.rs @@ -106,7 +106,8 @@ where .download_blocks(input.from(), input.to()) .instrument(info_span!(target: "stage", "blocks.download", from = %input.from(), to = %input.to())) .await - .map_err(Error::Gateway)?; + .map_err(Error::Gateway) + .inspect_err(|e| error!(error = %e , "Error downloading blocks."))?; let span = info_span!(target: "stage", "blocks.insert", from = %input.from(), to = %input.to()); let _enter = span.enter(); From 2d6d33267eacfc37dc17926760d35771f8f1bd10 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 13 Oct 2025 20:15:56 -0400 Subject: [PATCH 10/49] wip --- Cargo.lock | 3 --- crates/cli/src/full.rs | 18 ++++++++++++++++-- crates/node/Cargo.toml | 5 +---- crates/node/src/full/mod.rs | 17 ++++++++++++++--- crates/node/src/full/tip_watcher.rs | 2 +- crates/sync/pipeline/src/lib.rs | 3 ++- crates/sync/stage/src/blocks/downloader.rs | 11 +++-------- crates/sync/stage/src/blocks/mod.rs | 3 +-- crates/sync/stage/src/classes.rs | 6 +++++- crates/sync/stage/tests/trie.rs | 7 +++++++ crates/tracing/src/lib.rs | 2 +- 11 files changed, 51 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30bd20c1a..3f3b2a89f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6293,7 +6293,6 @@ version = "1.7.0" dependencies = [ "alloy-provider", "anyhow", - "clap", "futures", "http 1.3.1", "jsonrpsee", @@ -6329,8 +6328,6 @@ dependencies = [ "tower 0.5.2", "tower-http", "tracing", - "tracing-log 0.1.4", - "tracing-subscriber", "url", ] diff --git a/crates/cli/src/full.rs b/crates/cli/src/full.rs index 5cdd74cfe..3e0409ee6 100644 --- a/crates/cli/src/full.rs +++ b/crates/cli/src/full.rs @@ -2,10 +2,10 @@ use std::path::PathBuf; use anyhow::{Context, Result}; pub use clap::Parser; -use katana_node::config::db::DbConfig; use katana_node::config::metrics::MetricsConfig; use katana_node::config::rpc::RpcConfig; use katana_node::full; +use katana_node::{config::db::DbConfig, full::Network}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -28,6 +28,13 @@ pub struct FullNodeArgs { #[arg(value_name = "PATH")] pub db_dir: PathBuf, + #[arg(long = "eth.rpc")] + #[arg(value_name = "PATH")] + pub eth_rpc_url: String, + + #[arg(long)] + pub network: Network, + /// Gateway API key for accessing the sequencer gateway. #[arg(long)] #[arg(value_name = "KEY")] @@ -92,7 +99,14 @@ impl FullNodeArgs { let rpc = self.rpc_config()?; let metrics = self.metrics_config(); - Ok(full::Config { db, rpc, metrics, gateway_api_key: self.gateway_api_key.clone() }) + Ok(full::Config { + db, + rpc, + metrics, + network: self.network, + eth_rpc_url: self.eth_rpc_url.clone(), + gateway_api_key: self.gateway_api_key.clone(), + }) } fn db_config(&self) -> DbConfig { diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index b4750e045..90a98c9c6 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -44,10 +44,7 @@ url.workspace = true strum.workspace = true strum_macros.workspace = true -clap.workspace = true -tokio = { workspace = true, features = [ "time" ] } -tracing-log.workspace = true -tracing-subscriber.workspace = true +tokio={ workspace = true, features = ["time"], default-features = false } katana-starknet.workspace = true alloy-provider = { workspace = true, default-features = false, features = [ "reqwest", "reqwest-rustls-tls" ] } diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index 55c0b198d..f23c0ef01 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -42,8 +42,19 @@ use tip_watcher::ChainTipWatcher; use crate::config::rpc::{RpcConfig, RpcModuleKind}; use crate::full::pool::{FullNodePool, GatewayProxyValidator}; -#[derive(Debug, Clone)] +#[derive( + Debug, + Copy, + Clone, + serde::Serialize, + serde::Deserialize, + PartialEq, + Default, + strum::Display, + strum::EnumString, +)] pub enum Network { + #[default] Mainnet, Sepolia, } @@ -106,8 +117,8 @@ impl Node { // --- build pipeline - let (mut pipeline, _) = Pipeline::new(provider.clone(), 64); - let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 3); + let (mut pipeline, _) = Pipeline::new(provider.clone(), 100); + let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 10); pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); pipeline.add_stage(Classes::new(provider.clone(), gateway_client.clone(), 3)); // pipeline.add_stage(StateTrie::new(provider.clone())); diff --git a/crates/node/src/full/tip_watcher.rs b/crates/node/src/full/tip_watcher.rs index 345fe4b41..2b11a7eb2 100644 --- a/crates/node/src/full/tip_watcher.rs +++ b/crates/node/src/full/tip_watcher.rs @@ -42,7 +42,7 @@ impl ChainTipWatcher

{ pub async fn run(&self) -> Result<()> { let interval_in_secs = self.watch_interval.as_secs(); - info!(target: "node", interval = %interval_in_secs, "Chain tip watcher started."); + info!(interval = %interval_in_secs, "Chain tip watcher started."); let mut prev_tip: BlockNumber = 0; diff --git a/crates/sync/pipeline/src/lib.rs b/crates/sync/pipeline/src/lib.rs index f03f0416b..8fc5bccf5 100644 --- a/crates/sync/pipeline/src/lib.rs +++ b/crates/sync/pipeline/src/lib.rs @@ -75,7 +75,6 @@ //! [Erigon]: https://github.com/erigontech/erigon use core::future::IntoFuture; -use std::sync::atomic::{AtomicU64, Ordering}; use futures::future::BoxFuture; use katana_primitives::block::BlockNumber; @@ -344,6 +343,8 @@ impl Pipeline

{ /// Returns an error if any stage execution fails or if the pipeline fails to read the /// checkpoint. pub async fn run_once(&mut self, to: BlockNumber) -> PipelineResult { + let tip = self.tip.expect("qed; should exist by now"); + if self.stages.is_empty() { return Ok(to); } diff --git a/crates/sync/stage/src/blocks/downloader.rs b/crates/sync/stage/src/blocks/downloader.rs index a8e3e8c52..3677c5b8d 100644 --- a/crates/sync/stage/src/blocks/downloader.rs +++ b/crates/sync/stage/src/blocks/downloader.rs @@ -16,7 +16,6 @@ use anyhow::Result; use katana_gateway_client::Client as GatewayClient; use katana_gateway_types::StateUpdateWithBlock; use katana_primitives::block::BlockNumber; -use tracing::{info_span, Instrument}; use crate::downloader::{BatchDownloader, Downloader}; @@ -87,12 +86,9 @@ where to: BlockNumber, ) -> impl Future, katana_gateway_client::Error>> + Send { - async move { - // convert the range to a list of block keys - let block_keys = (from..=to).collect::>(); - self.inner.download(block_keys).await - } - .instrument(info_span!("download_blocks", %from, %to)) + // convert the range to a list of block keys + let block_keys = (from..=to).collect::>(); + self.inner.download(block_keys) } } @@ -129,7 +125,6 @@ mod impls { &self, key: &Self::Key, ) -> impl Future> { - trace!(block = %key, "Downloading block."); async { match self.gateway.get_state_update_with_block((*key).into()).await.inspect_err( |error| error!(block = %*key, ?error, "Error downloading block from gateway."), diff --git a/crates/sync/stage/src/blocks/mod.rs b/crates/sync/stage/src/blocks/mod.rs index 5cc3de551..b72f7164e 100644 --- a/crates/sync/stage/src/blocks/mod.rs +++ b/crates/sync/stage/src/blocks/mod.rs @@ -106,8 +106,7 @@ where .download_blocks(input.from(), input.to()) .instrument(info_span!(target: "stage", "blocks.download", from = %input.from(), to = %input.to())) .await - .map_err(Error::Gateway) - .inspect_err(|e| error!(error = %e , "Error downloading blocks."))?; + .map_err(Error::Gateway)?; let span = info_span!(target: "stage", "blocks.insert", from = %input.from(), to = %input.to()); let _enter = span.enter(); diff --git a/crates/sync/stage/src/classes.rs b/crates/sync/stage/src/classes.rs index 6f9e27afa..b10a30aa8 100644 --- a/crates/sync/stage/src/classes.rs +++ b/crates/sync/stage/src/classes.rs @@ -12,7 +12,7 @@ use katana_provider::api::state_update::StateUpdateProvider; use katana_provider::api::ProviderError; use katana_rpc_types::class::ConversionError; use rayon::prelude::*; -use tracing::{debug, error}; +use tracing::{debug, error, info, info_span, Instrument}; use super::{Stage, StageExecutionInput, StageExecutionOutput, StageResult}; use crate::downloader::{BatchDownloader, Downloader, DownloaderResult}; @@ -131,10 +131,13 @@ where let declared_class_hashes = self.get_declared_classes(input.from(), input.to())?; if !declared_class_hashes.is_empty() { + let total_classes = declared_classes.len(); + // fetch the classes artifacts let class_artifacts = self .downloader .download(declared_class_hashes.clone()) + .instrument(info_span!(target: "stage", "classes.download", %total_classes)) .await .map_err(Error::Gateway)?; @@ -148,6 +151,7 @@ where for (key, class) in declared_class_hashes.iter().zip(verified_classes.into_iter()) { self.provider.set_class(key.class_hash, class)?; } + } else { } Ok(StageExecutionOutput { last_block_processed: input.to() }) diff --git a/crates/sync/stage/tests/trie.rs b/crates/sync/stage/tests/trie.rs index f2a238247..ca32d05c5 100644 --- a/crates/sync/stage/tests/trie.rs +++ b/crates/sync/stage/tests/trie.rs @@ -102,6 +102,13 @@ impl StateUpdateProvider for MockProvider { Ok(None) } + fn declared_deprecated_classes( + &self, + block_id: BlockHashOrNumber, + ) -> ProviderResult>> { + Ok(None) + } + fn deployed_contracts( &self, _block_id: BlockHashOrNumber, diff --git a/crates/tracing/src/lib.rs b/crates/tracing/src/lib.rs index 3ce83f193..e34aea330 100644 --- a/crates/tracing/src/lib.rs +++ b/crates/tracing/src/lib.rs @@ -47,7 +47,7 @@ pub async fn init(format: LogFormat, telemetry_config: Option) -> const DEFAULT_LOG_FILTER: &str = "katana_db::mdbx=trace,cairo_native::compiler=off,pipeline=debug,stage=debug,tasks=debug,\ executor=trace,forking::backend=trace,blockifier=off,jsonrpsee_server=off,hyper=off,\ - messaging=debug,node=error,explorer=info,rpc=trace,pool=trace,info"; + messaging=debug,node=error,explorer=info,rpc=trace,pool=trace,katana_stage::downloader=trace,info"; let default_filter = EnvFilter::try_new(DEFAULT_LOG_FILTER); let filter = EnvFilter::try_from_default_env().or(default_filter)?; From 46479555617751939cece3826fc8479cb589b8ba Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 13 Oct 2025 20:47:56 -0400 Subject: [PATCH 11/49] wip --- crates/sync/stage/src/classes.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/sync/stage/src/classes.rs b/crates/sync/stage/src/classes.rs index b10a30aa8..2d9b3f64a 100644 --- a/crates/sync/stage/src/classes.rs +++ b/crates/sync/stage/src/classes.rs @@ -130,7 +130,9 @@ where Box::pin(async move { let declared_class_hashes = self.get_declared_classes(input.from(), input.to())?; - if !declared_class_hashes.is_empty() { + if declared_class_hashes.is_empty() { + debug!("No classes declared within the block range", from = %input.from(), to = %input.to()); + } else { let total_classes = declared_classes.len(); // fetch the classes artifacts @@ -151,7 +153,6 @@ where for (key, class) in declared_class_hashes.iter().zip(verified_classes.into_iter()) { self.provider.set_class(key.class_hash, class)?; } - } else { } Ok(StageExecutionOutput { last_block_processed: input.to() }) From db94098426bc464a7d03c971f9c1378720af9e8c Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 15 Oct 2025 15:39:56 -0400 Subject: [PATCH 12/49] wip --- crates/node/src/full/mod.rs | 4 ++-- crates/sync/stage/src/classes.rs | 2 +- crates/sync/stage/src/trie.rs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index f23c0ef01..d76d34bda 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -117,11 +117,11 @@ impl Node { // --- build pipeline - let (mut pipeline, _) = Pipeline::new(provider.clone(), 100); + let (mut pipeline, _) = Pipeline::new(provider.clone(), 10); let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 10); pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); pipeline.add_stage(Classes::new(provider.clone(), gateway_client.clone(), 3)); - // pipeline.add_stage(StateTrie::new(provider.clone())); + pipeline.add_stage(StateTrie::new(provider.clone())); // --- build rpc server diff --git a/crates/sync/stage/src/classes.rs b/crates/sync/stage/src/classes.rs index 2d9b3f64a..b299e5e59 100644 --- a/crates/sync/stage/src/classes.rs +++ b/crates/sync/stage/src/classes.rs @@ -131,7 +131,7 @@ where let declared_class_hashes = self.get_declared_classes(input.from(), input.to())?; if declared_class_hashes.is_empty() { - debug!("No classes declared within the block range", from = %input.from(), to = %input.to()); + debug!(from = %input.from(), to = %input.to(), "No classes declared within the block range"); } else { let total_classes = declared_classes.len(); diff --git a/crates/sync/stage/src/trie.rs b/crates/sync/stage/src/trie.rs index 95a24cd36..19a6e7ed0 100644 --- a/crates/sync/stage/src/trie.rs +++ b/crates/sync/stage/src/trie.rs @@ -4,6 +4,7 @@ use katana_primitives::Felt; use katana_provider::api::block::HeaderProvider; use katana_provider::api::state_update::StateUpdateProvider; use katana_provider::api::trie::TrieWriter; +use katana_rpc_types::class; use starknet::macros::short_string; use starknet_types_core::hash::{Poseidon, StarkHash}; use tracing::{debug, debug_span, error}; From 32038c63c65d01205f854af5a4f76fc37531f210 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 15 Oct 2025 20:06:24 -0400 Subject: [PATCH 13/49] wip --- crates/sync/stage/src/classes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sync/stage/src/classes.rs b/crates/sync/stage/src/classes.rs index b299e5e59..88b4ed147 100644 --- a/crates/sync/stage/src/classes.rs +++ b/crates/sync/stage/src/classes.rs @@ -133,7 +133,7 @@ where if declared_class_hashes.is_empty() { debug!(from = %input.from(), to = %input.to(), "No classes declared within the block range"); } else { - let total_classes = declared_classes.len(); + let total_classes = declared_class_hashes.len(); // fetch the classes artifacts let class_artifacts = self From 3fe258158c51eb827d0a480853301605bbde941a Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Thu, 16 Oct 2025 15:25:56 -0400 Subject: [PATCH 14/49] wip --- crates/storage/provider/provider/src/providers/fork/state.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/storage/provider/provider/src/providers/fork/state.rs b/crates/storage/provider/provider/src/providers/fork/state.rs index a8db588ea..f59fca91f 100644 --- a/crates/storage/provider/provider/src/providers/fork/state.rs +++ b/crates/storage/provider/provider/src/providers/fork/state.rs @@ -2,7 +2,9 @@ use std::cmp::Ordering; use std::sync::Arc; use katana_db::abstraction::{Database, DbTx, DbTxMut}; -use katana_db::models::contract::{ContractClassChange, ContractNonceChange}; +use katana_db::models::contract::{ + ContractClassChange, ContractClassChangeType, ContractNonceChange, +}; use katana_db::models::storage::{ContractStorageEntry, ContractStorageKey, StorageEntry}; use katana_db::tables; use katana_fork::BackendClient; From fb3bdeda056258eea11365c7f1b5389a2477f0c6 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 20 Oct 2025 18:07:50 -0400 Subject: [PATCH 15/49] wip --- crates/rpc/rpc/src/starknet/mod.rs | 3 +++ crates/storage/provider/provider-api/src/pending.rs | 12 +++--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index 701e05b43..0e3273bd6 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -19,6 +19,7 @@ use katana_primitives::Felt; use katana_provider::api::block::{BlockHashProvider, BlockIdReader, BlockNumberProvider}; use katana_provider::api::contract::ContractClassProvider; use katana_provider::api::env::BlockEnvProvider; +use katana_provider::api::pending::PendingBlockProvider; use katana_provider::api::state::{StateFactoryProvider, StateProvider, StateRootProvider}; use katana_provider::api::transaction::{ ReceiptProvider, TransactionProvider, TransactionStatusProvider, TransactionsProviderExt, @@ -93,6 +94,8 @@ where { pool: Pool, chain_spec: Arc, + pool: P, + pending_provider: Arc, storage: BlockchainProvider>, forked_client: Option, task_spawner: TaskSpawner, diff --git a/crates/storage/provider/provider-api/src/pending.rs b/crates/storage/provider/provider-api/src/pending.rs index e16398fc5..4e8f9d4fc 100644 --- a/crates/storage/provider/provider-api/src/pending.rs +++ b/crates/storage/provider/provider-api/src/pending.rs @@ -1,27 +1,21 @@ use katana_primitives::block::PartialHeader; use katana_primitives::env::BlockEnv; use katana_primitives::receipt::Receipt; +use katana_primitives::state::StateUpdates; use katana_primitives::transaction::{TxHash, TxWithHash}; use crate::state::StateProvider; use crate::ProviderResult; #[auto_impl::auto_impl(&, Box, Arc)] -pub trait PendingDataProvider: Send + Sync { - fn state(&self) -> ProviderResult>>; - - // returns block header, transactions, and receipts +pub trait PendingBlockProvider: Send + Sync { fn block_header(&self) -> ProviderResult>; fn block_env(&self) -> ProviderResult>; - fn block_transaction_count(&self) -> ProviderResult>; - - fn transaction_by_block_id_and_index(&self) -> ProviderResult>; - fn transaction(&self, hash: TxHash) -> ProviderResult>; fn receipt(&self, hash: TxHash) -> ProviderResult>; - fn state_update(&self) -> ProviderResult>; + fn state_update(&self) -> ProviderResult>; } From 3fd9101e428efe37d998aaeca281fc89e299a79f Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 21 Oct 2025 11:33:37 -0400 Subject: [PATCH 16/49] wip --- crates/cli/src/full.rs | 3 +- crates/gateway/gateway-server/src/handlers.rs | 1 - crates/gateway/gateway-server/src/lib.rs | 1 - crates/node/src/full/mod.rs | 1 + crates/node/src/full/pending.rs | 101 ++++++++++++++++++ crates/rpc/rpc/src/starknet/blockifier.rs | 7 ++ .../provider/provider-api/src/pending.rs | 4 +- crates/tracing/src/lib.rs | 9 +- 8 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 crates/node/src/full/pending.rs diff --git a/crates/cli/src/full.rs b/crates/cli/src/full.rs index 3e0409ee6..1fb1ddcd3 100644 --- a/crates/cli/src/full.rs +++ b/crates/cli/src/full.rs @@ -2,10 +2,11 @@ use std::path::PathBuf; use anyhow::{Context, Result}; pub use clap::Parser; +use katana_node::config::db::DbConfig; use katana_node::config::metrics::MetricsConfig; use katana_node::config::rpc::RpcConfig; use katana_node::full; -use katana_node::{config::db::DbConfig, full::Network}; +use katana_node::full::Network; use serde::{Deserialize, Serialize}; use tracing::info; diff --git a/crates/gateway/gateway-server/src/handlers.rs b/crates/gateway/gateway-server/src/handlers.rs index 640639cea..888ba74a5 100644 --- a/crates/gateway/gateway-server/src/handlers.rs +++ b/crates/gateway/gateway-server/src/handlers.rs @@ -2,7 +2,6 @@ use axum::extract::{Query, State}; use axum::http::StatusCode; use axum::response::{IntoResponse, Json, Response}; use katana_core::service::block_producer::BlockProducer; -use katana_executor::implementation::blockifier::BlockifierFactory; use katana_gateway_types::{ Block, ConfirmedReceipt, ConfirmedTransaction, ContractClass, ErrorCode, GatewayError, ReceiptBody, StateUpdate, StateUpdateWithBlock, diff --git a/crates/gateway/gateway-server/src/lib.rs b/crates/gateway/gateway-server/src/lib.rs index 00862617f..1f8d7cd37 100644 --- a/crates/gateway/gateway-server/src/lib.rs +++ b/crates/gateway/gateway-server/src/lib.rs @@ -5,7 +5,6 @@ use std::time::Duration; use axum::routing::get; use axum::Router; use katana_core::service::block_producer::BlockProducer; -use katana_executor::implementation::blockifier::BlockifierFactory; use katana_pool_api::TransactionPool; use katana_rpc::cors::Cors; use katana_rpc::starknet::StarknetApi; diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index d76d34bda..aefbde723 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -36,6 +36,7 @@ use crate::config::metrics::MetricsConfig; mod exit; pub mod tip_watcher; mod pool; +mod pending; use exit::NodeStoppedFuture; use tip_watcher::ChainTipWatcher; diff --git a/crates/node/src/full/pending.rs b/crates/node/src/full/pending.rs new file mode 100644 index 000000000..4b61b2921 --- /dev/null +++ b/crates/node/src/full/pending.rs @@ -0,0 +1,101 @@ +use katana_gateway::types::{ErrorCode, GatewayError}; +use katana_primitives::block::BlockNumber; +use katana_primitives::class::{ClassHash, CompiledClassHash, ContractClass}; +use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; +use katana_primitives::state::StateUpdates; +use katana_primitives::Felt; +use katana_provider::api::contract::ContractClassProvider; +use katana_provider::api::state::{StateProofProvider, StateProvider, StateRootProvider}; +use katana_provider::{ProviderError, ProviderResult}; +use katana_trie::MultiProof; +use tokio::runtime; + +pub struct PendingStateProvider { + base: Box, + pending_block_id: BlockNumber, + pending_state_updates: StateUpdates, + gateway: katana_gateway::client::Client, +} + +impl StateProvider for PendingStateProvider { + fn nonce(&self, address: ContractAddress) -> ProviderResult> { + if let Some(nonce) = self.pending_state_updates.nonce_updates.get(&address) { + return Ok(Some(*nonce)); + } + + self.base.nonce(address) + } + + fn storage( + &self, + address: ContractAddress, + storage_key: StorageKey, + ) -> ProviderResult> { + if let Some(contract_storage) = self.pending_state_updates.storage_updates.get(&address) { + if let Some(value) = contract_storage.get(&storage_key) { + return Ok(Some(*value)); + } + } + + self.base.storage(address, storage_key) + } + + fn class_hash_of_contract( + &self, + address: ContractAddress, + ) -> ProviderResult> { + if let Some(class_hash) = self.pending_state_updates.replaced_classes.get(&address) { + return Ok(Some(*class_hash)); + } + + if let Some(class_hash) = self.pending_state_updates.deployed_contracts.get(&address) { + return Ok(Some(*class_hash)); + } + + self.base.class_hash_of_contract(address) + } +} + +impl ContractClassProvider for PendingStateProvider { + fn class(&self, hash: ClassHash) -> ProviderResult> { + if let Some(class) = self.base.class(hash)? { + return Ok(Some(class)); + } + + let result = runtime::Builder::new_current_thread() + .build() + .unwrap() + .block_on(self.gateway.get_class(hash, block_id)); + + match result { + Ok(class) => { + let class = class.try_into().map_err(|e| ProviderError::Other(e.to_string()))?; + Ok(Some(class)) + } + + Err(error) => { + if let katana_gateway::client::Error::Sequencer(GatewayError { + code: ErrorCode::UndeclaredClass, + .. + }) = error + { + Ok(None) + } else { + Err(ProviderError::Other(error.to_string())) + } + } + } + } + + fn compiled_class_hash_of_class_hash( + &self, + hash: ClassHash, + ) -> ProviderResult> { + if let Some(compiled_hash) = self.pending_state_updates.declared_classes.get(&hash) { + return Ok(Some(*compiled_hash)); + } + + // Fallback to the base provider + self.base.compiled_class_hash_of_class_hash(hash) + } +} diff --git a/crates/rpc/rpc/src/starknet/blockifier.rs b/crates/rpc/rpc/src/starknet/blockifier.rs index 5fbb44bc0..dcbf6c67b 100644 --- a/crates/rpc/rpc/src/starknet/blockifier.rs +++ b/crates/rpc/rpc/src/starknet/blockifier.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use katana_chain_spec::ChainSpec; +use katana_executor::implementation::blockifier::blockifier::context::BlockContext; use katana_executor::implementation::blockifier::cache::ClassCache; use katana_executor::implementation::blockifier::call::execute_call; use katana_executor::implementation::blockifier::state::CachedState; @@ -15,6 +16,12 @@ use katana_rpc_types::{FeeEstimate, FunctionCall}; use crate::starknet::StarknetApiResult; +pub struct ExecutionCtx { + state: Box, + block_context: Arc, + simulation_flags: ExecutionFlags, +} + #[tracing::instrument(level = "trace", target = "rpc", skip_all, fields(total_txs = transactions.len()))] pub fn simulate( chain_spec: &ChainSpec, diff --git a/crates/storage/provider/provider-api/src/pending.rs b/crates/storage/provider/provider-api/src/pending.rs index 4e8f9d4fc..bc7cd3e42 100644 --- a/crates/storage/provider/provider-api/src/pending.rs +++ b/crates/storage/provider/provider-api/src/pending.rs @@ -13,9 +13,7 @@ pub trait PendingBlockProvider: Send + Sync { fn block_env(&self) -> ProviderResult>; - fn transaction(&self, hash: TxHash) -> ProviderResult>; - - fn receipt(&self, hash: TxHash) -> ProviderResult>; + fn transactions(&self) -> ProviderResult)>>; fn state_update(&self) -> ProviderResult>; } diff --git a/crates/tracing/src/lib.rs b/crates/tracing/src/lib.rs index e34aea330..a8505a880 100644 --- a/crates/tracing/src/lib.rs +++ b/crates/tracing/src/lib.rs @@ -44,10 +44,11 @@ pub enum Error { } pub async fn init(format: LogFormat, telemetry_config: Option) -> Result<(), Error> { - const DEFAULT_LOG_FILTER: &str = - "katana_db::mdbx=trace,cairo_native::compiler=off,pipeline=debug,stage=debug,tasks=debug,\ - executor=trace,forking::backend=trace,blockifier=off,jsonrpsee_server=off,hyper=off,\ - messaging=debug,node=error,explorer=info,rpc=trace,pool=trace,katana_stage::downloader=trace,info"; + const DEFAULT_LOG_FILTER: &str = "katana_db::mdbx=trace,cairo_native::compiler=off,\ + pipeline=debug,stage=debug,tasks=debug,executor=trace,\ + forking::backend=trace,blockifier=off,jsonrpsee_server=off,\ + hyper=off,messaging=debug,node=error,explorer=info,\ + rpc=trace,pool=trace,katana_stage::downloader=trace,info"; let default_filter = EnvFilter::try_new(DEFAULT_LOG_FILTER); let filter = EnvFilter::try_from_default_env().or(default_filter)?; From 5a033076bde2cdb2233f1ff5ff1225b91baac78e Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 21 Oct 2025 12:10:04 -0400 Subject: [PATCH 17/49] wip --- crates/node/src/full/pending.rs | 12 +++++++++--- crates/rpc/rpc/src/starknet/mod.rs | 2 +- crates/sync/stage/src/trie.rs | 13 ++++++++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/crates/node/src/full/pending.rs b/crates/node/src/full/pending.rs index 4b61b2921..789899fe4 100644 --- a/crates/node/src/full/pending.rs +++ b/crates/node/src/full/pending.rs @@ -7,7 +7,7 @@ use katana_primitives::Felt; use katana_provider::api::contract::ContractClassProvider; use katana_provider::api::state::{StateProofProvider, StateProvider, StateRootProvider}; use katana_provider::{ProviderError, ProviderResult}; -use katana_trie::MultiProof; +use katana_rpc_types::ConversionError; use tokio::runtime; pub struct PendingStateProvider { @@ -65,11 +65,13 @@ impl ContractClassProvider for PendingStateProvider { let result = runtime::Builder::new_current_thread() .build() .unwrap() - .block_on(self.gateway.get_class(hash, block_id)); + .block_on(self.gateway.get_class(hash, katana_gateway::types::BlockId::Pending)); match result { Ok(class) => { - let class = class.try_into().map_err(|e| ProviderError::Other(e.to_string()))?; + let class = class + .try_into() + .map_err(|e: ConversionError| ProviderError::Other(e.to_string()))?; Ok(Some(class)) } @@ -99,3 +101,7 @@ impl ContractClassProvider for PendingStateProvider { self.base.compiled_class_hash_of_class_hash(hash) } } + +impl StateRootProvider for PendingStateProvider {} + +impl StateProofProvider for PendingStateProvider {} diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index 0e3273bd6..3abee14c7 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -95,7 +95,7 @@ where pool: Pool, chain_spec: Arc, pool: P, - pending_provider: Arc, + // pending_provider: Arc, storage: BlockchainProvider>, forked_client: Option, task_spawner: TaskSpawner, diff --git a/crates/sync/stage/src/trie.rs b/crates/sync/stage/src/trie.rs index 19a6e7ed0..cb9096fe2 100644 --- a/crates/sync/stage/src/trie.rs +++ b/crates/sync/stage/src/trie.rs @@ -45,17 +45,24 @@ where let span = debug_span!("state_trie.compute_state_root", %block_number); let _enter = span.enter(); - let expected_state_root = self + let header = self .provider .header(block_number.into())? - .map(|header| header.state_root) .ok_or(Error::MissingBlockHeader(block_number))?; + dbg!(&header); + + let expected_state_root = header.state_root; + let state_update = self .provider .state_update(block_number.into())? .ok_or(Error::MissingStateUpdate(block_number))?; + if header.number >= 12400 { + println!("{:#?}", state_update); + } + let computed_contract_trie_root = self.provider.trie_insert_contract_updates(block_number, &state_update)?; @@ -73,7 +80,7 @@ where "Computed classes trie root." ); - let computed_state_root = if computed_class_trie_root == Felt::ZERO { + let computed_state_root = if dbg!(computed_class_trie_root == Felt::ZERO) { computed_contract_trie_root } else { Poseidon::hash_array(&[ From 6b41589149c09be66fdecd72e755bf67a7804b31 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 21 Oct 2025 15:05:52 -0400 Subject: [PATCH 18/49] wip --- crates/node/src/full/mod.rs | 4 ++-- crates/sync/stage/src/trie.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index aefbde723..513d6e6fa 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -118,8 +118,8 @@ impl Node { // --- build pipeline - let (mut pipeline, _) = Pipeline::new(provider.clone(), 10); - let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 10); + let (mut pipeline, _) = Pipeline::new(provider.clone(), 50); + let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 50); pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); pipeline.add_stage(Classes::new(provider.clone(), gateway_client.clone(), 3)); pipeline.add_stage(StateTrie::new(provider.clone())); diff --git a/crates/sync/stage/src/trie.rs b/crates/sync/stage/src/trie.rs index cb9096fe2..02e6c2891 100644 --- a/crates/sync/stage/src/trie.rs +++ b/crates/sync/stage/src/trie.rs @@ -50,8 +50,6 @@ where .header(block_number.into())? .ok_or(Error::MissingBlockHeader(block_number))?; - dbg!(&header); - let expected_state_root = header.state_root; let state_update = self @@ -60,6 +58,7 @@ where .ok_or(Error::MissingStateUpdate(block_number))?; if header.number >= 12400 { + dbg!(&header); println!("{:#?}", state_update); } From 65ab1e0df291b97bf889963142c8127d6792b289 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 21 Oct 2025 18:35:40 -0400 Subject: [PATCH 19/49] wip --- Cargo.lock | 1 + crates/node/src/full/mod.rs | 4 ++-- crates/node/src/full/pending.rs | 1 - crates/storage/provider/provider/Cargo.toml | 1 + crates/sync/stage/Cargo.toml | 1 + crates/sync/stage/src/trie.rs | 1 + crates/trie/src/lib.rs | 4 +++- 7 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f3b2a89f..910a27d81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6644,6 +6644,7 @@ dependencies = [ "katana-provider", "katana-rpc-types", "katana-tasks", + "katana-trie", "num-traits", "rayon", "rstest 0.18.2", diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index 513d6e6fa..fac8e9236 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -119,9 +119,9 @@ impl Node { // --- build pipeline let (mut pipeline, _) = Pipeline::new(provider.clone(), 50); - let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 50); + let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 8); pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); - pipeline.add_stage(Classes::new(provider.clone(), gateway_client.clone(), 3)); + pipeline.add_stage(Classes::new(provider.clone(), gateway_client.clone(), 8)); pipeline.add_stage(StateTrie::new(provider.clone())); // --- build rpc server diff --git a/crates/node/src/full/pending.rs b/crates/node/src/full/pending.rs index 789899fe4..156aeaed4 100644 --- a/crates/node/src/full/pending.rs +++ b/crates/node/src/full/pending.rs @@ -103,5 +103,4 @@ impl ContractClassProvider for PendingStateProvider { } impl StateRootProvider for PendingStateProvider {} - impl StateProofProvider for PendingStateProvider {} diff --git a/crates/storage/provider/provider/Cargo.toml b/crates/storage/provider/provider/Cargo.toml index 55a064eb4..f148826bc 100644 --- a/crates/storage/provider/provider/Cargo.toml +++ b/crates/storage/provider/provider/Cargo.toml @@ -19,6 +19,7 @@ katana-trie.workspace = true anyhow.workspace = true auto_impl.workspace = true bitvec.workspace = true +lazy_static.workspace = true parking_lot.workspace = true starknet.workspace = true starknet-types-core.workspace = true diff --git a/crates/sync/stage/Cargo.toml b/crates/sync/stage/Cargo.toml index 1e952ae28..0a79c34a3 100644 --- a/crates/sync/stage/Cargo.toml +++ b/crates/sync/stage/Cargo.toml @@ -12,6 +12,7 @@ katana-gateway-client.workspace = true katana-gateway-types.workspace = true katana-messaging.workspace = true katana-pool.workspace = true +katana-trie.workspace = true katana-primitives.workspace = true katana-provider.workspace = true katana-rpc-types.workspace = true diff --git a/crates/sync/stage/src/trie.rs b/crates/sync/stage/src/trie.rs index 02e6c2891..b642cb5af 100644 --- a/crates/sync/stage/src/trie.rs +++ b/crates/sync/stage/src/trie.rs @@ -5,6 +5,7 @@ use katana_provider::api::block::HeaderProvider; use katana_provider::api::state_update::StateUpdateProvider; use katana_provider::api::trie::TrieWriter; use katana_rpc_types::class; +use katana_trie::CommitId; use starknet::macros::short_string; use starknet_types_core::hash::{Poseidon, StarkHash}; use tracing::{debug, debug_span, error}; diff --git a/crates/trie/src/lib.rs b/crates/trie/src/lib.rs index bd2a183ba..4b357cb02 100644 --- a/crates/trie/src/lib.rs +++ b/crates/trie/src/lib.rs @@ -1,7 +1,9 @@ use bitvec::view::AsBits; pub use bonsai::{BitVec, MultiProof, Path, ProofNode}; use bonsai_trie::BonsaiStorage; -pub use bonsai_trie::{BonsaiDatabase, BonsaiPersistentDatabase, BonsaiStorageConfig}; +pub use bonsai_trie::{ + databases::HashMapDb, BonsaiDatabase, BonsaiPersistentDatabase, BonsaiStorageConfig, +}; use katana_primitives::class::ClassHash; use katana_primitives::Felt; use starknet_types_core::hash::{Pedersen, StarkHash}; From 45acb8ad2b2d0ae5fde17a26764499ec71ec1a40 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 22 Oct 2025 14:13:31 -0400 Subject: [PATCH 20/49] wip --- crates/gateway/gateway-client/src/lib.rs | 11 +++++++++++ .../provider/provider/src/providers/db/trie.rs | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/gateway/gateway-client/src/lib.rs b/crates/gateway/gateway-client/src/lib.rs index fd896faa3..9c42bb98d 100644 --- a/crates/gateway/gateway-client/src/lib.rs +++ b/crates/gateway/gateway-client/src/lib.rs @@ -201,6 +201,17 @@ impl Client { } } +// unknown format: +// +// +// 502 Server Error +// +// +//

Error: Server Error

+//

The server encountered a temporary error and could not complete your request.

Please try again in 30 seconds.

+//

+// + #[derive(Debug, thiserror::Error)] pub enum Error { #[error(transparent)] diff --git a/crates/storage/provider/provider/src/providers/db/trie.rs b/crates/storage/provider/provider/src/providers/db/trie.rs index f96465c36..ad9c70a82 100644 --- a/crates/storage/provider/provider/src/providers/db/trie.rs +++ b/crates/storage/provider/provider/src/providers/db/trie.rs @@ -13,6 +13,7 @@ use katana_provider_api::ProviderError; use katana_trie::{ compute_contract_state_hash, ClassesTrie, ContractLeaf, ContractsTrie, StoragesTrie, }; +use starknet::providers::Provider; use crate::providers::db::DbProvider; use crate::ProviderResult; @@ -114,6 +115,7 @@ fn contract_state_leaf_hash( provider: impl StateProvider, address: &ContractAddress, contract_leaf: &ContractLeaf, + block_number: BlockNumber, ) -> Felt { let nonce = contract_leaf.nonce.unwrap_or(provider.nonce(*address).unwrap().unwrap_or_default()); @@ -124,5 +126,18 @@ fn contract_state_leaf_hash( let storage_root = contract_leaf.storage_root.expect("root need to set"); - compute_contract_state_hash(&class_hash, &storage_root, &nonce) + let root = compute_contract_state_hash(&class_hash, &storage_root, &nonce); + + if block_number >= 6481 { + println!("----------------------------------------"); + println!("block {block_number}"); + println!("address: {address}"); + println!("class hash : {class_hash:#x}"); + println!("nonce : {nonce:#x}"); + println!("storage root : {storage_root:#x}"); + println!("Contract state hash: {root}"); + println!("----------------------------------------"); + } + + root } From 81dd17d04f19628aad4cd60251b92d3bc62db802 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 22 Oct 2025 17:12:00 -0400 Subject: [PATCH 21/49] wip --- crates/gateway/gateway-client/src/lib.rs | 4 ++-- crates/sync/stage/src/trie.rs | 5 ----- crates/trie/src/lib.rs | 5 ++--- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/gateway/gateway-client/src/lib.rs b/crates/gateway/gateway-client/src/lib.rs index 9c42bb98d..f55a5161b 100644 --- a/crates/gateway/gateway-client/src/lib.rs +++ b/crates/gateway/gateway-client/src/lib.rs @@ -208,8 +208,8 @@ impl Client { // // //

Error: Server Error

-//

The server encountered a temporary error and could not complete your request.

Please try again in 30 seconds.

-//

+//

The server encountered a temporary error and could not complete your request.

Please try +// again in 30 seconds.

// #[derive(Debug, thiserror::Error)] diff --git a/crates/sync/stage/src/trie.rs b/crates/sync/stage/src/trie.rs index b642cb5af..c0459c749 100644 --- a/crates/sync/stage/src/trie.rs +++ b/crates/sync/stage/src/trie.rs @@ -58,11 +58,6 @@ where .state_update(block_number.into())? .ok_or(Error::MissingStateUpdate(block_number))?; - if header.number >= 12400 { - dbg!(&header); - println!("{:#?}", state_update); - } - let computed_contract_trie_root = self.provider.trie_insert_contract_updates(block_number, &state_update)?; diff --git a/crates/trie/src/lib.rs b/crates/trie/src/lib.rs index 4b357cb02..1e49be803 100644 --- a/crates/trie/src/lib.rs +++ b/crates/trie/src/lib.rs @@ -1,9 +1,8 @@ use bitvec::view::AsBits; pub use bonsai::{BitVec, MultiProof, Path, ProofNode}; +pub use bonsai_trie::databases::HashMapDb; use bonsai_trie::BonsaiStorage; -pub use bonsai_trie::{ - databases::HashMapDb, BonsaiDatabase, BonsaiPersistentDatabase, BonsaiStorageConfig, -}; +pub use bonsai_trie::{BonsaiDatabase, BonsaiPersistentDatabase, BonsaiStorageConfig}; use katana_primitives::class::ClassHash; use katana_primitives::Felt; use starknet_types_core::hash::{Pedersen, StarkHash}; From 21bc420e380b76d87b3ad9199d3f0095ff8faa45 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 22 Oct 2025 18:13:49 -0400 Subject: [PATCH 22/49] wip --- .../provider/provider/src/providers/db/trie.rs | 15 +-------------- crates/sync/stage/src/trie.rs | 2 +- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/crates/storage/provider/provider/src/providers/db/trie.rs b/crates/storage/provider/provider/src/providers/db/trie.rs index ad9c70a82..912d510b6 100644 --- a/crates/storage/provider/provider/src/providers/db/trie.rs +++ b/crates/storage/provider/provider/src/providers/db/trie.rs @@ -126,18 +126,5 @@ fn contract_state_leaf_hash( let storage_root = contract_leaf.storage_root.expect("root need to set"); - let root = compute_contract_state_hash(&class_hash, &storage_root, &nonce); - - if block_number >= 6481 { - println!("----------------------------------------"); - println!("block {block_number}"); - println!("address: {address}"); - println!("class hash : {class_hash:#x}"); - println!("nonce : {nonce:#x}"); - println!("storage root : {storage_root:#x}"); - println!("Contract state hash: {root}"); - println!("----------------------------------------"); - } - - root + compute_contract_state_hash(&class_hash, &storage_root, &nonce) } diff --git a/crates/sync/stage/src/trie.rs b/crates/sync/stage/src/trie.rs index c0459c749..ee1f90752 100644 --- a/crates/sync/stage/src/trie.rs +++ b/crates/sync/stage/src/trie.rs @@ -75,7 +75,7 @@ where "Computed classes trie root." ); - let computed_state_root = if dbg!(computed_class_trie_root == Felt::ZERO) { + let computed_state_root = if computed_class_trie_root == Felt::ZERO { computed_contract_trie_root } else { Poseidon::hash_array(&[ From 49e2a18f4802fe427b8169d77db11a2cb3fc0ba8 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Thu, 23 Oct 2025 15:44:29 -0400 Subject: [PATCH 23/49] wip --- crates/sync/pipeline/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/sync/pipeline/src/lib.rs b/crates/sync/pipeline/src/lib.rs index 8fc5bccf5..35507a961 100644 --- a/crates/sync/pipeline/src/lib.rs +++ b/crates/sync/pipeline/src/lib.rs @@ -343,8 +343,6 @@ impl Pipeline

{ /// Returns an error if any stage execution fails or if the pipeline fails to read the /// checkpoint. pub async fn run_once(&mut self, to: BlockNumber) -> PipelineResult { - let tip = self.tip.expect("qed; should exist by now"); - if self.stages.is_empty() { return Ok(to); } From 998edb97800d11430158a7d578aa94eb5d440ed2 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Sat, 25 Oct 2025 16:30:05 -0400 Subject: [PATCH 24/49] wip --- Cargo.lock | 1 + crates/gateway/gateway-types/src/lib.rs | 112 ++++++++++++-- crates/gateway/gateway-types/tests/types.rs | 99 ++++++++++++ crates/node/Cargo.toml | 1 + crates/node/src/full/pending/mod.rs | 143 ++++++++++++++++++ .../src/full/{pending.rs => pending/state.rs} | 18 +-- .../provider/provider-api/src/pending.rs | 3 +- .../provider/src/providers/db/trie.rs | 1 - 8 files changed, 354 insertions(+), 24 deletions(-) create mode 100644 crates/node/src/full/pending/mod.rs rename crates/node/src/full/{pending.rs => pending/state.rs} (88%) diff --git a/Cargo.lock b/Cargo.lock index 910a27d81..9df3f2344 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6318,6 +6318,7 @@ dependencies = [ "katana-starknet", "katana-tasks", "katana-tracing", + "parking_lot", "serde", "serde_json", "strum 0.25.0", diff --git a/crates/gateway/gateway-types/src/lib.rs b/crates/gateway/gateway-types/src/lib.rs index d2d91043a..d35daaaea 100644 --- a/crates/gateway/gateway-types/src/lib.rs +++ b/crates/gateway/gateway-types/src/lib.rs @@ -19,7 +19,7 @@ //! - [`DeployAccountTxV3`]: Uses the custom DA mode and resource bounds //! - [`L1HandlerTx`]: Optional `nonce` field -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use katana_primitives::block::{BlockHash, BlockNumber}; pub use katana_primitives::class::CasmContractClass; @@ -180,17 +180,6 @@ pub struct ConfirmedStateUpdate { pub state_diff: StateDiff, } -// todo(kariy): merge the serialization of gateway into the rpc types -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct StateDiff { - pub storage_diffs: BTreeMap>, - pub deployed_contracts: Vec, - pub old_declared_contracts: Vec, - pub declared_classes: Vec, - pub nonces: BTreeMap, - pub replaced_classes: Vec, -} - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct StorageDiff { pub key: StorageKey, @@ -213,6 +202,105 @@ fn default_l2_gas_price() -> ResourcePrice { ResourcePrice { price_in_fri: Felt::from(1), price_in_wei: Felt::from(1) } } +// todo(kariy): merge the serialization of gateway into the rpc types +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct StateDiff { + pub storage_diffs: BTreeMap>, + pub deployed_contracts: Vec, + pub old_declared_contracts: Vec, + pub declared_classes: Vec, + pub nonces: BTreeMap, + pub replaced_classes: Vec, +} + +impl StateDiff { + /// Returns a new [`StateDiff`] that contains all updates from `self` and `other`, + /// preferring the values from `other` when both diffs touch the same entry. + pub fn merge(mut self, other: StateDiff) -> StateDiff { + let StateDiff { + storage_diffs, + deployed_contracts, + old_declared_contracts, + declared_classes, + nonces, + replaced_classes, + } = other; + + Self::merge_storage_diffs(&mut self.storage_diffs, storage_diffs); + Self::merge_deployed_contracts(&mut self.deployed_contracts, deployed_contracts); + Self::merge_deployed_contracts(&mut self.replaced_classes, replaced_classes); + Self::merge_declared_classes(&mut self.declared_classes, declared_classes); + Self::merge_old_declared_contracts( + &mut self.old_declared_contracts, + old_declared_contracts, + ); + self.nonces.extend(nonces); + + self + } + + fn merge_storage_diffs( + target: &mut BTreeMap>, + updates: BTreeMap>, + ) { + for (address, diffs) in updates { + let entry = target.entry(address).or_default(); + let mut index_by_key: BTreeMap = + entry.iter().enumerate().map(|(idx, diff)| (diff.key, idx)).collect(); + + for diff in diffs { + if let Some(idx) = index_by_key.get(&diff.key).copied() { + entry[idx] = diff; + } else { + index_by_key.insert(diff.key, entry.len()); + entry.push(diff); + } + } + } + } + + fn merge_deployed_contracts( + target: &mut Vec, + incoming: Vec, + ) { + let mut index_by_address: BTreeMap = + target.iter().enumerate().map(|(idx, contract)| (contract.address, idx)).collect(); + + for contract in incoming { + if let Some(idx) = index_by_address.get(&contract.address).copied() { + target[idx] = contract; + } else { + index_by_address.insert(contract.address, target.len()); + target.push(contract); + } + } + } + + fn merge_declared_classes(target: &mut Vec, incoming: Vec) { + let mut index_by_hash: BTreeMap = + target.iter().enumerate().map(|(idx, contract)| (contract.class_hash, idx)).collect(); + + for declared in incoming { + if let Some(idx) = index_by_hash.get(&declared.class_hash).copied() { + target[idx] = declared; + } else { + index_by_hash.insert(declared.class_hash, target.len()); + target.push(declared); + } + } + } + + fn merge_old_declared_contracts(target: &mut Vec, incoming: Vec) { + let mut seen: BTreeSet = target.iter().copied().collect(); + + for class_hash in incoming { + if seen.insert(class_hash) { + target.push(class_hash); + } + } + } +} + impl<'de> Deserialize<'de> for StateUpdate { fn deserialize>(deserializer: D) -> Result { struct __Visitor; diff --git a/crates/gateway/gateway-types/tests/types.rs b/crates/gateway/gateway-types/tests/types.rs index bab464518..8f5194104 100644 --- a/crates/gateway/gateway-types/tests/types.rs +++ b/crates/gateway/gateway-types/tests/types.rs @@ -95,6 +95,105 @@ fn state_diff_empty_conversion() { assert!(state_updates.replaced_classes.is_empty()); } +#[test] +fn state_diff_merge_merges_entries() { + let contract_a = address!("0x1"); + let contract_b = address!("0x2"); + + let mut base_storage = BTreeMap::new(); + base_storage.insert( + contract_a, + vec![ + StorageDiff { key: felt!("0x10"), value: felt!("0x100") }, + StorageDiff { key: felt!("0x11"), value: felt!("0x101") }, + ], + ); + + let deployed_a = DeployedContract { address: address!("0x300"), class_hash: felt!("0xaaa") }; + let base = StateDiff { + storage_diffs: base_storage, + deployed_contracts: vec![deployed_a.clone()], + old_declared_contracts: vec![felt!("0x400")], + declared_classes: vec![DeclaredContract { + class_hash: felt!("0x500"), + compiled_class_hash: felt!("0x501"), + }], + nonces: BTreeMap::from([(contract_a, felt!("0x1"))]), + replaced_classes: vec![DeployedContract { + address: address!("0x350"), + class_hash: felt!("0x900"), + }], + }; + + let mut other_storage = BTreeMap::new(); + other_storage.insert( + contract_a, + vec![ + StorageDiff { key: felt!("0x11"), value: felt!("0x202") }, + StorageDiff { key: felt!("0x12"), value: felt!("0x203") }, + ], + ); + other_storage + .insert(contract_b, vec![StorageDiff { key: felt!("0x20"), value: felt!("0x204") }]); + + let other = StateDiff { + storage_diffs: other_storage, + deployed_contracts: vec![ + DeployedContract { address: deployed_a.address, class_hash: felt!("0xbbb") }, + DeployedContract { address: address!("0x301"), class_hash: felt!("0xccc") }, + ], + old_declared_contracts: vec![felt!("0x400"), felt!("0x401")], + declared_classes: vec![ + DeclaredContract { class_hash: felt!("0x500"), compiled_class_hash: felt!("0x999") }, + DeclaredContract { class_hash: felt!("0x502"), compiled_class_hash: felt!("0x503") }, + ], + nonces: BTreeMap::from([(contract_a, felt!("0x5")), (contract_b, felt!("0x6"))]), + replaced_classes: vec![ + DeployedContract { address: address!("0x350"), class_hash: felt!("0x901") }, + DeployedContract { address: address!("0x351"), class_hash: felt!("0x902") }, + ], + }; + + let merged = base.merge(other); + + // storage diff merge + let merged_storage_a = merged.storage_diffs.get(&contract_a).expect("storage for contract A"); + assert_eq!(merged_storage_a.len(), 3); + assert_eq!(merged_storage_a[1].value, felt!("0x202")); + assert_eq!(merged_storage_a[2].key, felt!("0x12")); + assert_eq!( + merged.storage_diffs.get(&contract_b).expect("storage for contract B")[0].value, + felt!("0x204") + ); + + // deployed contracts updated + let deployed_by_addr: BTreeMap<_, _> = + merged.deployed_contracts.iter().map(|c| (c.address, c.class_hash)).collect(); + assert_eq!(deployed_by_addr.get(&deployed_a.address), Some(&felt!("0xbbb"))); + assert_eq!(deployed_by_addr.get(&address!("0x301")), Some(&felt!("0xccc"))); + + // replaced contracts updated + let replaced_by_addr: BTreeMap<_, _> = + merged.replaced_classes.iter().map(|c| (c.address, c.class_hash)).collect(); + assert_eq!(replaced_by_addr.get(&address!("0x350")), Some(&felt!("0x901"))); + assert_eq!(replaced_by_addr.get(&address!("0x351")), Some(&felt!("0x902"))); + + // declared classes merged + let declared_by_hash: BTreeMap<_, _> = + merged.declared_classes.iter().map(|c| (c.class_hash, c.compiled_class_hash)).collect(); + assert_eq!(declared_by_hash.get(&felt!("0x500")), Some(&felt!("0x999"))); + assert_eq!(declared_by_hash.get(&felt!("0x502")), Some(&felt!("0x503"))); + + // deprecated declared classes deduplicated + assert!(merged.old_declared_contracts.contains(&felt!("0x400"))); + assert!(merged.old_declared_contracts.contains(&felt!("0x401"))); + assert_eq!(merged.old_declared_contracts.iter().filter(|&&h| h == felt!("0x400")).count(), 1); + + // nonces override and extend + assert_eq!(merged.nonces.get(&contract_a), Some(&felt!("0x5"))); + assert_eq!(merged.nonces.get(&contract_b), Some(&felt!("0x6"))); +} + #[test] fn receipt_serde_succeeded() { let json = json!({ diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 90a98c9c6..2d86d6b76 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -34,6 +34,7 @@ http.workspace = true jsonrpsee.workspace = true serde.workspace = true serde_json.workspace = true +parking_lot.workspace = true thiserror.workspace = true toml.workspace = true tower = { workspace = true, features = [ "full" ] } diff --git a/crates/node/src/full/pending/mod.rs b/crates/node/src/full/pending/mod.rs new file mode 100644 index 000000000..56ef6bc6a --- /dev/null +++ b/crates/node/src/full/pending/mod.rs @@ -0,0 +1,143 @@ +use std::sync::Arc; +use std::time::Duration; + +use katana_gateway::client::Client; +use katana_gateway::types::{ + ConfirmedTransaction, ErrorCode, GatewayError, PreConfirmedBlock, StateDiff, + StateUpdateWithBlock, +}; +use katana_primitives::block::{BlockHash, BlockNumber}; +use katana_primitives::state::StateUpdates; +use katana_provider::api::state::StateFactoryProvider; +use parking_lot::Mutex; +use tokio::sync::watch; +use tracing::error; + +use crate::full::pending::state::PreconfStateProvider; +use crate::full::tip_watcher::TipSubscription; + +pub mod state; + +const DEFAULT_INTERVAL: Duration = Duration::from_millis(500); + +pub struct PreconfBlockWatcher { + interval: Duration, + gateway_client: Client, + + // from pipeline + latest_synced_block: watch::Receiver<(BlockNumber, BlockHash)>, + // from tip watcher (actual tip of the chain) + latest_block: TipSubscription, + + // shared state + pending_block_id: Arc>, + pending_state_updates: Arc>, +} + +impl PreconfBlockWatcher { + pub fn new(gateway_client: Client, tip_subscription: TipSubscription) -> Self { + todo!() + } + + pub async fn run(&mut self) { + let mut current_preconf_block_num = 0; + + loop { + let latest_chain_tip = self.latest_block.tip(); + let latest_synced_block_num = *self.latest_synced_block.borrow(); + + if latest_synced_block_num >= latest_chain_tip { + let preconf_block_num = latest_synced_block_num + 1; + + match self.gateway_client.get_preconfirmed_block(preconf_block_num).await { + Ok(preconf_block) => { + let preconf_state_diff: StateUpdates = preconf_block + .transaction_state_diffs + .into_iter() + .fold(StateDiff::default(), |acc, diff| { + diff.and_then(|diff| acc.merge(diff)) + }) + .into(); + + // update shared state + *self.pending_block_id.lock() = current_preconf_block_num; + *self.pending_state_updates.lock() = preconf_state_diff; + + // increment to get the next preconf block number + current_preconf_block_num = preconf_block_num + 1; + } + + // this could either be because the latest block is still not synced to the + // chain's tip, in which case we just skip to the next + // iteration. + Err(katana_gateway::client::Error::Sequencer(error)) + if error.code == ErrorCode::BlockNotFound => + { + continue + } + + Err(err) => panic!("{err}"), + } + } + + tokio::select! { + biased; + + res = self.latest_synced_block.changed() => { + if let Err(err) = res { + error!(error = ?err, "Error receiving latest block number."); + break; + } + } + + _ = tokio::time::sleep(self.interval) => {} + } + } + } +} + +pub struct PreconfStateFactory { + // from pipeline + latest_synced_block: watch::Receiver, + gateway_client: Client, + provider: P, + + // shared state + preconf_block_id: Arc>, + preconf_block: Arc>, + preconf_state_updates: Arc>, +} + +impl PreconfStateFactory

{ + pub fn new( + state_factory_provider: P, + gateway_client: Client, + chain_tip_subscription: TipSubscription, + ) -> Self { + todo!() + } + + pub fn state(&self) -> PreconfStateProvider { + let latest_block_num = *self.latest_synced_block.borrow(); + let latest_state = self.provider.historical(latest_block_num.into()).unwrap().unwrap(); + + PreconfStateProvider { + base: latest_state, + gateway: self.gateway_client.clone(), + pending_block_id: self.preconf_block_id.lock().clone(), + pending_state_updates: self.preconf_state_updates.lock().clone(), + } + } + + pub fn state_updates(&self) -> StateUpdates { + self.preconf_state_updates.lock().clone() + } + + pub fn block(&self) -> PreConfirmedBlock { + self.preconf_block.lock().clone() + } + + pub fn transactions(&self) -> Vec { + self.preconf_block.lock().transactions.clone() + } +} diff --git a/crates/node/src/full/pending.rs b/crates/node/src/full/pending/state.rs similarity index 88% rename from crates/node/src/full/pending.rs rename to crates/node/src/full/pending/state.rs index 156aeaed4..18370c880 100644 --- a/crates/node/src/full/pending.rs +++ b/crates/node/src/full/pending/state.rs @@ -10,14 +10,14 @@ use katana_provider::{ProviderError, ProviderResult}; use katana_rpc_types::ConversionError; use tokio::runtime; -pub struct PendingStateProvider { - base: Box, - pending_block_id: BlockNumber, - pending_state_updates: StateUpdates, - gateway: katana_gateway::client::Client, +pub struct PreconfStateProvider { + pub base: Box, + pub pending_block_id: BlockNumber, + pub pending_state_updates: StateUpdates, + pub gateway: katana_gateway::client::Client, } -impl StateProvider for PendingStateProvider { +impl StateProvider for PreconfStateProvider { fn nonce(&self, address: ContractAddress) -> ProviderResult> { if let Some(nonce) = self.pending_state_updates.nonce_updates.get(&address) { return Ok(Some(*nonce)); @@ -56,7 +56,7 @@ impl StateProvider for PendingStateProvider { } } -impl ContractClassProvider for PendingStateProvider { +impl ContractClassProvider for PreconfStateProvider { fn class(&self, hash: ClassHash) -> ProviderResult> { if let Some(class) = self.base.class(hash)? { return Ok(Some(class)); @@ -102,5 +102,5 @@ impl ContractClassProvider for PendingStateProvider { } } -impl StateRootProvider for PendingStateProvider {} -impl StateProofProvider for PendingStateProvider {} +impl StateRootProvider for PreconfStateProvider {} +impl StateProofProvider for PreconfStateProvider {} diff --git a/crates/storage/provider/provider-api/src/pending.rs b/crates/storage/provider/provider-api/src/pending.rs index bc7cd3e42..9f80591e0 100644 --- a/crates/storage/provider/provider-api/src/pending.rs +++ b/crates/storage/provider/provider-api/src/pending.rs @@ -2,9 +2,8 @@ use katana_primitives::block::PartialHeader; use katana_primitives::env::BlockEnv; use katana_primitives::receipt::Receipt; use katana_primitives::state::StateUpdates; -use katana_primitives::transaction::{TxHash, TxWithHash}; +use katana_primitives::transaction::TxWithHash; -use crate::state::StateProvider; use crate::ProviderResult; #[auto_impl::auto_impl(&, Box, Arc)] diff --git a/crates/storage/provider/provider/src/providers/db/trie.rs b/crates/storage/provider/provider/src/providers/db/trie.rs index 912d510b6..fe563a9b2 100644 --- a/crates/storage/provider/provider/src/providers/db/trie.rs +++ b/crates/storage/provider/provider/src/providers/db/trie.rs @@ -115,7 +115,6 @@ fn contract_state_leaf_hash( provider: impl StateProvider, address: &ContractAddress, contract_leaf: &ContractLeaf, - block_number: BlockNumber, ) -> Felt { let nonce = contract_leaf.nonce.unwrap_or(provider.nonce(*address).unwrap().unwrap_or_default()); From 3a297c1e3c274c9acadb1d8a9e3be8f3d65ff56f Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Sat, 25 Oct 2025 18:02:14 -0400 Subject: [PATCH 25/49] wip --- crates/node/src/full/pending/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/node/src/full/pending/mod.rs b/crates/node/src/full/pending/mod.rs index 56ef6bc6a..4bbbb3d1a 100644 --- a/crates/node/src/full/pending/mod.rs +++ b/crates/node/src/full/pending/mod.rs @@ -25,7 +25,7 @@ pub struct PreconfBlockWatcher { gateway_client: Client, // from pipeline - latest_synced_block: watch::Receiver<(BlockNumber, BlockHash)>, + latest_synced_block: watch::Receiver, // from tip watcher (actual tip of the chain) latest_block: TipSubscription, @@ -55,7 +55,11 @@ impl PreconfBlockWatcher { .transaction_state_diffs .into_iter() .fold(StateDiff::default(), |acc, diff| { - diff.and_then(|diff| acc.merge(diff)) + if let Some(diff) = diff { + acc.merge(diff) + } else { + acc + } }) .into(); From 1e99c90d340b881e6570bcc35091dbe467ea5b5e Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 27 Oct 2025 12:12:26 -0400 Subject: [PATCH 26/49] update preconf state --- crates/node/src/full/mod.rs | 10 +- crates/node/src/full/pending/mod.rs | 190 +++++++++++++--------- crates/node/src/full/pending/state.rs | 35 +++- crates/rpc/rpc/src/starknet/blockifier.rs | 110 +++++++------ 4 files changed, 203 insertions(+), 142 deletions(-) diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index fac8e9236..f835f1d7c 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -8,17 +8,12 @@ use http::header::CONTENT_TYPE; use http::Method; use jsonrpsee::RpcModule; use katana_chain_spec::ChainSpec; -use katana_core::backend::storage::Database; use katana_executor::ExecutionFlags; use katana_gateway_client::Client as SequencerGateway; use katana_metrics::exporters::prometheus::PrometheusRecorder; use katana_metrics::{Report, Server as MetricsServer}; use katana_pipeline::{Pipeline, PipelineHandle}; use katana_pool::ordering::FiFo; -use katana_pool::pool::Pool; -use katana_pool::validation::NoopValidator; -use katana_pool::TxPool; -use katana_primitives::transaction::ExecutableTxWithHash; use katana_provider::providers::db::DbProvider; use katana_provider::BlockchainProvider; use katana_rpc::cors::Cors; @@ -34,12 +29,13 @@ use crate::config::db::DbConfig; use crate::config::metrics::MetricsConfig; mod exit; -pub mod tip_watcher; -mod pool; mod pending; +mod pool; +pub mod tip_watcher; use exit::NodeStoppedFuture; use tip_watcher::ChainTipWatcher; + use crate::config::rpc::{RpcConfig, RpcModuleKind}; use crate::full::pool::{FullNodePool, GatewayProxyValidator}; diff --git a/crates/node/src/full/pending/mod.rs b/crates/node/src/full/pending/mod.rs index 4bbbb3d1a..2517c55c8 100644 --- a/crates/node/src/full/pending/mod.rs +++ b/crates/node/src/full/pending/mod.rs @@ -2,11 +2,8 @@ use std::sync::Arc; use std::time::Duration; use katana_gateway::client::Client; -use katana_gateway::types::{ - ConfirmedTransaction, ErrorCode, GatewayError, PreConfirmedBlock, StateDiff, - StateUpdateWithBlock, -}; -use katana_primitives::block::{BlockHash, BlockNumber}; +use katana_gateway::types::{ConfirmedTransaction, ErrorCode, PreConfirmedBlock, StateDiff}; +use katana_primitives::block::BlockNumber; use katana_primitives::state::StateUpdates; use katana_provider::api::state::StateFactoryProvider; use parking_lot::Mutex; @@ -18,9 +15,99 @@ use crate::full::tip_watcher::TipSubscription; pub mod state; +pub struct PreconfStateFactory { + // from pipeline + latest_synced_block: watch::Receiver, + gateway_client: Client, + provider: P, + + // shared state + shared_preconf_block: SharedPreconfBlockData, +} + +impl PreconfStateFactory

{ + pub fn new( + state_factory_provider: P, + gateway_client: Client, + latest_synced_block: watch::Receiver, + tip_subscription: TipSubscription, + ) -> Self { + let shared_preconf_block = SharedPreconfBlockData::default(); + + let mut worker = PreconfBlockWatcher { + interval: DEFAULT_INTERVAL, + latest_block: tip_subscription, + gateway_client: gateway_client.clone(), + latest_synced_block: latest_synced_block.clone(), + shared_preconf_block: shared_preconf_block.clone(), + }; + + tokio::spawn(async move { worker.run().await }); + + Self { + gateway_client, + latest_synced_block, + shared_preconf_block, + provider: state_factory_provider, + } + } + + pub fn state(&self) -> PreconfStateProvider { + let latest_block_num = *self.latest_synced_block.borrow(); + let base = self.provider.historical(latest_block_num.into()).unwrap().unwrap(); + + let preconf_block = self.shared_preconf_block.inner.lock(); + let preconf_block_id = preconf_block.as_ref().map(|b| b.preconf_block_id); + let preconf_state_updates = preconf_block.as_ref().map(|b| b.preconf_state_updates.clone()); + + PreconfStateProvider { + base, + preconf_block_id, + preconf_state_updates, + gateway: self.gateway_client.clone(), + } + } + + pub fn state_updates(&self) -> Option { + if let Some(preconf_data) = self.shared_preconf_block.inner.lock().as_ref() { + Some(preconf_data.preconf_state_updates.clone()) + } else { + None + } + } + + pub fn block(&self) -> Option { + if let Some(preconf_data) = self.shared_preconf_block.inner.lock().as_ref() { + Some(preconf_data.preconf_block.clone()) + } else { + None + } + } + + pub fn transactions(&self) -> Option> { + if let Some(preconf_data) = self.shared_preconf_block.inner.lock().as_ref() { + Some(preconf_data.preconf_block.transactions.clone()) + } else { + None + } + } +} + +#[derive(Debug, Default, Clone)] +struct SharedPreconfBlockData { + inner: Arc>>, +} + +#[derive(Debug)] +struct PreconfBlockData { + preconf_block_id: BlockNumber, + preconf_block: PreConfirmedBlock, + preconf_state_updates: StateUpdates, +} + const DEFAULT_INTERVAL: Duration = Duration::from_millis(500); -pub struct PreconfBlockWatcher { +struct PreconfBlockWatcher { interval: Duration, gateway_client: Client, @@ -30,29 +117,20 @@ pub struct PreconfBlockWatcher { latest_block: TipSubscription, // shared state - pending_block_id: Arc>, - pending_state_updates: Arc>, + shared_preconf_block: SharedPreconfBlockData, } impl PreconfBlockWatcher { - pub fn new(gateway_client: Client, tip_subscription: TipSubscription) -> Self { - todo!() - } - - pub async fn run(&mut self) { - let mut current_preconf_block_num = 0; + async fn run(&mut self) { + let mut current_preconf_block_num = *self.latest_synced_block.borrow() + 1; loop { - let latest_chain_tip = self.latest_block.tip(); - let latest_synced_block_num = *self.latest_synced_block.borrow(); - - if latest_synced_block_num >= latest_chain_tip { - let preconf_block_num = latest_synced_block_num + 1; - - match self.gateway_client.get_preconfirmed_block(preconf_block_num).await { + if current_preconf_block_num >= self.latest_block.tip() { + match self.gateway_client.get_preconfirmed_block(current_preconf_block_num).await { Ok(preconf_block) => { let preconf_state_diff: StateUpdates = preconf_block .transaction_state_diffs + .clone() .into_iter() .fold(StateDiff::default(), |acc, diff| { if let Some(diff) = diff { @@ -64,11 +142,18 @@ impl PreconfBlockWatcher { .into(); // update shared state - *self.pending_block_id.lock() = current_preconf_block_num; - *self.pending_state_updates.lock() = preconf_state_diff; - - // increment to get the next preconf block number - current_preconf_block_num = preconf_block_num + 1; + let mut shared_data_lock = self.shared_preconf_block.inner.lock(); + if let Some(block) = shared_data_lock.as_mut() { + block.preconf_block = preconf_block; + block.preconf_block_id = current_preconf_block_num; + block.preconf_state_updates = preconf_state_diff; + } else { + *shared_data_lock = Some(PreconfBlockData { + preconf_block, + preconf_state_updates: preconf_state_diff, + preconf_block_id: current_preconf_block_num, + }) + } } // this could either be because the latest block is still not synced to the @@ -92,56 +177,15 @@ impl PreconfBlockWatcher { error!(error = ?err, "Error receiving latest block number."); break; } + + let latest_synced_block_num = *self.latest_synced_block.borrow(); + current_preconf_block_num = latest_synced_block_num + 1; } - _ = tokio::time::sleep(self.interval) => {} + _ = tokio::time::sleep(self.interval) => { + current_preconf_block_num += 1; + } } } } } - -pub struct PreconfStateFactory { - // from pipeline - latest_synced_block: watch::Receiver, - gateway_client: Client, - provider: P, - - // shared state - preconf_block_id: Arc>, - preconf_block: Arc>, - preconf_state_updates: Arc>, -} - -impl PreconfStateFactory

{ - pub fn new( - state_factory_provider: P, - gateway_client: Client, - chain_tip_subscription: TipSubscription, - ) -> Self { - todo!() - } - - pub fn state(&self) -> PreconfStateProvider { - let latest_block_num = *self.latest_synced_block.borrow(); - let latest_state = self.provider.historical(latest_block_num.into()).unwrap().unwrap(); - - PreconfStateProvider { - base: latest_state, - gateway: self.gateway_client.clone(), - pending_block_id: self.preconf_block_id.lock().clone(), - pending_state_updates: self.preconf_state_updates.lock().clone(), - } - } - - pub fn state_updates(&self) -> StateUpdates { - self.preconf_state_updates.lock().clone() - } - - pub fn block(&self) -> PreConfirmedBlock { - self.preconf_block.lock().clone() - } - - pub fn transactions(&self) -> Vec { - self.preconf_block.lock().transactions.clone() - } -} diff --git a/crates/node/src/full/pending/state.rs b/crates/node/src/full/pending/state.rs index 18370c880..a331c3c93 100644 --- a/crates/node/src/full/pending/state.rs +++ b/crates/node/src/full/pending/state.rs @@ -3,7 +3,6 @@ use katana_primitives::block::BlockNumber; use katana_primitives::class::{ClassHash, CompiledClassHash, ContractClass}; use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; use katana_primitives::state::StateUpdates; -use katana_primitives::Felt; use katana_provider::api::contract::ContractClassProvider; use katana_provider::api::state::{StateProofProvider, StateProvider, StateRootProvider}; use katana_provider::{ProviderError, ProviderResult}; @@ -12,14 +11,18 @@ use tokio::runtime; pub struct PreconfStateProvider { pub base: Box, - pub pending_block_id: BlockNumber, - pub pending_state_updates: StateUpdates, + pub preconf_block_id: Option, + pub preconf_state_updates: Option, pub gateway: katana_gateway::client::Client, } impl StateProvider for PreconfStateProvider { fn nonce(&self, address: ContractAddress) -> ProviderResult> { - if let Some(nonce) = self.pending_state_updates.nonce_updates.get(&address) { + if let Some(nonce) = self + .preconf_state_updates + .as_ref() + .and_then(|updates| updates.nonce_updates.get(&address)) + { return Ok(Some(*nonce)); } @@ -31,7 +34,11 @@ impl StateProvider for PreconfStateProvider { address: ContractAddress, storage_key: StorageKey, ) -> ProviderResult> { - if let Some(contract_storage) = self.pending_state_updates.storage_updates.get(&address) { + if let Some(contract_storage) = self + .preconf_state_updates + .as_ref() + .and_then(|updates| updates.storage_updates.get(&address)) + { if let Some(value) = contract_storage.get(&storage_key) { return Ok(Some(*value)); } @@ -44,11 +51,19 @@ impl StateProvider for PreconfStateProvider { &self, address: ContractAddress, ) -> ProviderResult> { - if let Some(class_hash) = self.pending_state_updates.replaced_classes.get(&address) { + if let Some(class_hash) = self + .preconf_state_updates + .as_ref() + .and_then(|updates| updates.replaced_classes.get(&address)) + { return Ok(Some(*class_hash)); } - if let Some(class_hash) = self.pending_state_updates.deployed_contracts.get(&address) { + if let Some(class_hash) = self + .preconf_state_updates + .as_ref() + .and_then(|updates| updates.deployed_contracts.get(&address)) + { return Ok(Some(*class_hash)); } @@ -93,7 +108,11 @@ impl ContractClassProvider for PreconfStateProvider { &self, hash: ClassHash, ) -> ProviderResult> { - if let Some(compiled_hash) = self.pending_state_updates.declared_classes.get(&hash) { + if let Some(compiled_hash) = self + .preconf_state_updates + .as_ref() + .and_then(|updates| updates.declared_classes.get(&hash)) + { return Ok(Some(*compiled_hash)); } diff --git a/crates/rpc/rpc/src/starknet/blockifier.rs b/crates/rpc/rpc/src/starknet/blockifier.rs index dcbf6c67b..619d7cbf8 100644 --- a/crates/rpc/rpc/src/starknet/blockifier.rs +++ b/crates/rpc/rpc/src/starknet/blockifier.rs @@ -154,57 +154,59 @@ fn to_api_error(error: ExecutionError) -> StarknetApiError { } } -#[cfg(test)] -mod tests { - use std::usize; - - use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; - use katana_primitives::{address, ContractAddress}; - use katana_provider::api::state::StateFactoryProvider; - use katana_provider::test_utils::{get_chain_for_testing, test_provider}; - use katana_rpc_api::error::starknet::StarknetApiError; - use katana_rpc_types::FunctionCall; - use starknet::macros::selector; - - #[test] - fn call_on_contract_not_deployed() { - let chain_spec = get_chain_for_testing(); - let provider = test_provider(); - let state = provider.latest().unwrap(); - - let max_call_gas = 1_000_000_000; - let block_env = BlockEnv::default(); - let cfg_env = - VersionedConstantsOverrides { max_recursion_depth: usize::MAX, ..Default::default() }; - - let call = FunctionCall { - calldata: Vec::new(), - contract_address: address!("1337"), - entry_point_selector: selector!("foo"), - }; - - let result = super::call(&chain_spec, state, block_env, &cfg_env, call, max_call_gas); - assert!(matches!(result, Err(StarknetApiError::ContractNotFound))); - } - - #[test] - fn call_on_entry_point_not_found() { - let chain_spec = get_chain_for_testing(); - let provider = test_provider(); - let state = provider.latest().unwrap(); - - let max_call_gas = 1_000_000_000; - let block_env = BlockEnv::default(); - let cfg_env = - VersionedConstantsOverrides { max_recursion_depth: usize::MAX, ..Default::default() }; - - let call = FunctionCall { - calldata: Vec::new(), - contract_address: address!("0x1"), - entry_point_selector: selector!("foobar"), - }; - - let result = super::call(&chain_spec, state, block_env, &cfg_env, call, max_call_gas); - assert!(matches!(result, Err(StarknetApiError::EntrypointNotFound))); - } -} +// #[cfg(test)] +// mod tests { +// use std::usize; + +// use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; +// use katana_primitives::{address, ContractAddress}; +// use katana_provider::api::state::StateFactoryProvider; +// use katana_provider::test_utils::{get_chain_for_testing, test_provider}; +// use katana_rpc_api::error::starknet::StarknetApiError; +// use katana_rpc_types::FunctionCall; +// use starknet::macros::selector; + +// #[test] +// fn call_on_contract_not_deployed() { +// let chain_spec = get_chain_for_testing(); +// let provider = test_provider(); +// let state = provider.latest().unwrap(); + +// let max_call_gas = 1_000_000_000; +// let block_env = BlockEnv::default(); +// let cfg_env = +// VersionedConstantsOverrides { max_recursion_depth: usize::MAX, ..Default::default() +// }; + +// let call = FunctionCall { +// calldata: Vec::new(), +// contract_address: address!("1337"), +// entry_point_selector: selector!("foo"), +// }; + +// let result = super::call(&chain_spec, state, block_env, &cfg_env, call, max_call_gas); +// assert!(matches!(result, Err(StarknetApiError::ContractNotFound))); +// } + +// #[test] +// fn call_on_entry_point_not_found() { +// let chain_spec = get_chain_for_testing(); +// let provider = test_provider(); +// let state = provider.latest().unwrap(); + +// let max_call_gas = 1_000_000_000; +// let block_env = BlockEnv::default(); +// let cfg_env = +// VersionedConstantsOverrides { max_recursion_depth: usize::MAX, ..Default::default() +// }; + +// let call = FunctionCall { +// calldata: Vec::new(), +// contract_address: address!("0x1"), +// entry_point_selector: selector!("foobar"), +// }; + +// let result = super::call(&chain_spec, state, block_env, &cfg_env, call, max_call_gas); +// assert!(matches!(result, Err(StarknetApiError::EntrypointNotFound))); +// } +// } From 42a28dc34dac34591822364389e392e94fd95fca Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 27 Oct 2025 15:26:11 -0400 Subject: [PATCH 27/49] rpc pending provider --- Cargo.lock | 3 + crates/core/src/utils/mod.rs | 2 +- crates/node/Cargo.toml | 2 + crates/node/src/full/pending/mod.rs | 2 + crates/node/src/full/pending/provider.rs | 69 +++++++++++++++++++ crates/node/src/lib.rs | 4 +- crates/oracle/gas/src/fixed.rs | 2 +- crates/primitives/src/block.rs | 8 +++ crates/rpc/rpc/Cargo.toml | 1 + crates/rpc/rpc/src/starknet/mod.rs | 34 +++++++-- .../storage/provider/provider-api/src/lib.rs | 1 - .../provider/provider-api/src/pending.rs | 18 ----- 12 files changed, 120 insertions(+), 26 deletions(-) create mode 100644 crates/node/src/full/pending/provider.rs delete mode 100644 crates/storage/provider/provider-api/src/pending.rs diff --git a/Cargo.lock b/Cargo.lock index 9df3f2344..075bf2de7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6318,9 +6318,11 @@ dependencies = [ "katana-starknet", "katana-tasks", "katana-tracing", + "num-traits", "parking_lot", "serde", "serde_json", + "starknet", "strum 0.25.0", "strum_macros 0.25.3", "thiserror 1.0.69", @@ -6501,6 +6503,7 @@ dependencies = [ "katana-core", "katana-executor", "katana-explorer", + "katana-gas-price-oracle", "katana-genesis", "katana-messaging", "katana-metrics", diff --git a/crates/core/src/utils/mod.rs b/crates/core/src/utils/mod.rs index 9094e57ef..8a7e83c47 100644 --- a/crates/core/src/utils/mod.rs +++ b/crates/core/src/utils/mod.rs @@ -1,6 +1,6 @@ use std::time::SystemTime; -pub(super) fn get_current_timestamp() -> std::time::Duration { +pub fn get_current_timestamp() -> std::time::Duration { SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .expect("should get current UNIX timestamp") diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 2d86d6b76..c2e191044 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -29,6 +29,8 @@ katana-tasks.workspace = true katana-tracing.workspace = true anyhow.workspace = true +starknet.workspace = true +num-traits.workspace = true futures.workspace = true http.workspace = true jsonrpsee.workspace = true diff --git a/crates/node/src/full/pending/mod.rs b/crates/node/src/full/pending/mod.rs index 2517c55c8..e504147bd 100644 --- a/crates/node/src/full/pending/mod.rs +++ b/crates/node/src/full/pending/mod.rs @@ -13,8 +13,10 @@ use tracing::error; use crate::full::pending::state::PreconfStateProvider; use crate::full::tip_watcher::TipSubscription; +mod provider; pub mod state; +#[derive(Debug)] pub struct PreconfStateFactory { // from pipeline latest_synced_block: watch::Receiver, diff --git a/crates/node/src/full/pending/provider.rs b/crates/node/src/full/pending/provider.rs new file mode 100644 index 000000000..22858b50c --- /dev/null +++ b/crates/node/src/full/pending/provider.rs @@ -0,0 +1,69 @@ +use std::fmt::Debug; + +use katana_gateway::types::TxTryFromError; +use katana_primitives::block::{GasPrices, PartialHeader, PendingBlock}; +use katana_primitives::transaction::TxWithHash; +use katana_primitives::Felt; +use katana_provider::api::state::{StateFactoryProvider, StateProvider}; +use katana_rpc::starknet::PendingBlockProvider; +use num_traits::ToPrimitive; +use starknet::core::types::ResourcePrice; + +use crate::full::pending::PreconfStateFactory; + +impl PendingBlockProvider for PreconfStateFactory

{ + fn pending_block(&self) -> Option { + if let Some(preconf_block) = self.block() { + Some(PendingBlock { + header: PartialHeader { + l1_da_mode: preconf_block.l1_da_mode, + l1_gas_prices: to_gas_prices(preconf_block.l1_gas_price), + l2_gas_prices: to_gas_prices(preconf_block.l2_gas_price), + l1_data_gas_prices: to_gas_prices(preconf_block.l1_data_gas_price), + sequencer_address: preconf_block.sequencer_address, + starknet_version: preconf_block.starknet_version.try_into().unwrap(), + timestamp: preconf_block.timestamp, + number: 0, + parent_hash: Felt::ZERO, + }, + body: preconf_block + .transactions + .clone() + .into_iter() + .map(TxWithHash::try_from) + .collect::, TxTryFromError>>() + .unwrap(), + }) + } else { + None + } + } + + fn pending_state_update(&self) -> Option { + self.state_updates() + } + + fn pending_transactions( + &self, + ) -> Vec<( + katana_primitives::transaction::TxWithHash, + Option, + )> { + todo!() + } + + fn pending_state(&self) -> Option> { + Some(Box::new(self.state())) + } +} + +fn to_gas_prices(prices: ResourcePrice) -> GasPrices { + let eth = prices.price_in_wei.to_u128().expect("valid u128"); + let strk = prices.price_in_fri.to_u128().expect("valid u128"); + // older blocks might have zero gas prices (recent Starknet upgrade has made the minimum gas + // prices to 1) we may need to handle this case if we want to be able to compute the + // block hash correctly + let eth = if eth == 0 { 1 } else { eth }; + let strk = if strk == 0 { 1 } else { strk }; + unsafe { GasPrices::new_unchecked(eth, strk) } +} diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index c2257d1a8..cc9981897 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -189,7 +189,7 @@ impl Node { let block_context_generator = BlockContextGenerator::default().into(); let backend = Arc::new(Backend { - gas_oracle, + gas_oracle: gas_oracle.clone(), blockchain, executor_factory, block_context_generator, @@ -272,6 +272,7 @@ impl Node { task_spawner.clone(), starknet_api_cfg, block_producer.clone(), + gas_oracle.clone(), ) } else { StarknetApi::new( @@ -281,6 +282,7 @@ impl Node { task_spawner.clone(), starknet_api_cfg, block_producer.clone(), + gas_oracle.clone(), ) }; diff --git a/crates/oracle/gas/src/fixed.rs b/crates/oracle/gas/src/fixed.rs index 8d5bf7b31..d3f126c01 100644 --- a/crates/oracle/gas/src/fixed.rs +++ b/crates/oracle/gas/src/fixed.rs @@ -3,7 +3,7 @@ use std::num::NonZeroU128; use katana_primitives::block::{GasPrice, GasPrices}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FixedPriceOracle { l2_gas_prices: GasPrices, l1_gas_prices: GasPrices, diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 15866a6ea..1c20a8a06 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -116,6 +116,14 @@ pub struct PartialHeader { pub starknet_version: StarknetVersion, } +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct PendingBlock { + pub header: PartialHeader, + pub body: Vec, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 011a0aec8..8235f4f86 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -22,6 +22,7 @@ katana-rpc-types.workspace = true katana-rpc-types-builder.workspace = true katana-tasks.workspace = true katana-tracing.workspace = true +katana-gas-price-oracle.workspace = true anyhow.workspace = true auto_impl.workspace = true diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index 3abee14c7..e4c336f6f 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -8,6 +8,8 @@ use katana_core::backend::Backend; use katana_executor::ExecutorFactory; use katana_chain_spec::ChainSpec; use katana_core::backend::storage::Database; +use katana_core::utils::get_current_timestamp; +use katana_gas_price_oracle::GasPriceOracle; use katana_pool::TransactionPool; use katana_primitives::block::{BlockHashOrNumber, BlockIdOrTag, FinalityStatus, GasPrices}; use katana_primitives::class::{ClassHash, CompiledClass}; @@ -15,11 +17,11 @@ use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageVal use katana_primitives::env::BlockEnv; use katana_primitives::event::MaybeForkedContinuationToken; use katana_primitives::transaction::{ExecutableTxWithHash, TxHash, TxNumber}; +use katana_primitives::version::CURRENT_STARKNET_VERSION; use katana_primitives::Felt; use katana_provider::api::block::{BlockHashProvider, BlockIdReader, BlockNumberProvider}; use katana_provider::api::contract::ContractClassProvider; use katana_provider::api::env::BlockEnvProvider; -use katana_provider::api::pending::PendingBlockProvider; use katana_provider::api::state::{StateFactoryProvider, StateProvider, StateRootProvider}; use katana_provider::api::transaction::{ ReceiptProvider, TransactionProvider, TransactionStatusProvider, TransactionsProviderExt, @@ -68,6 +70,7 @@ mod write; pub use config::PaymasterConfig; pub use config::StarknetApiConfig; use forking::ForkedClient; +pub use pending::PendingBlockProvider; type StarknetApiResult = Result; @@ -95,7 +98,8 @@ where pool: Pool, chain_spec: Arc, pool: P, - // pending_provider: Arc, + gas_oracle: GasPriceOracle, + preconf_provider: Arc, storage: BlockchainProvider>, forked_client: Option, task_spawner: TaskSpawner, @@ -142,8 +146,18 @@ where task_spawner: TaskSpawner, config: StarknetApiConfig, pending_block_provider: PP, + gas_oracle: GasPriceOracle, ) -> Self { - Self::new_inner(chain_spec, storage, pool, None, task_spawner, config, pending_block_provider) + Self::new_inner( + chain_spec, + storage, + pool, + None, + task_spawner, + config, + pending_block_provider, + gas_oracle, + ) } pub fn new_forked( @@ -154,8 +168,18 @@ where task_spawner: TaskSpawner, config: StarknetApiConfig, pending_block_provider: PP, + gas_oracle: GasPriceOracle, ) -> Self { - Self::new_inner(chain_spec, storage, pool, Some(forked_client), task_spawner, config, pending_block_provider) + Self::new_inner( + chain_spec, + storage, + pool, + Some(forked_client), + task_spawner, + config, + pending_block_provider, + gas_oracle, + ) } fn new_inner( @@ -166,6 +190,7 @@ where task_spawner: TaskSpawner, config: StarknetApiConfig, pending_block_provider: PP, + gas_oracle: GasPriceOracle, ) -> Self { let total_permits = config .max_concurrent_estimate_fee_requests @@ -181,6 +206,7 @@ where estimate_fee_permit, config, pending_block_provider, + gas_oracle, }; Self { inner: Arc::new(inner) } diff --git a/crates/storage/provider/provider-api/src/lib.rs b/crates/storage/provider/provider-api/src/lib.rs index 3d21f920a..31fdfcfe7 100644 --- a/crates/storage/provider/provider-api/src/lib.rs +++ b/crates/storage/provider/provider-api/src/lib.rs @@ -6,7 +6,6 @@ pub mod block; pub mod contract; pub mod env; mod error; -pub mod pending; pub mod stage; pub mod state; pub mod state_update; diff --git a/crates/storage/provider/provider-api/src/pending.rs b/crates/storage/provider/provider-api/src/pending.rs deleted file mode 100644 index 9f80591e0..000000000 --- a/crates/storage/provider/provider-api/src/pending.rs +++ /dev/null @@ -1,18 +0,0 @@ -use katana_primitives::block::PartialHeader; -use katana_primitives::env::BlockEnv; -use katana_primitives::receipt::Receipt; -use katana_primitives::state::StateUpdates; -use katana_primitives::transaction::TxWithHash; - -use crate::ProviderResult; - -#[auto_impl::auto_impl(&, Box, Arc)] -pub trait PendingBlockProvider: Send + Sync { - fn block_header(&self) -> ProviderResult>; - - fn block_env(&self) -> ProviderResult>; - - fn transactions(&self) -> ProviderResult)>>; - - fn state_update(&self) -> ProviderResult>; -} From 303da6cad7603a07d0f7b3b6795390617592bb2a Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 27 Oct 2025 17:11:42 -0400 Subject: [PATCH 28/49] implement rpc pending provider to block producer --- crates/rpc/rpc/src/starknet/mod.rs | 2 +- crates/rpc/rpc/src/starknet/read.rs | 1 - crates/storage/provider/provider/src/providers/db/trie.rs | 1 - crates/storage/provider/provider/src/providers/fork/state.rs | 4 +--- crates/sync/pipeline/src/lib.rs | 2 +- crates/sync/stage/src/trie.rs | 2 -- crates/sync/stage/tests/trie.rs | 1 - 7 files changed, 3 insertions(+), 10 deletions(-) diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index e4c336f6f..ebe87e021 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -99,7 +99,7 @@ where chain_spec: Arc, pool: P, gas_oracle: GasPriceOracle, - preconf_provider: Arc, + preconf_provider: Box, storage: BlockchainProvider>, forked_client: Option, task_spawner: TaskSpawner, diff --git a/crates/rpc/rpc/src/starknet/read.rs b/crates/rpc/rpc/src/starknet/read.rs index 70b439602..a3dbf3fc7 100644 --- a/crates/rpc/rpc/src/starknet/read.rs +++ b/crates/rpc/rpc/src/starknet/read.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use anyhow::anyhow; use jsonrpsee::core::{async_trait, RpcResult}; use jsonrpsee::types::ErrorObjectOwned; -use katana_executor::ExecutorFactory; #[cfg(feature = "cartridge")] use katana_genesis::allocation::GenesisAccountAlloc; use katana_pool::TransactionPool; diff --git a/crates/storage/provider/provider/src/providers/db/trie.rs b/crates/storage/provider/provider/src/providers/db/trie.rs index fe563a9b2..f96465c36 100644 --- a/crates/storage/provider/provider/src/providers/db/trie.rs +++ b/crates/storage/provider/provider/src/providers/db/trie.rs @@ -13,7 +13,6 @@ use katana_provider_api::ProviderError; use katana_trie::{ compute_contract_state_hash, ClassesTrie, ContractLeaf, ContractsTrie, StoragesTrie, }; -use starknet::providers::Provider; use crate::providers::db::DbProvider; use crate::ProviderResult; diff --git a/crates/storage/provider/provider/src/providers/fork/state.rs b/crates/storage/provider/provider/src/providers/fork/state.rs index f59fca91f..a8db588ea 100644 --- a/crates/storage/provider/provider/src/providers/fork/state.rs +++ b/crates/storage/provider/provider/src/providers/fork/state.rs @@ -2,9 +2,7 @@ use std::cmp::Ordering; use std::sync::Arc; use katana_db::abstraction::{Database, DbTx, DbTxMut}; -use katana_db::models::contract::{ - ContractClassChange, ContractClassChangeType, ContractNonceChange, -}; +use katana_db::models::contract::{ContractClassChange, ContractNonceChange}; use katana_db::models::storage::{ContractStorageEntry, ContractStorageKey, StorageEntry}; use katana_db::tables; use katana_fork::BackendClient; diff --git a/crates/sync/pipeline/src/lib.rs b/crates/sync/pipeline/src/lib.rs index 35507a961..ea184b82a 100644 --- a/crates/sync/pipeline/src/lib.rs +++ b/crates/sync/pipeline/src/lib.rs @@ -83,7 +83,7 @@ use katana_provider_api::ProviderError; use katana_stage::{Stage, StageExecutionInput, StageExecutionOutput}; use tokio::sync::watch; use tokio::task::yield_now; -use tracing::{debug, error, info, info_span, Instrument, Span}; +use tracing::{debug, error, info, info_span, Instrument}; /// The result of a pipeline execution. pub type PipelineResult = Result; diff --git a/crates/sync/stage/src/trie.rs b/crates/sync/stage/src/trie.rs index ee1f90752..19e1fe7ec 100644 --- a/crates/sync/stage/src/trie.rs +++ b/crates/sync/stage/src/trie.rs @@ -4,8 +4,6 @@ use katana_primitives::Felt; use katana_provider::api::block::HeaderProvider; use katana_provider::api::state_update::StateUpdateProvider; use katana_provider::api::trie::TrieWriter; -use katana_rpc_types::class; -use katana_trie::CommitId; use starknet::macros::short_string; use starknet_types_core::hash::{Poseidon, StarkHash}; use tracing::{debug, debug_span, error}; diff --git a/crates/sync/stage/tests/trie.rs b/crates/sync/stage/tests/trie.rs index ca32d05c5..74f9406b9 100644 --- a/crates/sync/stage/tests/trie.rs +++ b/crates/sync/stage/tests/trie.rs @@ -14,7 +14,6 @@ use katana_stage::trie::StateTrie; use katana_stage::{Stage, StageExecutionInput}; use rstest::rstest; use starknet::macros::short_string; -use starknet::providers::sequencer::models::state_update; use starknet_types_core::hash::{Poseidon, StarkHash}; /// Mock provider implementation for testing StateTrie stage. From 3f9c88b3b35ddb0003663caf53ad4ac9cdb10623 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 27 Oct 2025 18:05:01 -0400 Subject: [PATCH 29/49] node integration --- Cargo.lock | 2 - .../core/src/service/block_producer_tests.rs | 1 - crates/gateway/gateway-server/Cargo.toml | 2 - crates/node/src/full/mod.rs | 44 +++++++++++++------ crates/node/src/full/pending/mod.rs | 15 ++++--- crates/rpc/rpc/src/starknet/mod.rs | 10 ++--- crates/starknet/src/lib.rs | 9 ++-- crates/sync/pipeline/src/lib.rs | 1 + 8 files changed, 48 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 075bf2de7..9ec08bc81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6177,8 +6177,6 @@ dependencies = [ "axum 0.7.9", "http 1.3.1", "http-body 1.0.1", - "katana-core", - "katana-executor", "katana-gateway-types", "katana-metrics", "katana-pool", diff --git a/crates/core/src/service/block_producer_tests.rs b/crates/core/src/service/block_producer_tests.rs index ea96a03d1..326f67fde 100644 --- a/crates/core/src/service/block_producer_tests.rs +++ b/crates/core/src/service/block_producer_tests.rs @@ -6,7 +6,6 @@ use katana_gas_price_oracle::GasPriceOracle; use katana_primitives::transaction::{ExecutableTx, InvokeTx}; use katana_primitives::Felt; use katana_provider::providers::db::DbProvider; -use katana_tasks::TaskManager; use super::*; use crate::backend::storage::Blockchain; diff --git a/crates/gateway/gateway-server/Cargo.toml b/crates/gateway/gateway-server/Cargo.toml index 5e68497cb..af6665959 100644 --- a/crates/gateway/gateway-server/Cargo.toml +++ b/crates/gateway/gateway-server/Cargo.toml @@ -7,8 +7,6 @@ version.workspace = true [dependencies] katana-gateway-types.workspace = true -katana-executor.workspace = true -katana-core.workspace = true katana-primitives.workspace = true katana-metrics.workspace = true katana-pool.workspace = true diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index f835f1d7c..f5b114978 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -3,12 +3,14 @@ use std::future::IntoFuture; use std::sync::Arc; +use alloy_provider::RootProvider; use anyhow::Result; use http::header::CONTENT_TYPE; use http::Method; use jsonrpsee::RpcModule; use katana_chain_spec::ChainSpec; use katana_executor::ExecutionFlags; +use katana_gas_price_oracle::GasPriceOracle; use katana_gateway_client::Client as SequencerGateway; use katana_metrics::exporters::prometheus::PrometheusRecorder; use katana_metrics::{Report, Server as MetricsServer}; @@ -27,6 +29,7 @@ use tracing::{error, info}; use crate::config::db::DbConfig; use crate::config::metrics::MetricsConfig; +use crate::full::pending::PreconfStateFactory; mod exit; mod pending; @@ -75,6 +78,7 @@ pub struct Node { pub pipeline: Pipeline, pub rpc_server: RpcServer, pub gateway_client: SequencerGateway, + pub chain_tip_watcher: ChainTipWatcher, } impl Node { @@ -114,12 +118,32 @@ impl Node { // --- build pipeline - let (mut pipeline, _) = Pipeline::new(provider.clone(), 50); + let (mut pipeline, pipeline_handle) = Pipeline::new(provider.clone(), 50); let block_downloader = BatchBlockDownloader::new_gateway(gateway_client.clone(), 8); pipeline.add_stage(Blocks::new(provider.clone(), block_downloader)); pipeline.add_stage(Classes::new(provider.clone(), gateway_client.clone(), 8)); pipeline.add_stage(StateTrie::new(provider.clone())); + // -- + + let core_contract = match config.network { + Network::Mainnet => { + katana_starknet::StarknetCore::new_http_mainnet(&config.eth_rpc_url)? + } + Network::Sepolia => { + katana_starknet::StarknetCore::new_http_sepolia(&config.eth_rpc_url)? + } + }; + + let chain_tip_watcher = ChainTipWatcher::new(core_contract); + + let preconf_factory = PreconfStateFactory::new( + provider.clone(), + gateway_client.clone(), + pipeline_handle.subscribe_blocks(), + chain_tip_watcher.subscribe(), + ); + // --- build rpc server let mut rpc_modules = RpcModule::new(()); @@ -148,6 +172,8 @@ impl Node { pool.clone(), task_spawner.clone(), starknet_api_cfg, + Box::new(preconf_factory), + GasPriceOracle::create_for_testing(), ); if config.rpc.apis.contains(&RpcModuleKind::Starknet) { @@ -194,6 +220,7 @@ impl Node { rpc_server, task_manager, gateway_client, + chain_tip_watcher, config: Arc::new(config), }) } @@ -212,18 +239,7 @@ impl Node { let pipeline_handle = self.pipeline.handle(); - let core_contract = match self.config.network { - Network::Mainnet => { - katana_starknet::StarknetCore::new_http_mainnet(&self.config.eth_rpc_url).await? - } - Network::Sepolia => { - katana_starknet::StarknetCore::new_http_sepolia(&self.config.eth_rpc_url).await? - } - }; - - let tip_watcher = ChainTipWatcher::new(core_contract); - - let mut tip_subscription = tip_watcher.subscribe(); + let mut tip_subscription = self.chain_tip_watcher.subscribe(); let pipeline_handle_clone = pipeline_handle.clone(); self.task_manager @@ -237,7 +253,7 @@ impl Node { .build_task() .graceful_shutdown() .name("Chain tip watcher") - .spawn(tip_watcher.into_future()); + .spawn(self.chain_tip_watcher.into_future()); // spawn a task for updating the pipeline's tip based on chain tip changes self.task_manager.task_spawner().spawn(async move { diff --git a/crates/node/src/full/pending/mod.rs b/crates/node/src/full/pending/mod.rs index e504147bd..b559da6d3 100644 --- a/crates/node/src/full/pending/mod.rs +++ b/crates/node/src/full/pending/mod.rs @@ -3,11 +3,11 @@ use std::time::Duration; use katana_gateway::client::Client; use katana_gateway::types::{ConfirmedTransaction, ErrorCode, PreConfirmedBlock, StateDiff}; +use katana_pipeline::PipelineBlockSubscription; use katana_primitives::block::BlockNumber; use katana_primitives::state::StateUpdates; use katana_provider::api::state::StateFactoryProvider; use parking_lot::Mutex; -use tokio::sync::watch; use tracing::error; use crate::full::pending::state::PreconfStateProvider; @@ -19,7 +19,7 @@ pub mod state; #[derive(Debug)] pub struct PreconfStateFactory { // from pipeline - latest_synced_block: watch::Receiver, + latest_synced_block: PipelineBlockSubscription, gateway_client: Client, provider: P, @@ -31,7 +31,7 @@ impl PreconfStateFactory

{ pub fn new( state_factory_provider: P, gateway_client: Client, - latest_synced_block: watch::Receiver, + latest_synced_block: PipelineBlockSubscription, tip_subscription: TipSubscription, ) -> Self { let shared_preconf_block = SharedPreconfBlockData::default(); @@ -55,7 +55,7 @@ impl PreconfStateFactory

{ } pub fn state(&self) -> PreconfStateProvider { - let latest_block_num = *self.latest_synced_block.borrow(); + let latest_block_num = self.latest_synced_block.block().unwrap(); let base = self.provider.historical(latest_block_num.into()).unwrap().unwrap(); let preconf_block = self.shared_preconf_block.inner.lock(); @@ -114,7 +114,7 @@ struct PreconfBlockWatcher { gateway_client: Client, // from pipeline - latest_synced_block: watch::Receiver, + latest_synced_block: PipelineBlockSubscription, // from tip watcher (actual tip of the chain) latest_block: TipSubscription, @@ -124,7 +124,8 @@ struct PreconfBlockWatcher { impl PreconfBlockWatcher { async fn run(&mut self) { - let mut current_preconf_block_num = *self.latest_synced_block.borrow() + 1; + let mut current_preconf_block_num = + self.latest_synced_block.block().map(|b| b + 1).unwrap_or(0); loop { if current_preconf_block_num >= self.latest_block.tip() { @@ -180,7 +181,7 @@ impl PreconfBlockWatcher { break; } - let latest_synced_block_num = *self.latest_synced_block.borrow(); + let latest_synced_block_num = self.latest_synced_block.block().unwrap(); current_preconf_block_num = latest_synced_block_num + 1; } diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index ebe87e021..d90c2bd69 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -4,11 +4,11 @@ use std::fmt::Debug; use std::future::Future; use std::sync::Arc; -use katana_core::backend::Backend; -use katana_executor::ExecutorFactory; use katana_chain_spec::ChainSpec; use katana_core::backend::storage::Database; +use katana_core::backend::Backend; use katana_core::utils::get_current_timestamp; +use katana_executor::ExecutorFactory; use katana_gas_price_oracle::GasPriceOracle; use katana_pool::TransactionPool; use katana_primitives::block::{BlockHashOrNumber, BlockIdOrTag, FinalityStatus, GasPrices}; @@ -53,7 +53,6 @@ use katana_rpc_types_builder::{BlockBuilder, ReceiptBuilder}; use katana_tasks::{Result as TaskResult, TaskSpawner}; use crate::permit::Permits; -use crate::starknet::pending::PendingBlockProvider; use crate::utils::events::{Cursor, EventBlockId}; use crate::{utils, DEFAULT_ESTIMATE_FEE_MAX_CONCURRENT_REQUESTS}; @@ -97,9 +96,8 @@ where { pool: Pool, chain_spec: Arc, - pool: P, + pool: Pool, gas_oracle: GasPriceOracle, - preconf_provider: Box, storage: BlockchainProvider>, forked_client: Option, task_spawner: TaskSpawner, @@ -154,7 +152,7 @@ where pool, None, task_spawner, - config, + config, pending_block_provider, gas_oracle, ) diff --git a/crates/starknet/src/lib.rs b/crates/starknet/src/lib.rs index 40bd3165e..7242481b5 100644 --- a/crates/starknet/src/lib.rs +++ b/crates/starknet/src/lib.rs @@ -14,7 +14,8 @@ use alloy_network::Ethereum; use alloy_primitives::Address; -use alloy_provider::{Provider, RootProvider}; +use alloy_provider::Provider; +pub use alloy_provider::RootProvider; use alloy_rpc_types_eth::{Filter, FilterBlockOption, FilterSet, Log, Topic}; use alloy_sol_types::{sol, SolEvent}; use anyhow::Result; @@ -225,7 +226,7 @@ impl StarknetCore> { /// /// * `rpc_url` - The HTTP URL of the Ethereum RPC endpoint /// * `contract_address` - The address of the Starknet Core Contract - pub async fn new_http(rpc_url: impl AsRef, contract_address: Address) -> Result { + pub fn new_http(rpc_url: impl AsRef, contract_address: Address) -> Result { let provider = RootProvider::::new_http(reqwest::Url::parse(rpc_url.as_ref())?); Ok(Self::new(provider, contract_address)) } @@ -236,7 +237,7 @@ impl StarknetCore> { /// # Arguments /// /// * `rpc_url` - The HTTP URL of the Ethereum RPC endpoint - pub async fn new_http_mainnet(rpc_url: impl AsRef) -> Result { + pub fn new_http_mainnet(rpc_url: impl AsRef) -> Result { let provider = RootProvider::::new_http(reqwest::Url::parse(rpc_url.as_ref())?); Ok(Self::new_mainnet(provider)) } @@ -247,7 +248,7 @@ impl StarknetCore> { /// # Arguments /// /// * `rpc_url` - The HTTP URL of the Ethereum RPC endpoint - pub async fn new_http_sepolia(rpc_url: impl AsRef) -> Result { + pub fn new_http_sepolia(rpc_url: impl AsRef) -> Result { let provider = RootProvider::::new_http(reqwest::Url::parse(rpc_url.as_ref())?); Ok(Self::new_sepolia(provider)) } diff --git a/crates/sync/pipeline/src/lib.rs b/crates/sync/pipeline/src/lib.rs index ea184b82a..c8f1741ce 100644 --- a/crates/sync/pipeline/src/lib.rs +++ b/crates/sync/pipeline/src/lib.rs @@ -117,6 +117,7 @@ enum PipelineCommand { /// This subscription receives notifications whenever the pipeline completes processing /// a block through all stages. The block number represents the highest block that has /// been successfully processed by all pipeline stages for a given batch. +#[derive(Clone)] pub struct PipelineBlockSubscription { rx: watch::Receiver>, } From 218a69a26e3b2a68ac3319f7e1f6f1a9a513a2cc Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 27 Oct 2025 19:00:09 -0400 Subject: [PATCH 30/49] wip --- crates/node/src/full/mod.rs | 4 ++-- crates/node/src/full/pool.rs | 17 ++++------------- crates/rpc/rpc/src/starknet/blockifier.rs | 9 +-------- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index f5b114978..0af2fd317 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -15,7 +15,7 @@ use katana_gateway_client::Client as SequencerGateway; use katana_metrics::exporters::prometheus::PrometheusRecorder; use katana_metrics::{Report, Server as MetricsServer}; use katana_pipeline::{Pipeline, PipelineHandle}; -use katana_pool::ordering::FiFo; +use katana_pool::ordering::{FiFo, TipOrdering}; use katana_provider::providers::db::DbProvider; use katana_provider::BlockchainProvider; use katana_rpc::cors::Cors; @@ -114,7 +114,7 @@ impl Node { // --- build transaction pool let validator = GatewayProxyValidator::new(gateway_client.clone()); - let pool = FullNodePool::new(validator, FiFo::new()); + let pool = FullNodePool::new(validator, TipOrdering::new()); // --- build pipeline diff --git a/crates/node/src/full/pool.rs b/crates/node/src/full/pool.rs index 2eed2e11a..46c3e2eea 100644 --- a/crates/node/src/full/pool.rs +++ b/crates/node/src/full/pool.rs @@ -1,22 +1,13 @@ use std::future::Future; -use katana_pool::ordering::FiFo; +use katana_pool::ordering::TipOrdering; use katana_pool::pool::Pool; -use katana_pool::validation::stateful::TxValidator; -use katana_pool::PoolTransaction; use katana_pool_api::validation::{ValidationOutcome, ValidationResult, Validator}; -use katana_primitives::chain::ChainId; -use katana_primitives::fee::ResourceBoundsMapping; -use katana_primitives::transaction::{ - DeclareTx, DeployAccountTx, ExecutableTxWithHash, InvokeTx, TxHash, -}; -use katana_primitives::{ContractAddress, Felt}; -use katana_rpc_types::{ - BroadcastedDeclareTx, BroadcastedDeployAccountTx, BroadcastedInvokeTx, BroadcastedTx, -}; +use katana_primitives::transaction::ExecutableTxWithHash; +use katana_rpc_types::BroadcastedTx; pub type FullNodePool = - Pool>; + Pool>; /// This is an implementation of the [`Validator`] trait that proxies incoming transactions to a /// Starknet sequencer via the gateway endpoint. diff --git a/crates/rpc/rpc/src/starknet/blockifier.rs b/crates/rpc/rpc/src/starknet/blockifier.rs index 619d7cbf8..40a63ea52 100644 --- a/crates/rpc/rpc/src/starknet/blockifier.rs +++ b/crates/rpc/rpc/src/starknet/blockifier.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use katana_chain_spec::ChainSpec; -use katana_executor::implementation::blockifier::blockifier::context::BlockContext; use katana_executor::implementation::blockifier::cache::ClassCache; use katana_executor::implementation::blockifier::call::execute_call; use katana_executor::implementation::blockifier::state::CachedState; @@ -9,19 +8,13 @@ use katana_executor::implementation::blockifier::utils::{self, block_context_fro use katana_executor::{ExecutionError, ExecutionFlags, ExecutionResult, ResultAndStates}; use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::transaction::ExecutableTxWithHash; -use katana_primitives::{chain, Felt}; +use katana_primitives::Felt; use katana_provider::api::state::StateProvider; use katana_rpc_api::error::starknet::{ContractErrorData, StarknetApiError}; use katana_rpc_types::{FeeEstimate, FunctionCall}; use crate::starknet::StarknetApiResult; -pub struct ExecutionCtx { - state: Box, - block_context: Arc, - simulation_flags: ExecutionFlags, -} - #[tracing::instrument(level = "trace", target = "rpc", skip_all, fields(total_txs = transactions.len()))] pub fn simulate( chain_spec: &ChainSpec, From e0cf602ef61f6d821aa72f586163a1db8519957b Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 28 Oct 2025 00:12:36 -0400 Subject: [PATCH 31/49] wip --- Cargo.lock | 1 + crates/node/Cargo.toml | 1 + crates/node/src/full/pending/mod.rs | 6 +++--- crates/node/src/full/pending/provider.rs | 2 +- crates/node/src/full/pending/state.rs | 8 ++++---- crates/node/src/full/pool.rs | 4 ++-- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ec08bc81..630dac0f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6301,6 +6301,7 @@ dependencies = [ "katana-gas-price-oracle", "katana-gateway-client", "katana-gateway-server", + "katana-gateway-types", "katana-messaging", "katana-metrics", "katana-pipeline", diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index c2e191044..f086bf880 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -12,6 +12,7 @@ katana-db.workspace = true katana-executor.workspace = true katana-gateway-server.workspace = true katana-gateway-client.workspace = true +katana-gateway-types.workspace = true katana-gas-price-oracle.workspace = true katana-messaging.workspace = true katana-metrics.workspace = true diff --git a/crates/node/src/full/pending/mod.rs b/crates/node/src/full/pending/mod.rs index b559da6d3..99c4ae2c4 100644 --- a/crates/node/src/full/pending/mod.rs +++ b/crates/node/src/full/pending/mod.rs @@ -1,8 +1,8 @@ use std::sync::Arc; use std::time::Duration; -use katana_gateway::client::Client; -use katana_gateway::types::{ConfirmedTransaction, ErrorCode, PreConfirmedBlock, StateDiff}; +use katana_gateway_client::Client; +use katana_gateway_types::{ConfirmedTransaction, ErrorCode, PreConfirmedBlock, StateDiff}; use katana_pipeline::PipelineBlockSubscription; use katana_primitives::block::BlockNumber; use katana_primitives::state::StateUpdates; @@ -162,7 +162,7 @@ impl PreconfBlockWatcher { // this could either be because the latest block is still not synced to the // chain's tip, in which case we just skip to the next // iteration. - Err(katana_gateway::client::Error::Sequencer(error)) + Err(katana_gateway_client::Error::Sequencer(error)) if error.code == ErrorCode::BlockNotFound => { continue diff --git a/crates/node/src/full/pending/provider.rs b/crates/node/src/full/pending/provider.rs index 22858b50c..eb6c11ac4 100644 --- a/crates/node/src/full/pending/provider.rs +++ b/crates/node/src/full/pending/provider.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use katana_gateway::types::TxTryFromError; +use katana_gateway_types::TxTryFromError; use katana_primitives::block::{GasPrices, PartialHeader, PendingBlock}; use katana_primitives::transaction::TxWithHash; use katana_primitives::Felt; diff --git a/crates/node/src/full/pending/state.rs b/crates/node/src/full/pending/state.rs index a331c3c93..316a4ecf1 100644 --- a/crates/node/src/full/pending/state.rs +++ b/crates/node/src/full/pending/state.rs @@ -1,4 +1,4 @@ -use katana_gateway::types::{ErrorCode, GatewayError}; +use katana_gateway_types::{ErrorCode, GatewayError}; use katana_primitives::block::BlockNumber; use katana_primitives::class::{ClassHash, CompiledClassHash, ContractClass}; use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; @@ -13,7 +13,7 @@ pub struct PreconfStateProvider { pub base: Box, pub preconf_block_id: Option, pub preconf_state_updates: Option, - pub gateway: katana_gateway::client::Client, + pub gateway: katana_gateway_client::Client, } impl StateProvider for PreconfStateProvider { @@ -80,7 +80,7 @@ impl ContractClassProvider for PreconfStateProvider { let result = runtime::Builder::new_current_thread() .build() .unwrap() - .block_on(self.gateway.get_class(hash, katana_gateway::types::BlockId::Pending)); + .block_on(self.gateway.get_class(hash, katana_gateway_types::BlockId::Pending)); match result { Ok(class) => { @@ -91,7 +91,7 @@ impl ContractClassProvider for PreconfStateProvider { } Err(error) => { - if let katana_gateway::client::Error::Sequencer(GatewayError { + if let katana_gateway_client::Error::Sequencer(GatewayError { code: ErrorCode::UndeclaredClass, .. }) = error diff --git a/crates/node/src/full/pool.rs b/crates/node/src/full/pool.rs index 46c3e2eea..52f2d504c 100644 --- a/crates/node/src/full/pool.rs +++ b/crates/node/src/full/pool.rs @@ -15,11 +15,11 @@ pub type FullNodePool = /// Any transaction validation is performed by the Starknet sequencer. #[derive(Debug)] pub struct GatewayProxyValidator { - gateway_client: katana_gateway::client::Client, + gateway_client: katana_gateway_client::Client, } impl GatewayProxyValidator { - pub fn new(gateway_client: katana_gateway::client::Client) -> Self { + pub fn new(gateway_client: katana_gateway_client::Client) -> Self { Self { gateway_client } } } From 9a3ebe4082b64a3079ab3c270564627acca51bf8 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 28 Oct 2025 12:04:53 -0400 Subject: [PATCH 32/49] integrate pending provider --- crates/node/src/full/pending/provider.rs | 2 +- crates/oracle/gas/src/lib.rs | 2 +- crates/oracle/gas/src/sampled/mod.rs | 8 ++++- crates/primitives/src/block.rs | 4 ++- crates/rpc/rpc/src/starknet/mod.rs | 37 +++++++++++++++++------- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/crates/node/src/full/pending/provider.rs b/crates/node/src/full/pending/provider.rs index eb6c11ac4..608a9f1e6 100644 --- a/crates/node/src/full/pending/provider.rs +++ b/crates/node/src/full/pending/provider.rs @@ -26,7 +26,7 @@ impl PendingBlockProvider for PreconfStateFacto number: 0, parent_hash: Felt::ZERO, }, - body: preconf_block + transactions: preconf_block .transactions .clone() .into_iter() diff --git a/crates/oracle/gas/src/lib.rs b/crates/oracle/gas/src/lib.rs index 8555d9bf0..1c034736f 100644 --- a/crates/oracle/gas/src/lib.rs +++ b/crates/oracle/gas/src/lib.rs @@ -17,7 +17,7 @@ pub use sampled::{SampledPriceOracle, Sampler}; use crate::sampled::starknet::{StarknetGatewaySampler, StarknetJsonRpcSampler}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum GasPriceOracle { Fixed(fixed::FixedPriceOracle), Sampled(sampled::SampledPriceOracle>), diff --git a/crates/oracle/gas/src/sampled/mod.rs b/crates/oracle/gas/src/sampled/mod.rs index 4b0f0a1e6..d85fd7399 100644 --- a/crates/oracle/gas/src/sampled/mod.rs +++ b/crates/oracle/gas/src/sampled/mod.rs @@ -23,11 +23,17 @@ pub trait Sampler: Debug + Send + Sync { fn sample(&self) -> BoxFuture<'_, anyhow::Result>; } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct SampledPriceOracle { inner: Arc>, } +impl Clone for SampledPriceOracle { + fn clone(&self) -> Self { + Self { inner: self.inner.clone() } + } +} + #[derive(Debug)] struct SampledPriceOracleInner { samples: Mutex, diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 1c20a8a06..3b4dac2c8 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -8,6 +8,7 @@ use starknet::macros::short_string; use crate::contract::ContractAddress; use crate::da::L1DataAvailabilityMode; +use crate::receipt::Receipt; use crate::transaction::{ExecutableTxWithHash, TxHash, TxWithHash}; use crate::version::StarknetVersion; use crate::Felt; @@ -121,7 +122,8 @@ pub struct PartialHeader { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PendingBlock { pub header: PartialHeader, - pub body: Vec, + pub transactions: Vec, + pub receipts: Vec, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index d90c2bd69..ac12ae673 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -48,11 +48,14 @@ use katana_rpc_types::trie::{ ClassesProof, ContractLeafData, ContractStorageKeys, ContractStorageProofs, ContractsProof, GetStorageProofResponse, GlobalRoots, Nodes, }; -use katana_rpc_types::{FeeEstimate, TxStatus}; +use katana_rpc_types::{ + FeeEstimate, PreConfirmedBlockWithReceipts, PreConfirmedBlockWithTxs, TxStatus, +}; use katana_rpc_types_builder::{BlockBuilder, ReceiptBuilder}; use katana_tasks::{Result as TaskResult, TaskSpawner}; use crate::permit::Permits; +use crate::starknet::pending::PendingBlockProvider2; use crate::utils::events::{Cursor, EventBlockId}; use crate::{utils, DEFAULT_ESTIMATE_FEE_MAX_CONCURRENT_REQUESTS}; @@ -341,7 +344,13 @@ where else { let num = provider.latest_number()?; let mut env = provider.block_env_at(num.into())?.expect("missing block env"); - self.inner.backend.update_block_env(&mut env); + + env.number += 1; + env.timestamp = get_current_timestamp().as_secs() as u64; + env.l2_gas_prices = self.inner.gas_oracle.l2_gas_prices(); + env.l1_gas_prices = self.inner.gas_oracle.l1_gas_prices(); + env.l1_data_gas_prices = self.inner.gas_oracle.l1_data_gas_prices(); + Some(env) } } @@ -680,9 +689,11 @@ where .build()? .map(MaybePreConfirmedBlock::Confirmed); - StarknetApiResult::Ok(block) - } else { - StarknetApiResult::Ok(None) + StarknetApiResult::Ok(block) + } else { + StarknetApiResult::Ok(None) + } + } } }) .await??; @@ -717,9 +728,11 @@ where .build_with_receipts()? .map(GetBlockWithReceiptsResponse::Block); - StarknetApiResult::Ok(block) - } else { - StarknetApiResult::Ok(None) + StarknetApiResult::Ok(block) + } else { + StarknetApiResult::Ok(None) + } + } } }) .await??; @@ -754,9 +767,11 @@ where .build_with_tx_hash()? .map(GetBlockWithTxHashesResponse::Block); - StarknetApiResult::Ok(block) - } else { - StarknetApiResult::Ok(None) + StarknetApiResult::Ok(block) + } else { + StarknetApiResult::Ok(None) + } + } } }) .await??; From 3a1814c9f6fbbecb5302c202940d40455fa7bd9e Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 28 Oct 2025 15:01:44 -0400 Subject: [PATCH 33/49] wip --- crates/node/src/full/pending/provider.rs | 131 +++++++++++++++++------ crates/node/src/lib.rs | 2 +- crates/rpc/rpc/src/starknet/mod.rs | 8 +- crates/rpc/rpc/src/starknet/pending.rs | 1 + 4 files changed, 101 insertions(+), 41 deletions(-) diff --git a/crates/node/src/full/pending/provider.rs b/crates/node/src/full/pending/provider.rs index 608a9f1e6..e1bbcde34 100644 --- a/crates/node/src/full/pending/provider.rs +++ b/crates/node/src/full/pending/provider.rs @@ -2,57 +2,120 @@ use std::fmt::Debug; use katana_gateway_types::TxTryFromError; use katana_primitives::block::{GasPrices, PartialHeader, PendingBlock}; -use katana_primitives::transaction::TxWithHash; +use katana_primitives::transaction::{TxHash, TxWithHash}; use katana_primitives::Felt; use katana_provider::api::state::{StateFactoryProvider, StateProvider}; -use katana_rpc::starknet::PendingBlockProvider; +use katana_rpc::starknet::{PendingBlockProvider, StarknetApiResult}; +use katana_rpc_types::PreConfirmedBlockWithTxs; use num_traits::ToPrimitive; use starknet::core::types::ResourcePrice; use crate::full::pending::PreconfStateFactory; impl PendingBlockProvider for PreconfStateFactory

{ - fn pending_block(&self) -> Option { - if let Some(preconf_block) = self.block() { - Some(PendingBlock { - header: PartialHeader { - l1_da_mode: preconf_block.l1_da_mode, - l1_gas_prices: to_gas_prices(preconf_block.l1_gas_price), - l2_gas_prices: to_gas_prices(preconf_block.l2_gas_price), - l1_data_gas_prices: to_gas_prices(preconf_block.l1_data_gas_price), - sequencer_address: preconf_block.sequencer_address, - starknet_version: preconf_block.starknet_version.try_into().unwrap(), - timestamp: preconf_block.timestamp, - number: 0, - parent_hash: Felt::ZERO, - }, - transactions: preconf_block - .transactions - .clone() - .into_iter() - .map(TxWithHash::try_from) - .collect::, TxTryFromError>>() - .unwrap(), - }) + fn get_pending_block_with_txs( + &self, + ) -> StarknetApiResult> { + if let Some(block) = self.block() { + let transactions = block + .transactions + .clone() + .into_iter() + .map(TxWithHash::try_from) + .collect::, TxTryFromError>>() + .unwrap(); + + Ok(Some(katana_rpc_types::PreConfirmedBlockWithTxs { + transactions, + block_number: 0, + l1_da_mode: block.l1_da_mode, + l1_gas_price: to_gas_prices(block.l1_gas_price), + l2_gas_price: to_gas_prices(block.l2_gas_price), + l1_data_gas_price: to_gas_prices(block.l1_data_gas_price), + sequencer_address: block.sequencer_address, + starknet_version: block.starknet_version, + timestamp: block.timestamp, + })) + } else { + Ok(None) + } + } + + fn get_pending_block_with_receipts( + &self, + ) -> StarknetApiResult> { + if let Some(block) = self.block() { + Ok(Some(katana_rpc_types::PreConfirmedBlockWithReceipts { + transactions: Vec::new(), + block_number: 0, + l1_da_mode: block.l1_da_mode, + l1_gas_price: to_gas_prices(block.l1_gas_price), + l2_gas_price: to_gas_prices(block.l2_gas_price), + l1_data_gas_price: to_gas_prices(block.l1_data_gas_price), + sequencer_address: block.sequencer_address, + starknet_version: block.starknet_version, + timestamp: block.timestamp, + })) + } else { + Ok(None) + } + } + + fn get_pending_block_with_tx_hashes( + &self, + ) -> StarknetApiResult> { + if let Some(block) = self.block() { + let transactions = block + .transactions + .clone() + .into_iter() + .map(|tx| tx.transaction_hash) + .collect::>(); + + Ok(Some(katana_rpc_types::PreConfirmedBlockWithTxHashes { + transactions, + block_number: 0, + l1_da_mode: block.l1_da_mode, + l1_gas_price: to_gas_prices(block.l1_gas_price), + l2_gas_price: to_gas_prices(block.l2_gas_price), + l1_data_gas_price: to_gas_prices(block.l1_data_gas_price), + sequencer_address: block.sequencer_address, + starknet_version: block.starknet_version, + timestamp: block.timestamp, + })) } else { - None + Ok(None) } } - fn pending_state_update(&self) -> Option { - self.state_updates() + fn get_pending_receipt( + &self, + hash: TxHash, + ) -> StarknetApiResult> { + Ok(None) + } + + fn get_pending_state_update( + &self, + ) -> StarknetApiResult> { + Ok(None) + } + + fn get_pending_transaction( + &self, + hash: TxHash, + ) -> StarknetApiResult> { + Ok(None) } - fn pending_transactions( + fn get_pending_transaction_by_index( &self, - ) -> Vec<( - katana_primitives::transaction::TxWithHash, - Option, - )> { - todo!() + hash: TxHash, + ) -> StarknetApiResult> { + Ok(None) } - fn pending_state(&self) -> Option> { + fn pending_state(&self) -> StarknetApiResult>> { Some(Box::new(self.state())) } } diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index cc9981897..011ec2554 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -1,4 +1,4 @@ -// #![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] pub mod full; diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index ac12ae673..f970ceb73 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -17,7 +17,6 @@ use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageVal use katana_primitives::env::BlockEnv; use katana_primitives::event::MaybeForkedContinuationToken; use katana_primitives::transaction::{ExecutableTxWithHash, TxHash, TxNumber}; -use katana_primitives::version::CURRENT_STARKNET_VERSION; use katana_primitives::Felt; use katana_provider::api::block::{BlockHashProvider, BlockIdReader, BlockNumberProvider}; use katana_provider::api::contract::ContractClassProvider; @@ -48,14 +47,11 @@ use katana_rpc_types::trie::{ ClassesProof, ContractLeafData, ContractStorageKeys, ContractStorageProofs, ContractsProof, GetStorageProofResponse, GlobalRoots, Nodes, }; -use katana_rpc_types::{ - FeeEstimate, PreConfirmedBlockWithReceipts, PreConfirmedBlockWithTxs, TxStatus, -}; +use katana_rpc_types::{FeeEstimate, TxStatus}; use katana_rpc_types_builder::{BlockBuilder, ReceiptBuilder}; use katana_tasks::{Result as TaskResult, TaskSpawner}; use crate::permit::Permits; -use crate::starknet::pending::PendingBlockProvider2; use crate::utils::events::{Cursor, EventBlockId}; use crate::{utils, DEFAULT_ESTIMATE_FEE_MAX_CONCURRENT_REQUESTS}; @@ -74,7 +70,7 @@ pub use config::StarknetApiConfig; use forking::ForkedClient; pub use pending::PendingBlockProvider; -type StarknetApiResult = Result; +pub type StarknetApiResult = Result; /// Handler for the Starknet JSON-RPC server. /// diff --git a/crates/rpc/rpc/src/starknet/pending.rs b/crates/rpc/rpc/src/starknet/pending.rs index 505c3add3..feeebbc4a 100644 --- a/crates/rpc/rpc/src/starknet/pending.rs +++ b/crates/rpc/rpc/src/starknet/pending.rs @@ -7,6 +7,7 @@ use katana_primitives::da::L1DataAvailabilityMode; use katana_primitives::execution::TypedTransactionExecutionInfo; use katana_primitives::transaction::{TxHash, TxNumber}; use katana_primitives::version::CURRENT_STARKNET_VERSION; +use katana_primitives::{block::PartialHeader, transaction::TxNumber}; use katana_provider::api::state::StateProvider; use katana_rpc_types::{ FinalityStatus, PreConfirmedBlockWithReceipts, PreConfirmedBlockWithTxHashes, From 971916f6b66a2055498626217843739d97dd72dd Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 28 Oct 2025 15:28:21 -0400 Subject: [PATCH 34/49] wip --- Cargo.lock | 1 + crates/node/src/full/pending/provider.rs | 43 ++++++++---------------- crates/primitives/Cargo.toml | 1 + crates/rpc/rpc/src/starknet/pending.rs | 1 - 4 files changed, 16 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 630dac0f0..d34fc28d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6405,6 +6405,7 @@ dependencies = [ "anyhow", "arbitrary", "assert_matches", + "bincode 1.3.3", "blockifier 0.0.0 (git+https://github.com/dojoengine/sequencer?rev=5d737b9c9)", "cainome-cairo-serde", "cairo-lang-starknet-classes", diff --git a/crates/node/src/full/pending/provider.rs b/crates/node/src/full/pending/provider.rs index e1bbcde34..de7aba7a9 100644 --- a/crates/node/src/full/pending/provider.rs +++ b/crates/node/src/full/pending/provider.rs @@ -1,14 +1,10 @@ use std::fmt::Debug; use katana_gateway_types::TxTryFromError; -use katana_primitives::block::{GasPrices, PartialHeader, PendingBlock}; -use katana_primitives::transaction::{TxHash, TxWithHash}; -use katana_primitives::Felt; +use katana_primitives::transaction::{TxHash, TxNumber, TxWithHash}; use katana_provider::api::state::{StateFactoryProvider, StateProvider}; use katana_rpc::starknet::{PendingBlockProvider, StarknetApiResult}; -use katana_rpc_types::PreConfirmedBlockWithTxs; -use num_traits::ToPrimitive; -use starknet::core::types::ResourcePrice; +use katana_rpc_types::RpcTxWithHash; use crate::full::pending::PreconfStateFactory; @@ -21,7 +17,7 @@ impl PendingBlockProvider for PreconfStateFacto .transactions .clone() .into_iter() - .map(TxWithHash::try_from) + .map(|tx| Ok(RpcTxWithHash::from(TxWithHash::try_from(tx)?))) .collect::, TxTryFromError>>() .unwrap(); @@ -29,9 +25,9 @@ impl PendingBlockProvider for PreconfStateFacto transactions, block_number: 0, l1_da_mode: block.l1_da_mode, - l1_gas_price: to_gas_prices(block.l1_gas_price), - l2_gas_price: to_gas_prices(block.l2_gas_price), - l1_data_gas_price: to_gas_prices(block.l1_data_gas_price), + l1_gas_price: block.l1_gas_price, + l2_gas_price: block.l2_gas_price, + l1_data_gas_price: block.l1_data_gas_price, sequencer_address: block.sequencer_address, starknet_version: block.starknet_version, timestamp: block.timestamp, @@ -49,9 +45,9 @@ impl PendingBlockProvider for PreconfStateFacto transactions: Vec::new(), block_number: 0, l1_da_mode: block.l1_da_mode, - l1_gas_price: to_gas_prices(block.l1_gas_price), - l2_gas_price: to_gas_prices(block.l2_gas_price), - l1_data_gas_price: to_gas_prices(block.l1_data_gas_price), + l1_gas_price: block.l1_gas_price, + l2_gas_price: block.l2_gas_price, + l1_data_gas_price: block.l1_data_gas_price, sequencer_address: block.sequencer_address, starknet_version: block.starknet_version, timestamp: block.timestamp, @@ -76,9 +72,9 @@ impl PendingBlockProvider for PreconfStateFacto transactions, block_number: 0, l1_da_mode: block.l1_da_mode, - l1_gas_price: to_gas_prices(block.l1_gas_price), - l2_gas_price: to_gas_prices(block.l2_gas_price), - l1_data_gas_price: to_gas_prices(block.l1_data_gas_price), + l1_gas_price: block.l1_gas_price, + l2_gas_price: block.l2_gas_price, + l1_data_gas_price: block.l1_data_gas_price, sequencer_address: block.sequencer_address, starknet_version: block.starknet_version, timestamp: block.timestamp, @@ -110,23 +106,12 @@ impl PendingBlockProvider for PreconfStateFacto fn get_pending_transaction_by_index( &self, - hash: TxHash, + index: TxNumber, ) -> StarknetApiResult> { Ok(None) } fn pending_state(&self) -> StarknetApiResult>> { - Some(Box::new(self.state())) + Ok(Some(Box::new(self.state()))) } } - -fn to_gas_prices(prices: ResourcePrice) -> GasPrices { - let eth = prices.price_in_wei.to_u128().expect("valid u128"); - let strk = prices.price_in_fri.to_u128().expect("valid u128"); - // older blocks might have zero gas prices (recent Starknet upgrade has made the minimum gas - // prices to 1) we may need to handle this case if we want to be able to compute the - // block hash correctly - let eth = if eth == 0 { 1 } else { eth }; - let strk = if strk == 0 { 1 } else { strk }; - unsafe { GasPrices::new_unchecked(eth, strk) } -} diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index c7fce089d..fd574b47d 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -33,6 +33,7 @@ strum_macros.workspace = true [dev-dependencies] assert_matches.workspace = true +bincode = "1.3" postcard.workspace = true rstest.workspace = true similar-asserts.workspace = true diff --git a/crates/rpc/rpc/src/starknet/pending.rs b/crates/rpc/rpc/src/starknet/pending.rs index feeebbc4a..505c3add3 100644 --- a/crates/rpc/rpc/src/starknet/pending.rs +++ b/crates/rpc/rpc/src/starknet/pending.rs @@ -7,7 +7,6 @@ use katana_primitives::da::L1DataAvailabilityMode; use katana_primitives::execution::TypedTransactionExecutionInfo; use katana_primitives::transaction::{TxHash, TxNumber}; use katana_primitives::version::CURRENT_STARKNET_VERSION; -use katana_primitives::{block::PartialHeader, transaction::TxNumber}; use katana_provider::api::state::StateProvider; use katana_rpc_types::{ FinalityStatus, PreConfirmedBlockWithReceipts, PreConfirmedBlockWithTxHashes, From fad6345b15a1c4fc8eae7da12a756f03bf86633d Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 28 Oct 2025 18:49:42 -0400 Subject: [PATCH 35/49] wip --- Cargo.lock | 2 ++ crates/gateway/gateway-server/Cargo.toml | 2 ++ crates/gateway/gateway-server/src/handlers.rs | 1 + crates/gateway/gateway-server/src/lib.rs | 1 + crates/node/src/full/pending/provider.rs | 12 ++++++- crates/rpc/rpc/src/starknet/mod.rs | 35 ++++++------------- crates/rpc/rpc/src/starknet/trace.rs | 4 +-- 7 files changed, 30 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d34fc28d4..280ec7279 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6177,6 +6177,8 @@ dependencies = [ "axum 0.7.9", "http 1.3.1", "http-body 1.0.1", + "katana-core", + "katana-executor", "katana-gateway-types", "katana-metrics", "katana-pool", diff --git a/crates/gateway/gateway-server/Cargo.toml b/crates/gateway/gateway-server/Cargo.toml index af6665959..845faff20 100644 --- a/crates/gateway/gateway-server/Cargo.toml +++ b/crates/gateway/gateway-server/Cargo.toml @@ -8,6 +8,8 @@ version.workspace = true [dependencies] katana-gateway-types.workspace = true katana-primitives.workspace = true +katana-executor.workspace = true +katana-core.workspace = true katana-metrics.workspace = true katana-pool.workspace = true katana-rpc.workspace = true diff --git a/crates/gateway/gateway-server/src/handlers.rs b/crates/gateway/gateway-server/src/handlers.rs index 888ba74a5..640639cea 100644 --- a/crates/gateway/gateway-server/src/handlers.rs +++ b/crates/gateway/gateway-server/src/handlers.rs @@ -2,6 +2,7 @@ use axum::extract::{Query, State}; use axum::http::StatusCode; use axum::response::{IntoResponse, Json, Response}; use katana_core::service::block_producer::BlockProducer; +use katana_executor::implementation::blockifier::BlockifierFactory; use katana_gateway_types::{ Block, ConfirmedReceipt, ConfirmedTransaction, ContractClass, ErrorCode, GatewayError, ReceiptBody, StateUpdate, StateUpdateWithBlock, diff --git a/crates/gateway/gateway-server/src/lib.rs b/crates/gateway/gateway-server/src/lib.rs index 1f8d7cd37..00862617f 100644 --- a/crates/gateway/gateway-server/src/lib.rs +++ b/crates/gateway/gateway-server/src/lib.rs @@ -5,6 +5,7 @@ use std::time::Duration; use axum::routing::get; use axum::Router; use katana_core::service::block_producer::BlockProducer; +use katana_executor::implementation::blockifier::BlockifierFactory; use katana_pool_api::TransactionPool; use katana_rpc::cors::Cors; use katana_rpc::starknet::StarknetApi; diff --git a/crates/node/src/full/pending/provider.rs b/crates/node/src/full/pending/provider.rs index de7aba7a9..031289bdc 100644 --- a/crates/node/src/full/pending/provider.rs +++ b/crates/node/src/full/pending/provider.rs @@ -8,7 +8,10 @@ use katana_rpc_types::RpcTxWithHash; use crate::full::pending::PreconfStateFactory; -impl PendingBlockProvider for PreconfStateFactory

{ +impl

PendingBlockProvider for PreconfStateFactory

+where + P: StateFactoryProvider + Debug + 'static, +{ fn get_pending_block_with_txs( &self, ) -> StarknetApiResult> { @@ -114,4 +117,11 @@ impl PendingBlockProvider for PreconfStateFacto fn pending_state(&self) -> StarknetApiResult>> { Ok(Some(Box::new(self.state()))) } + + fn get_pending_trace( + &self, + hash: TxHash, + ) -> StarknetApiResult> { + Ok(None) + } } diff --git a/crates/rpc/rpc/src/starknet/mod.rs b/crates/rpc/rpc/src/starknet/mod.rs index f970ceb73..4d33aa488 100644 --- a/crates/rpc/rpc/src/starknet/mod.rs +++ b/crates/rpc/rpc/src/starknet/mod.rs @@ -567,14 +567,7 @@ where { Result::<_, StarknetApiError>::Ok(pending_tx) } else { - let tx = this - .inner - .backend - .blockchain - .provider() - .transaction_by_hash(hash)? - .map(RpcTxWithHash::from); - + let tx = this.inner.storage.transaction_by_hash(hash)?.map(RpcTxWithHash::from); Result::<_, StarknetApiError>::Ok(tx) } }) @@ -597,7 +590,7 @@ where { StarknetApiResult::Ok(pending_receipt) } else { - let provider = this.inner.backend.blockchain.provider(); + let provider = &this.inner.storage; StarknetApiResult::Ok(ReceiptBuilder::new(hash, provider).build()?) } }) @@ -685,11 +678,9 @@ where .build()? .map(MaybePreConfirmedBlock::Confirmed); - StarknetApiResult::Ok(block) - } else { - StarknetApiResult::Ok(None) - } - } + StarknetApiResult::Ok(block) + } else { + StarknetApiResult::Ok(None) } }) .await??; @@ -724,11 +715,9 @@ where .build_with_receipts()? .map(GetBlockWithReceiptsResponse::Block); - StarknetApiResult::Ok(block) - } else { - StarknetApiResult::Ok(None) - } - } + StarknetApiResult::Ok(block) + } else { + StarknetApiResult::Ok(None) } }) .await??; @@ -763,11 +752,9 @@ where .build_with_tx_hash()? .map(GetBlockWithTxHashesResponse::Block); - StarknetApiResult::Ok(block) - } else { - StarknetApiResult::Ok(None) - } - } + StarknetApiResult::Ok(block) + } else { + StarknetApiResult::Ok(None) } }) .await??; diff --git a/crates/rpc/rpc/src/starknet/trace.rs b/crates/rpc/rpc/src/starknet/trace.rs index 9734d11cc..0f7f29814 100644 --- a/crates/rpc/rpc/src/starknet/trace.rs +++ b/crates/rpc/rpc/src/starknet/trace.rs @@ -135,8 +135,8 @@ where Ok(pending_trace) } else { // If not found in pending block, fallback to the provider - let provider = self.inner.backend.blockchain.provider(); - let trace = provider.transaction_execution(tx_hash)?.ok_or(TxnHashNotFound)?; + let trace = + self.inner.storage.transaction_execution(tx_hash)?.ok_or(TxnHashNotFound)?; Ok(TxTrace::from(trace)) } } From 0856d094e74c3fb42225fb3a42a6e46adfe3f077 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 28 Oct 2025 18:50:47 -0400 Subject: [PATCH 36/49] wip --- crates/node/src/full/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index 0af2fd317..3290f4a81 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -172,7 +172,7 @@ impl Node { pool.clone(), task_spawner.clone(), starknet_api_cfg, - Box::new(preconf_factory), + preconf_factory, GasPriceOracle::create_for_testing(), ); From d730845c8d38708eb718481621329be3797a764a Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 29 Oct 2025 12:22:42 -0400 Subject: [PATCH 37/49] wip --- crates/node/src/full/mod.rs | 11 ++++++++--- crates/rpc/rpc/src/starknet/write.rs | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index 3290f4a81..7cd467b7b 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -15,7 +15,7 @@ use katana_gateway_client::Client as SequencerGateway; use katana_metrics::exporters::prometheus::PrometheusRecorder; use katana_metrics::{Report, Server as MetricsServer}; use katana_pipeline::{Pipeline, PipelineHandle}; -use katana_pool::ordering::{FiFo, TipOrdering}; +use katana_pool::ordering::TipOrdering; use katana_provider::providers::db::DbProvider; use katana_provider::BlockchainProvider; use katana_rpc::cors::Cors; @@ -105,10 +105,15 @@ impl Node { // --- build gateway client + let gateway_client = match config.network { + Network::Mainnet => SequencerGateway::mainnet(), + Network::Sepolia => SequencerGateway::sepolia(), + }; + let gateway_client = if let Some(ref key) = config.gateway_api_key { - SequencerGateway::sepolia().with_api_key(key.clone()) + gateway_client.with_api_key(key.clone()) } else { - SequencerGateway::sepolia() + gateway_client }; // --- build transaction pool diff --git a/crates/rpc/rpc/src/starknet/write.rs b/crates/rpc/rpc/src/starknet/write.rs index e4b6615a7..691f1b810 100644 --- a/crates/rpc/rpc/src/starknet/write.rs +++ b/crates/rpc/rpc/src/starknet/write.rs @@ -9,6 +9,7 @@ use katana_rpc_types::broadcasted::{ }; use katana_rpc_types::{BroadcastedTx, BroadcastedTxWithChainId}; +use super::StarknetApi; use crate::starknet::pending::PendingBlockProvider; impl StarknetApi From 72902cf6660b4a72f84d3522d1ab1992f23580a6 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Fri, 31 Oct 2025 14:16:03 -0400 Subject: [PATCH 38/49] wip --- crates/node/src/full/pool.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/node/src/full/pool.rs b/crates/node/src/full/pool.rs index 52f2d504c..0dd553ec9 100644 --- a/crates/node/src/full/pool.rs +++ b/crates/node/src/full/pool.rs @@ -2,12 +2,13 @@ use std::future::Future; use katana_pool::ordering::TipOrdering; use katana_pool::pool::Pool; +use katana_pool_api::validation::Error as ValidationError; use katana_pool_api::validation::{ValidationOutcome, ValidationResult, Validator}; use katana_primitives::transaction::ExecutableTxWithHash; -use katana_rpc_types::BroadcastedTx; +use katana_rpc_types::{BroadcastedTx, BroadcastedTxWithChainId}; pub type FullNodePool = - Pool>; + Pool>; /// This is an implementation of the [`Validator`] trait that proxies incoming transactions to a /// Starknet sequencer via the gateway endpoint. @@ -25,7 +26,7 @@ impl GatewayProxyValidator { } impl Validator for GatewayProxyValidator { - type Transaction = ExecutableTxWithHash; + type Transaction = BroadcastedTxWithChainId; fn validate( &self, @@ -34,7 +35,7 @@ impl Validator for GatewayProxyValidator { let gateway_client = self.gateway_client.clone(); async move { - match BroadcastedTx::from(tx.transaction.clone()) { + match tx.tx.clone() { BroadcastedTx::Invoke(invoke_tx) => { gateway_client.add_invoke_transaction(invoke_tx).await.unwrap(); } From e332fc41cb98512003c295c29cabb5e020afa48c Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 10 Nov 2025 12:05:30 -0500 Subject: [PATCH 39/49] wip --- Cargo.lock | 71 ++++++++++++----------- crates/rpc/rpc-server/src/starknet/mod.rs | 1 - 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 280ec7279..c15685dcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5919,7 +5919,7 @@ dependencies = [ "katana-messaging", "katana-node", "katana-primitives", - "katana-rpc", + "katana-rpc-server", "katana-slot-controller", "katana-tracing", "katana-utils", @@ -6157,7 +6157,6 @@ dependencies = [ "clap", "katana-gateway-types", "katana-primitives", - "katana-rpc-types", "reqwest 0.12.22", "rstest 0.18.2", "serde", @@ -6185,8 +6184,8 @@ dependencies = [ "katana-pool-api", "katana-primitives", "katana-provider-api", - "katana-rpc", "katana-rpc-api", + "katana-rpc-server", "metrics 0.23.1", "serde", "serde-utils", @@ -6203,6 +6202,9 @@ dependencies = [ name = "katana-gateway-types" version = "1.7.0" dependencies = [ + "base64 0.21.7", + "cairo-lang-starknet-classes", + "flate2", "katana-primitives", "katana-rpc-api", "katana-rpc-types", @@ -6311,9 +6313,9 @@ dependencies = [ "katana-pool-api", "katana-primitives", "katana-provider", - "katana-rpc", "katana-rpc-api", "katana-rpc-client", + "katana-rpc-server", "katana-rpc-types", "katana-stage", "katana-starknet", @@ -6482,7 +6484,35 @@ dependencies = [ ] [[package]] -name = "katana-rpc" +name = "katana-rpc-api" +version = "1.7.0" +dependencies = [ + "anyhow", + "jsonrpsee", + "katana-pool-api", + "katana-primitives", + "katana-provider-api", + "katana-rpc-types", + "rstest 0.18.2", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "katana-rpc-client" +version = "1.7.0" +dependencies = [ + "jsonrpsee", + "katana-primitives", + "katana-rpc-api", + "katana-rpc-types", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "katana-rpc-server" version = "1.7.0" dependencies = [ "alloy-contract", @@ -6539,34 +6569,6 @@ dependencies = [ "url", ] -[[package]] -name = "katana-rpc-api" -version = "1.7.0" -dependencies = [ - "anyhow", - "jsonrpsee", - "katana-pool-api", - "katana-primitives", - "katana-provider-api", - "katana-rpc-types", - "rstest 0.18.2", - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "katana-rpc-client" -version = "1.7.0" -dependencies = [ - "jsonrpsee", - "katana-primitives", - "katana-rpc-api", - "katana-rpc-types", - "thiserror 1.0.69", - "tokio", -] - [[package]] name = "katana-rpc-types" version = "1.7.0" @@ -6576,6 +6578,7 @@ dependencies = [ "cainome-cairo-serde", "cairo-lang-starknet-classes", "cairo-lang-utils", + "derive_more 0.99.20", "flate2", "katana-genesis", "katana-primitives", @@ -6744,9 +6747,9 @@ dependencies = [ "katana-node", "katana-primitives", "katana-provider", - "katana-rpc", "katana-rpc-api", "katana-rpc-client", + "katana-rpc-server", "katana-rpc-types", "katana-utils-macro", "rand 0.8.5", diff --git a/crates/rpc/rpc-server/src/starknet/mod.rs b/crates/rpc/rpc-server/src/starknet/mod.rs index 4d33aa488..f92b1c942 100644 --- a/crates/rpc/rpc-server/src/starknet/mod.rs +++ b/crates/rpc/rpc-server/src/starknet/mod.rs @@ -95,7 +95,6 @@ where { pool: Pool, chain_spec: Arc, - pool: Pool, gas_oracle: GasPriceOracle, storage: BlockchainProvider>, forked_client: Option, From 206e609a068b7dc0ef9590ee1b20d6538fb730fb Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 10 Nov 2025 12:19:49 -0500 Subject: [PATCH 40/49] implement PoolTransaction for BroadcastedTxWithChainId --- Cargo.lock | 1 + crates/gateway/gateway-server/src/lib.rs | 4 +-- crates/node/src/full/pool.rs | 29 +++++++++------- crates/rpc/rpc-server/src/starknet/list.rs | 4 +-- crates/rpc/rpc-types/Cargo.toml | 1 + crates/rpc/rpc-types/src/broadcasted.rs | 40 ++++++++++++++++++++-- 6 files changed, 60 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c15685dcb..6984bb650 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6581,6 +6581,7 @@ dependencies = [ "derive_more 0.99.20", "flate2", "katana-genesis", + "katana-pool-api", "katana-primitives", "katana-trie", "rstest 0.18.2", diff --git a/crates/gateway/gateway-server/src/lib.rs b/crates/gateway/gateway-server/src/lib.rs index 49a93a099..4283bde3f 100644 --- a/crates/gateway/gateway-server/src/lib.rs +++ b/crates/gateway/gateway-server/src/lib.rs @@ -84,9 +84,7 @@ where Pool: TransactionPool + Clone + Send + Sync + 'static, { /// Create a new feeder gateway server. - pub fn new( - starknet_api: StarknetApi>, - ) -> Self { + pub fn new(starknet_api: StarknetApi>) -> Self { Self { timeout: DEFAULT_GATEWAY_TIMEOUT, cors: None, diff --git a/crates/node/src/full/pool.rs b/crates/node/src/full/pool.rs index 0dd553ec9..aca0b1862 100644 --- a/crates/node/src/full/pool.rs +++ b/crates/node/src/full/pool.rs @@ -2,9 +2,9 @@ use std::future::Future; use katana_pool::ordering::TipOrdering; use katana_pool::pool::Pool; -use katana_pool_api::validation::Error as ValidationError; -use katana_pool_api::validation::{ValidationOutcome, ValidationResult, Validator}; -use katana_primitives::transaction::ExecutableTxWithHash; +use katana_pool_api::validation::{ + Error as ValidationError, ValidationOutcome, ValidationResult, Validator, +}; use katana_rpc_types::{BroadcastedTx, BroadcastedTxWithChainId}; pub type FullNodePool = @@ -35,19 +35,24 @@ impl Validator for GatewayProxyValidator { let gateway_client = self.gateway_client.clone(); async move { - match tx.tx.clone() { - BroadcastedTx::Invoke(invoke_tx) => { - gateway_client.add_invoke_transaction(invoke_tx).await.unwrap(); + let hash = tx.calculate_hash(); + + let result = match tx.tx.clone() { + BroadcastedTx::Invoke(inner_tx) => { + gateway_client.add_invoke_transaction(inner_tx.into()).await.map(|_| ()) } - BroadcastedTx::Declare(declare_tx) => { - gateway_client.add_declare_transaction(declare_tx).await.unwrap(); + BroadcastedTx::Declare(inner_tx) => { + gateway_client.add_declare_transaction(inner_tx.into()).await.map(|_| ()) } - BroadcastedTx::DeployAccount(deploy_account_tx) => { - gateway_client.add_deploy_account_transaction(deploy_account_tx).await.unwrap(); + BroadcastedTx::DeployAccount(inner_tx) => { + gateway_client.add_deploy_account_transaction(inner_tx.into()).await.map(|_| ()) } - } + }; - ValidationResult::Ok(ValidationOutcome::Valid(tx)) + match result { + Ok(_) => ValidationResult::Ok(ValidationOutcome::Valid(tx)), + Err(e) => ValidationResult::Err(ValidationError::new(hash, Box::new(e))), + } } } } diff --git a/crates/rpc/rpc-server/src/starknet/list.rs b/crates/rpc/rpc-server/src/starknet/list.rs index ea346f479..d52232346 100644 --- a/crates/rpc/rpc-server/src/starknet/list.rs +++ b/crates/rpc/rpc-server/src/starknet/list.rs @@ -13,9 +13,9 @@ use crate::starknet::pending::PendingBlockProvider; #[async_trait] impl StarknetApiExtServer for StarknetApi -where +where Pool: TransactionPool + 'static, - PP: PendingBlockProvider + PP: PendingBlockProvider, { async fn get_blocks(&self, request: GetBlocksRequest) -> RpcResult { Ok(self.blocks(request).await?) diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index f570a53aa..e454c69eb 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -9,6 +9,7 @@ version.workspace = true [dependencies] katana-primitives = { workspace = true, features = [ "serde" ] } katana-genesis.workspace = true +katana-pool-api.workspace = true katana-trie.workspace = true serde-utils.workspace = true diff --git a/crates/rpc/rpc-types/src/broadcasted.rs b/crates/rpc/rpc-types/src/broadcasted.rs index c1cd0d737..470710d18 100644 --- a/crates/rpc/rpc-types/src/broadcasted.rs +++ b/crates/rpc/rpc-types/src/broadcasted.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use katana_pool_api::PoolTransaction; use katana_primitives::chain::ChainId; use katana_primitives::class::{ ClassHash, CompiledClassHash, ComputeClassHashError, ContractClass, SierraContractClass, @@ -11,8 +12,8 @@ use katana_primitives::fee::{ }; use katana_primitives::transaction::{ DeclareTx, DeclareTxV0, DeclareTxV1, DeclareTxV2, DeclareTxV3, DeclareTxWithClass, - DeployAccountTx, DeployAccountTxV1, DeployAccountTxV3, ExecutableTx, - ExecutableTxWithHash, InvokeTx, InvokeTxV0, InvokeTxV1, InvokeTxV3, TxHash, TxType, + DeployAccountTx, DeployAccountTxV1, DeployAccountTxV3, ExecutableTx, ExecutableTxWithHash, + InvokeTx, InvokeTxV0, InvokeTxV1, InvokeTxV3, TxHash, TxType, }; use katana_primitives::utils::get_contract_address; use katana_primitives::{ContractAddress, Felt}; @@ -706,6 +707,41 @@ impl BroadcastedTxWithChainId { } } +impl PoolTransaction for BroadcastedTxWithChainId { + fn hash(&self) -> TxHash { + self.calculate_hash() + } + + fn nonce(&self) -> Nonce { + match &self.tx { + BroadcastedTx::Invoke(tx) => tx.nonce, + BroadcastedTx::Declare(tx) => tx.nonce, + BroadcastedTx::DeployAccount(tx) => tx.nonce, + } + } + + fn sender(&self) -> ContractAddress { + match &self.tx { + BroadcastedTx::Invoke(tx) => tx.sender_address, + BroadcastedTx::Declare(tx) => tx.sender_address, + BroadcastedTx::DeployAccount(tx) => tx.contract_address(), + } + } + + fn max_fee(&self) -> u128 { + // V3 transactions don't have max_fee, they use resource bounds instead + 0 + } + + fn tip(&self) -> u64 { + match &self.tx { + BroadcastedTx::Invoke(tx) => tx.tip.into(), + BroadcastedTx::Declare(tx) => tx.tip.into(), + BroadcastedTx::DeployAccount(tx) => tx.tip.into(), + } + } +} + /// A broadcasted transaction. #[derive(Debug, Clone, Serialize)] #[serde(untagged)] From c73d9da5c332e57c35b7993714b3fd1902ed4a9e Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 10 Nov 2025 12:21:24 -0500 Subject: [PATCH 41/49] wip --- crates/node/src/full/mod.rs | 6 +++--- crates/node/src/full/pending/provider.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index 7cd467b7b..2ceb52506 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -18,10 +18,10 @@ use katana_pipeline::{Pipeline, PipelineHandle}; use katana_pool::ordering::TipOrdering; use katana_provider::providers::db::DbProvider; use katana_provider::BlockchainProvider; -use katana_rpc::cors::Cors; -use katana_rpc::starknet::{StarknetApi, StarknetApiConfig}; -use katana_rpc::{RpcServer, RpcServerHandle}; use katana_rpc_api::starknet::{StarknetApiServer, StarknetTraceApiServer, StarknetWriteApiServer}; +use katana_rpc_server::cors::Cors; +use katana_rpc_server::starknet::{StarknetApi, StarknetApiConfig}; +use katana_rpc_server::{RpcServer, RpcServerHandle}; use katana_stage::blocks::BatchBlockDownloader; use katana_stage::{Blocks, Classes, StateTrie}; use katana_tasks::TaskManager; diff --git a/crates/node/src/full/pending/provider.rs b/crates/node/src/full/pending/provider.rs index 031289bdc..8a5945639 100644 --- a/crates/node/src/full/pending/provider.rs +++ b/crates/node/src/full/pending/provider.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use katana_gateway_types::TxTryFromError; use katana_primitives::transaction::{TxHash, TxNumber, TxWithHash}; use katana_provider::api::state::{StateFactoryProvider, StateProvider}; -use katana_rpc::starknet::{PendingBlockProvider, StarknetApiResult}; +use katana_rpc_server::starknet::{PendingBlockProvider, StarknetApiResult}; use katana_rpc_types::RpcTxWithHash; use crate::full::pending::PreconfStateFactory; From 67c9947ed112f8240ba67331019c8ed910623e0c Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 10 Nov 2025 12:23:35 -0500 Subject: [PATCH 42/49] remove unused type --- crates/primitives/src/block.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 3b4dac2c8..15866a6ea 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -8,7 +8,6 @@ use starknet::macros::short_string; use crate::contract::ContractAddress; use crate::da::L1DataAvailabilityMode; -use crate::receipt::Receipt; use crate::transaction::{ExecutableTxWithHash, TxHash, TxWithHash}; use crate::version::StarknetVersion; use crate::Felt; @@ -117,15 +116,6 @@ pub struct PartialHeader { pub starknet_version: StarknetVersion, } -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct PendingBlock { - pub header: PartialHeader, - pub transactions: Vec, - pub receipts: Vec, -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] From c477ab065979226b5930afe923cc6fcd17effa96 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 10 Nov 2025 15:22:42 -0500 Subject: [PATCH 43/49] verioned constant overrides refactor --- bin/katana/src/cli/init/mod.rs | 18 +---- crates/chain-spec/src/dev.rs | 5 -- crates/chain-spec/src/full_node.rs | 65 +++++++++++++++++ crates/chain-spec/src/lib.rs | 30 ++++++-- crates/chain-spec/src/rollup/file.rs | 2 - crates/chain-spec/src/rollup/mod.rs | 3 - crates/cli/src/utils.rs | 2 + crates/core/src/backend/mod.rs | 5 ++ crates/core/src/service/block_producer.rs | 4 +- crates/core/tests/backend.rs | 18 ++--- crates/executor/benches/utils.rs | 6 +- crates/executor/src/abstraction/executor.rs | 2 +- .../src/implementation/blockifier/mod.rs | 25 ++++--- .../src/implementation/blockifier/utils.rs | 73 ++++++++++++------- crates/executor/src/implementation/noop.rs | 6 +- crates/executor/tests/fixtures/mod.rs | 7 +- crates/node/src/full/mod.rs | 8 +- crates/node/src/lib.rs | 33 ++++----- crates/pool/pool/src/validation/stateful.rs | 7 +- crates/primitives/src/env.rs | 10 +-- .../rpc/rpc-server/src/starknet/blockifier.rs | 12 +-- crates/rpc/rpc-server/src/starknet/mod.rs | 18 +++-- crates/rpc/rpc-server/src/starknet/read.rs | 2 +- crates/rpc/rpc-server/src/starknet/trace.rs | 2 +- crates/rpc/rpc-types/src/broadcasted.rs | 4 +- crates/sync/stage/src/classes.rs | 2 +- 26 files changed, 230 insertions(+), 139 deletions(-) create mode 100644 crates/chain-spec/src/full_node.rs diff --git a/bin/katana/src/cli/init/mod.rs b/bin/katana/src/cli/init/mod.rs index 197ea8ba9..a8ffd7619 100644 --- a/bin/katana/src/cli/init/mod.rs +++ b/bin/katana/src/cli/init/mod.rs @@ -247,14 +247,7 @@ impl RollupArgs { // At the moment, the fee token is limited to a predefined token. let fee_contracts = FeeContracts::default(); - let versioned_constants_overrides = None; - let chain_spec = rollup::ChainSpec { - id, - genesis, - settlement, - fee_contracts, - versioned_constants_overrides, - }; + let chain_spec = rollup::ChainSpec { id, genesis, settlement, fee_contracts }; if let Some(path) = self.output_path { let dir = ChainConfigDir::create(path)?; @@ -392,14 +385,7 @@ impl SovereignArgs { // At the moment, the fee token is limited to a predefined token. let fee_contracts = FeeContracts::default(); - let versioned_constants_overrides = None; - let chain_spec = rollup::ChainSpec { - id, - genesis, - settlement, - fee_contracts, - versioned_constants_overrides, - }; + let chain_spec = rollup::ChainSpec { id, genesis, settlement, fee_contracts }; if let Some(path) = self.output_path { let dir = ChainConfigDir::create(path)?; diff --git a/crates/chain-spec/src/dev.rs b/crates/chain-spec/src/dev.rs index 4bd8cb8ad..f764dc4df 100644 --- a/crates/chain-spec/src/dev.rs +++ b/crates/chain-spec/src/dev.rs @@ -15,7 +15,6 @@ use katana_primitives::chain::ChainId; use katana_primitives::class::ClassHash; use katana_primitives::contract::ContractAddress; use katana_primitives::da::L1DataAvailabilityMode; -use katana_primitives::env::VersionedConstantsOverrides; use katana_primitives::state::StateUpdatesWithClasses; use katana_primitives::utils::split_u256; use katana_primitives::version::StarknetVersion; @@ -37,8 +36,6 @@ pub struct ChainSpec { pub fee_contracts: FeeContracts, pub settlement: Option, - - pub versioned_constants_overrides: Option, } ////////////////////////////////////////////////////////////// @@ -140,7 +137,6 @@ lazy_static! { genesis, fee_contracts, settlement: None, - versioned_constants_overrides: None, } }; } @@ -328,7 +324,6 @@ mod tests { strk: DEFAULT_STRK_FEE_TOKEN_ADDRESS, }, settlement: None, - versioned_constants_overrides: None, }; // setup expected storage values diff --git a/crates/chain-spec/src/full_node.rs b/crates/chain-spec/src/full_node.rs new file mode 100644 index 000000000..3beec702c --- /dev/null +++ b/crates/chain-spec/src/full_node.rs @@ -0,0 +1,65 @@ +use katana_genesis::Genesis; +use katana_primitives::chain::ChainId; +use lazy_static::lazy_static; + +use crate::{FeeContracts, SettlementLayer}; + +/// The full node chain specification. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ChainSpec { + /// The network chain id. + pub id: ChainId, + + /// The chain's genesis states. + pub genesis: Genesis, + + /// The chain fee token contract. + pub fee_contracts: FeeContracts, + + /// The chain's settlement layer configurations (if any). + pub settlement: Option, +} + +////////////////////////////////////////////////////////////// +// ChainSpec implementations +////////////////////////////////////////////////////////////// + +impl ChainSpec { + /// Creates a new [`ChainSpec`] for Starknet mainnet. + pub fn mainnet() -> Self { + MAINNET.clone() + } + + /// Creates a new [`ChainSpec`] for Starknet sepolia testnet. + pub fn sepolia() -> Self { + SEPOLIA.clone() + } +} + +////////////////////////////////////////////////////////////// +// Predefined ChainSpec instances +////////////////////////////////////////////////////////////// + +lazy_static! { + /// Starknet mainnet chain specification. + pub static ref MAINNET: ChainSpec = ChainSpec { + id: ChainId::MAINNET, + genesis: Genesis::default(), + fee_contracts: FeeContracts { + eth: katana_genesis::constant::DEFAULT_ETH_FEE_TOKEN_ADDRESS, + strk: katana_genesis::constant::DEFAULT_STRK_FEE_TOKEN_ADDRESS, + }, + settlement: None, + }; + + /// Starknet sepolia testnet chain specification. + pub static ref SEPOLIA: ChainSpec = ChainSpec { + id: ChainId::SEPOLIA, + genesis: Genesis::default(), + fee_contracts: FeeContracts { + eth: katana_genesis::constant::DEFAULT_ETH_FEE_TOKEN_ADDRESS, + strk: katana_genesis::constant::DEFAULT_STRK_FEE_TOKEN_ADDRESS, + }, + settlement: None, + }; +} diff --git a/crates/chain-spec/src/lib.rs b/crates/chain-spec/src/lib.rs index cfdada364..6a68a7aec 100644 --- a/crates/chain-spec/src/lib.rs +++ b/crates/chain-spec/src/lib.rs @@ -1,18 +1,19 @@ use katana_genesis::Genesis; use katana_primitives::block::BlockNumber; use katana_primitives::chain::ChainId; -use katana_primitives::env::VersionedConstantsOverrides; use katana_primitives::{eth, ContractAddress}; use serde::{Deserialize, Serialize}; use url::Url; pub mod dev; +pub mod full_node; pub mod rollup; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ChainSpec { Dev(dev::ChainSpec), Rollup(rollup::ChainSpec), + FullNode(full_node::ChainSpec), } ////////////////////////////////////////////////////////////// @@ -25,10 +26,21 @@ impl ChainSpec { Self::Dev(dev::DEV.clone()) } + /// Creates a new [`ChainSpec`] for Starknet mainnet. + pub fn mainnet() -> Self { + Self::FullNode(full_node::ChainSpec::mainnet()) + } + + /// Creates a new [`ChainSpec`] for Starknet sepolia testnet. + pub fn sepolia() -> Self { + Self::FullNode(full_node::ChainSpec::sepolia()) + } + pub fn id(&self) -> ChainId { match self { Self::Dev(spec) => spec.id, Self::Rollup(spec) => spec.id, + Self::FullNode(spec) => spec.id, } } @@ -36,6 +48,7 @@ impl ChainSpec { match self { Self::Dev(spec) => &spec.genesis, Self::Rollup(spec) => &spec.genesis, + Self::FullNode(spec) => &spec.genesis, } } @@ -43,6 +56,7 @@ impl ChainSpec { match self { Self::Dev(spec) => spec.settlement.as_ref(), Self::Rollup(spec) => Some(&spec.settlement), + Self::FullNode(spec) => spec.settlement.as_ref(), } } @@ -50,13 +64,7 @@ impl ChainSpec { match self { Self::Dev(spec) => &spec.fee_contracts, Self::Rollup(spec) => &spec.fee_contracts, - } - } - - pub fn versioned_constants_overrides(&self) -> Option<&VersionedConstantsOverrides> { - match self { - Self::Dev(spec) => spec.versioned_constants_overrides.as_ref(), - Self::Rollup(spec) => spec.versioned_constants_overrides.as_ref(), + Self::FullNode(spec) => &spec.fee_contracts, } } } @@ -73,6 +81,12 @@ impl From for ChainSpec { } } +impl From for ChainSpec { + fn from(spec: full_node::ChainSpec) -> Self { + Self::FullNode(spec) + } +} + impl Default for ChainSpec { fn default() -> Self { Self::dev() diff --git a/crates/chain-spec/src/rollup/file.rs b/crates/chain-spec/src/rollup/file.rs index 64f862a7f..f49e80140 100644 --- a/crates/chain-spec/src/rollup/file.rs +++ b/crates/chain-spec/src/rollup/file.rs @@ -87,7 +87,6 @@ pub fn read(dir: &ChainConfigDir) -> Result { id: chain_spec.id, settlement: chain_spec.settlement, fee_contracts: chain_spec.fee_contract.into(), - versioned_constants_overrides: None, }) } @@ -355,7 +354,6 @@ mod tests { eth: ContractAddress::default(), strk: ContractAddress::default(), }, - versioned_constants_overrides: None, settlement: SettlementLayer::Starknet { block: 0, id: ChainId::default(), diff --git a/crates/chain-spec/src/rollup/mod.rs b/crates/chain-spec/src/rollup/mod.rs index 1b03d47dd..d2229734f 100644 --- a/crates/chain-spec/src/rollup/mod.rs +++ b/crates/chain-spec/src/rollup/mod.rs @@ -2,7 +2,6 @@ use katana_genesis::Genesis; use katana_primitives::block::{ExecutableBlock, GasPrices, PartialHeader}; use katana_primitives::chain::ChainId; use katana_primitives::da::L1DataAvailabilityMode; -use katana_primitives::env::VersionedConstantsOverrides; use katana_primitives::version::CURRENT_STARKNET_VERSION; mod file; @@ -27,8 +26,6 @@ pub struct ChainSpec { /// The chain's settlement layer configurations. pub settlement: SettlementLayer, - - pub versioned_constants_overrides: Option, } ////////////////////////////////////////////////////////////// diff --git a/crates/cli/src/utils.rs b/crates/cli/src/utils.rs index 94aa22f2b..5004bd772 100644 --- a/crates/cli/src/utils.rs +++ b/crates/cli/src/utils.rs @@ -130,6 +130,8 @@ PREDEPLOYED CONTRACTS cs.fee_contracts.strk, DEFAULT_LEGACY_ERC20_CLASS_HASH, ); } + + ChainSpec::FullNode(..) => {} } println!( diff --git a/crates/core/src/backend/mod.rs b/crates/core/src/backend/mod.rs index a26234241..2d9b7c2ab 100644 --- a/crates/core/src/backend/mod.rs +++ b/crates/core/src/backend/mod.rs @@ -76,6 +76,11 @@ impl Backend { match self.chain_spec.as_ref() { ChainSpec::Dev(cs) => self.init_dev_genesis(cs), ChainSpec::Rollup(cs) => self.init_rollup_genesis(cs), + ChainSpec::FullNode(_) => { + // Full nodes sync from the network, so we skip genesis initialization + info!("Full node mode: genesis initialization skipped, will sync from network"); + Ok(()) + } } } diff --git a/crates/core/src/service/block_producer.rs b/crates/core/src/service/block_producer.rs index 4d5882d88..d87985f4c 100644 --- a/crates/core/src/service/block_producer.rs +++ b/crates/core/src/service/block_producer.rs @@ -263,7 +263,7 @@ impl IntervalBlockProducer { let validator = TxValidator::new( state, flags.clone(), - cfg.clone(), + cfg.cloned(), block_env, permit.clone(), backend.chain_spec.clone(), @@ -591,7 +591,7 @@ impl InstantBlockProducer { let validator = TxValidator::new( state, flags.clone(), - cfg.clone(), + cfg.cloned(), block_env, permit.clone(), backend.chain_spec.clone(), diff --git a/crates/core/tests/backend.rs b/crates/core/tests/backend.rs index e22ec3224..9546bdcd0 100644 --- a/crates/core/tests/backend.rs +++ b/crates/core/tests/backend.rs @@ -21,11 +21,11 @@ use url::Url; fn executor(chain_spec: Arc) -> BlockifierFactory { BlockifierFactory::new( - VersionedConstantsOverrides { - validate_max_n_steps: u32::MAX, - invoke_tx_max_n_steps: u32::MAX, - max_recursion_depth: usize::MAX, - }, + Some(VersionedConstantsOverrides { + validate_max_n_steps: Some(u32::MAX), + invoke_tx_max_n_steps: Some(u32::MAX), + max_recursion_depth: Some(usize::MAX), + }), Default::default(), BlockLimits::default(), ClassCache::new().unwrap(), @@ -72,13 +72,7 @@ fn rollup_chain_spec() -> rollup::ChainSpec { rpc_url: Url::parse("http://localhost:5050").unwrap(), }; - rollup::ChainSpec { - id, - genesis, - settlement, - fee_contracts, - versioned_constants_overrides: None, - } + rollup::ChainSpec { id, genesis, settlement, fee_contracts } } #[rstest] diff --git a/crates/executor/benches/utils.rs b/crates/executor/benches/utils.rs index 0636e14fa..5618cd6be 100644 --- a/crates/executor/benches/utils.rs +++ b/crates/executor/benches/utils.rs @@ -30,9 +30,9 @@ pub fn envs() -> (BlockEnv, VersionedConstantsOverrides) { ..Default::default() }; let cfg = VersionedConstantsOverrides { - max_recursion_depth: 100, - validate_max_n_steps: 4_000_000, - invoke_tx_max_n_steps: 4_000_000, + max_recursion_depth: Some(100), + validate_max_n_steps: Some(4_000_000), + invoke_tx_max_n_steps: Some(4_000_000), // fee_token_addresses: FeeTokenAddressses { // eth: DEFAULT_ETH_FEE_TOKEN_ADDRESS, // strk: DEFAULT_ETH_FEE_TOKEN_ADDRESS, diff --git a/crates/executor/src/abstraction/executor.rs b/crates/executor/src/abstraction/executor.rs index c9a0df567..96f7137dd 100644 --- a/crates/executor/src/abstraction/executor.rs +++ b/crates/executor/src/abstraction/executor.rs @@ -23,7 +23,7 @@ pub trait ExecutorFactory: Send + Sync + 'static + core::fmt::Debug { P: StateProvider + 'a; /// Returns the configuration environment of the factory. - fn cfg(&self) -> &VersionedConstantsOverrides; + fn cfg(&self) -> Option<&VersionedConstantsOverrides>; /// Returns the execution flags set by the factory. fn execution_flags(&self) -> &ExecutionFlags; diff --git a/crates/executor/src/implementation/blockifier/mod.rs b/crates/executor/src/implementation/blockifier/mod.rs index e9402bf25..e2b9db97f 100644 --- a/crates/executor/src/implementation/blockifier/mod.rs +++ b/crates/executor/src/implementation/blockifier/mod.rs @@ -35,7 +35,7 @@ pub(crate) const LOG_TARGET: &str = "katana::executor::blockifier"; #[derive(Debug)] pub struct BlockifierFactory { - cfg: VersionedConstantsOverrides, + cfg: Option, flags: ExecutionFlags, limits: BlockLimits, class_cache: ClassCache, @@ -45,7 +45,7 @@ pub struct BlockifierFactory { impl BlockifierFactory { /// Create a new factory with the given configuration and simulation flags. pub fn new( - cfg: VersionedConstantsOverrides, + cfg: Option, flags: ExecutionFlags, limits: BlockLimits, class_cache: ClassCache, @@ -85,8 +85,8 @@ impl ExecutorFactory for BlockifierFactory { )) } - fn cfg(&self) -> &VersionedConstantsOverrides { - &self.cfg + fn cfg(&self) -> Option<&VersionedConstantsOverrides> { + self.cfg.as_ref() } /// Returns the execution flags set by the factory. @@ -104,22 +104,25 @@ pub struct StarknetVMProcessor<'a> { stats: ExecutionStats, bouncer: Bouncer, starknet_version: StarknetVersion, - cfg_env: VersionedConstantsOverrides, + cfg_env: Option, } impl<'a> StarknetVMProcessor<'a> { pub fn new( state: impl StateProvider + 'a, block_env: BlockEnv, - cfg_env: VersionedConstantsOverrides, + cfg_env: Option, simulation_flags: ExecutionFlags, limits: BlockLimits, class_cache: ClassCache, chain_spec: Arc, ) -> Self { let transactions = Vec::new(); - let block_context = - Arc::new(utils::block_context_from_envs(chain_spec.as_ref(), &block_env, &cfg_env)); + let block_context = Arc::new(utils::block_context_from_envs( + chain_spec.as_ref(), + &block_env, + cfg_env.as_ref(), + )); let state = state::CachedState::new(state, class_cache); @@ -196,7 +199,11 @@ impl<'a> StarknetVMProcessor<'a> { let sn_version = header.starknet_version.try_into().expect("valid version"); let mut versioned_constants = VersionedConstants::get(&sn_version).unwrap().clone(); - apply_versioned_constant_overrides(&self.cfg_env, &mut versioned_constants); + + // Only apply overrides if provided + if let Some(ref cfg) = self.cfg_env { + apply_versioned_constant_overrides(cfg, &mut versioned_constants); + } self.starknet_version = header.starknet_version; self.block_context = Arc::new(BlockContext::new( diff --git a/crates/executor/src/implementation/blockifier/utils.rs b/crates/executor/src/implementation/blockifier/utils.rs index 265d63495..6272ef737 100644 --- a/crates/executor/src/implementation/blockifier/utils.rs +++ b/crates/executor/src/implementation/blockifier/utils.rs @@ -433,7 +433,7 @@ fn set_max_initial_sierra_gas(tx: &mut ExecutableTxWithHash) { pub fn block_context_from_envs( chain_spec: &ChainSpec, block_env: &BlockEnv, - cfg_env: &VersionedConstantsOverrides, + cfg_env: Option<&VersionedConstantsOverrides>, ) -> BlockContext { let fee_token_addresses = FeeTokenAddresses { eth_fee_token_address: to_blk_address(chain_spec.fee_contracts().eth), @@ -486,7 +486,11 @@ pub fn block_context_from_envs( // fees. let sn_version: StarknetVersion = block_env.starknet_version.try_into().expect("valid version"); let mut versioned_constants = VersionedConstants::get(&sn_version).unwrap().clone(); - apply_versioned_constant_overrides(cfg_env, &mut versioned_constants); + + // Only apply overrides if provided + if let Some(cfg) = cfg_env { + apply_versioned_constant_overrides(cfg, &mut versioned_constants); + } BlockContext::new(block_info, chain_info, versioned_constants, BouncerConfig::max()) } @@ -741,32 +745,51 @@ pub(crate) fn apply_versioned_constant_overrides( cfg: &VersionedConstantsOverrides, versioned_constants: &mut VersionedConstants, ) { - versioned_constants.max_recursion_depth = cfg.max_recursion_depth; - versioned_constants.validate_max_n_steps = cfg.validate_max_n_steps; - versioned_constants.invoke_tx_max_n_steps = cfg.invoke_tx_max_n_steps; + // Only apply overrides for fields that are provided (Some) + if let Some(max_recursion_depth) = cfg.max_recursion_depth { + versioned_constants.max_recursion_depth = max_recursion_depth; + } - // Convert the steps to L2 gas. - // - // Reference: https://github.com/dojoengine/sequencer/blob/5d737b9c90a14bdf4483d759d1a1d4ce64aa9fd2/crates/blockifier/src/execution/entry_point.rs#L431-L440 - let l2_gas_per_step = versioned_constants.os_constants.gas_costs.base.step_gas_cost; - let execute_max_sierra_gas = if l2_gas_per_step.is_zero() { - u64::MAX - } else { - (cfg.invoke_tx_max_n_steps as u64).saturating_mul(l2_gas_per_step) - }; + if let Some(validate_max_n_steps) = cfg.validate_max_n_steps { + versioned_constants.validate_max_n_steps = validate_max_n_steps; + } - let validate_max_sierra_gas = if l2_gas_per_step.is_zero() { - u64::MAX - } else { - (cfg.validate_max_n_steps as u64).saturating_mul(l2_gas_per_step) - }; + if let Some(invoke_tx_max_n_steps) = cfg.invoke_tx_max_n_steps { + versioned_constants.invoke_tx_max_n_steps = invoke_tx_max_n_steps; + } + + // Only update sierra gas limits if at least one of the step limits is provided + if cfg.invoke_tx_max_n_steps.is_some() || cfg.validate_max_n_steps.is_some() { + // Convert the steps to L2 gas. + // + // Reference: https://github.com/dojoengine/sequencer/blob/5d737b9c90a14bdf4483d759d1a1d4ce64aa9fd2/crates/blockifier/src/execution/entry_point.rs#L431-L440 + let l2_gas_per_step = versioned_constants.os_constants.gas_costs.base.step_gas_cost; + + let mut os_constants = versioned_constants.os_constants.as_ref().clone(); + + if let Some(invoke_tx_max_n_steps) = cfg.invoke_tx_max_n_steps { + let execute_max_sierra_gas = if l2_gas_per_step.is_zero() { + u64::MAX + } else { + (invoke_tx_max_n_steps as u64).saturating_mul(l2_gas_per_step) + }; + os_constants.execute_max_sierra_gas = execute_max_sierra_gas.into(); + } - // Override the max sierra gas as well so that both resources have equal limits as tranasction - // may be executed using different tracked resources ie cairo steps or sierra gas. - let mut os_constants = versioned_constants.os_constants.as_ref().clone(); - os_constants.execute_max_sierra_gas = execute_max_sierra_gas.into(); - os_constants.validate_max_sierra_gas = validate_max_sierra_gas.into(); - versioned_constants.os_constants = Arc::new(os_constants); + if let Some(validate_max_n_steps) = cfg.validate_max_n_steps { + let validate_max_sierra_gas = if l2_gas_per_step.is_zero() { + u64::MAX + } else { + (validate_max_n_steps as u64).saturating_mul(l2_gas_per_step) + }; + os_constants.validate_max_sierra_gas = validate_max_sierra_gas.into(); + } + + // Override the max sierra gas as well so that both resources have equal limits as + // tranasction may be executed using different tracked resources ie cairo steps or + // sierra gas. + versioned_constants.os_constants = Arc::new(os_constants); + } } #[cfg(test)] diff --git a/crates/executor/src/implementation/noop.rs b/crates/executor/src/implementation/noop.rs index 218781519..6b0df3d72 100644 --- a/crates/executor/src/implementation/noop.rs +++ b/crates/executor/src/implementation/noop.rs @@ -16,7 +16,7 @@ use crate::ExecutorError; /// A no-op executor factory. Creates an executor that does nothing. #[derive(Debug, Default)] pub struct NoopExecutorFactory { - cfg: VersionedConstantsOverrides, + cfg: Option, execution_flags: ExecutionFlags, } @@ -49,8 +49,8 @@ impl ExecutorFactory for NoopExecutorFactory { Box::new(NoopExecutor { block_env }) } - fn cfg(&self) -> &VersionedConstantsOverrides { - &self.cfg + fn cfg(&self) -> Option<&VersionedConstantsOverrides> { + self.cfg.as_ref() } fn execution_flags(&self) -> &ExecutionFlags { diff --git a/crates/executor/tests/fixtures/mod.rs b/crates/executor/tests/fixtures/mod.rs index 8f72041a4..005d54471 100644 --- a/crates/executor/tests/fixtures/mod.rs +++ b/crates/executor/tests/fixtures/mod.rs @@ -245,10 +245,9 @@ pub fn cfg() -> VersionedConstantsOverrides { VersionedConstantsOverrides { // fee_token_addresses, - max_recursion_depth: usize::MAX, - validate_max_n_steps: u32::MAX, - invoke_tx_max_n_steps: u32::MAX, - // chain_id: ChainId::parse("KATANA").unwrap(), + max_recursion_depth: Some(usize::MAX), + validate_max_n_steps: Some(u32::MAX), + invoke_tx_max_n_steps: Some(u32::MAX), } } diff --git a/crates/node/src/full/mod.rs b/crates/node/src/full/mod.rs index 2ceb52506..3b5f1b4c3 100644 --- a/crates/node/src/full/mod.rs +++ b/crates/node/src/full/mod.rs @@ -171,14 +171,20 @@ impl Node { paymaster: None, }; + let chain_spec = match config.network { + Network::Mainnet => ChainSpec::mainnet(), + Network::Sepolia => ChainSpec::sepolia(), + }; + let starknet_api = StarknetApi::new( - Arc::new(ChainSpec::dev()), + Arc::new(chain_spec), BlockchainProvider::new(Box::new(provider.clone())), pool.clone(), task_spawner.clone(), starknet_api_cfg, preconf_factory, GasPriceOracle::create_for_testing(), + None, // Full nodes don't have versioned constants overrides ); if config.rpc.apis.contains(&RpcModuleKind::Starknet) { diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index c2751dd27..caade566e 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -23,7 +23,7 @@ use katana_core::service::block_producer::BlockProducer; use katana_db::Db; use katana_executor::implementation::blockifier::cache::ClassCache; use katana_executor::implementation::blockifier::BlockifierFactory; -use katana_executor::ExecutionFlags; +use katana_executor::{ExecutionFlags, ExecutorFactory}; use katana_gas_price_oracle::{FixedPriceOracle, GasPriceOracle}; use katana_gateway_server::{GatewayServer, GatewayServerHandle}; use katana_metrics::exporters::prometheus::PrometheusRecorder; @@ -31,7 +31,7 @@ use katana_metrics::sys::DiskReporter; use katana_metrics::{Report, Server as MetricsServer}; use katana_pool::ordering::FiFo; use katana_pool::TxPool; -use katana_primitives::env::{FeeTokenAddressses, VersionedConstantsOverrides}; +use katana_primitives::env::VersionedConstantsOverrides; #[cfg(feature = "cartridge")] use katana_rpc_api::cartridge::CartridgeApiServer; use katana_rpc_api::dev::DevApiServer; @@ -91,20 +91,12 @@ impl Node { // --- build executor factory - // let fee_token_addresses = match config.chain.as_ref() { - // ChainSpec::Dev(cs) => { - // FeeTokenAddressses { eth: cs.fee_contracts.eth, strk: cs.fee_contracts.strk } - // } - // ChainSpec::Rollup(cs) => { - // FeeTokenAddressses { eth: cs.fee_contracts.eth, strk: cs.fee_contracts.strk } - // } - // }; - - let cfg_env = VersionedConstantsOverrides { - invoke_tx_max_n_steps: config.execution.invocation_max_steps, - validate_max_n_steps: config.execution.validation_max_steps, - max_recursion_depth: config.execution.max_recursion_depth, - }; + // Create versioned constants overrides from config + let cfg_env = Some(VersionedConstantsOverrides { + invoke_tx_max_n_steps: Some(config.execution.invocation_max_steps), + validate_max_n_steps: Some(config.execution.validation_max_steps), + max_recursion_depth: Some(config.execution.max_recursion_depth), + }); let execution_flags = ExecutionFlags::new() .with_account_validation(config.dev.account_validation) @@ -187,6 +179,9 @@ impl Node { GasPriceOracle::Fixed(FixedPriceOracle::default()) }; + // Get cfg_env before moving executor_factory into Backend + let cfg_env = executor_factory.cfg().cloned(); + let block_context_generator = BlockContextGenerator::default().into(); let backend = Arc::new(Backend { gas_oracle: gas_oracle.clone(), @@ -263,16 +258,17 @@ impl Node { let storage_provider = backend.blockchain.provider().clone(); let chain_spec = backend.chain_spec.clone(); - let starknet_api = if let Some(client) = forked_client { + let starknet_api = if let Some(forked_client) = forked_client { StarknetApi::new_forked( chain_spec.clone(), storage_provider.clone(), pool.clone(), - client, + forked_client, task_spawner.clone(), starknet_api_cfg, block_producer.clone(), gas_oracle.clone(), + cfg_env.clone(), ) } else { StarknetApi::new( @@ -283,6 +279,7 @@ impl Node { starknet_api_cfg, block_producer.clone(), gas_oracle.clone(), + cfg_env, ) }; diff --git a/crates/pool/pool/src/validation/stateful.rs b/crates/pool/pool/src/validation/stateful.rs index e456549f0..467ee8dcf 100644 --- a/crates/pool/pool/src/validation/stateful.rs +++ b/crates/pool/pool/src/validation/stateful.rs @@ -40,7 +40,7 @@ pub struct TxValidator { struct Inner { // execution context - cfg_env: VersionedConstantsOverrides, + cfg_env: Option, block_env: BlockEnv, execution_flags: ExecutionFlags, state: Arc>, @@ -52,7 +52,7 @@ impl TxValidator { pub fn new( state: Box, execution_flags: ExecutionFlags, - cfg_env: VersionedConstantsOverrides, + cfg_env: Option, block_env: BlockEnv, permit: Arc>, chain_spec: Arc, @@ -98,7 +98,8 @@ impl Inner { let state_provider = StateProviderDb::new_with_class_cache(state, class_cache); let cached_state = CachedState::new(state_provider); - let context = block_context_from_envs(&self.chain_spec, &self.block_env, &self.cfg_env); + let context = + block_context_from_envs(&self.chain_spec, &self.block_env, self.cfg_env.as_ref()); StatefulValidator::create(cached_state, context) } diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index e3f8d9a47..4d0d46770 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -24,16 +24,12 @@ pub struct BlockEnv { /// The chain configuration values. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct VersionedConstantsOverrides { - // /// The chain id. - // pub chain_id: ChainId, - // /// The contract addresses of the fee tokens. - // pub fee_token_addresses: FeeTokenAddressses, /// The maximum number of steps allowed for an invoke transaction. - pub invoke_tx_max_n_steps: u32, + pub invoke_tx_max_n_steps: Option, /// The maximum number of steps allowed for transaction validation. - pub validate_max_n_steps: u32, + pub validate_max_n_steps: Option, /// The maximum recursion depth allowed. - pub max_recursion_depth: usize, + pub max_recursion_depth: Option, } /// The contract addresses of the tokens used for the fees. diff --git a/crates/rpc/rpc-server/src/starknet/blockifier.rs b/crates/rpc/rpc-server/src/starknet/blockifier.rs index 40a63ea52..fb1aeb76a 100644 --- a/crates/rpc/rpc-server/src/starknet/blockifier.rs +++ b/crates/rpc/rpc-server/src/starknet/blockifier.rs @@ -20,11 +20,11 @@ pub fn simulate( chain_spec: &ChainSpec, state: impl StateProvider, block_env: BlockEnv, - cfg_env: &VersionedConstantsOverrides, + overrides: Option<&VersionedConstantsOverrides>, transactions: Vec, flags: ExecutionFlags, ) -> Vec { - let block_context = Arc::new(block_context_from_envs(chain_spec, &block_env, &cfg_env)); + let block_context = Arc::new(block_context_from_envs(chain_spec, &block_env, overrides)); let state = CachedState::new(state, ClassCache::global().clone()); let mut results = Vec::with_capacity(transactions.len()); @@ -59,12 +59,12 @@ pub fn estimate_fees( chain_spec: &ChainSpec, state: impl StateProvider, block_env: BlockEnv, - cfg_env: &VersionedConstantsOverrides, + overrides: Option<&VersionedConstantsOverrides>, transactions: Vec, flags: ExecutionFlags, ) -> StarknetApiResult> { let flags = flags.with_fee(false); - let block_context = block_context_from_envs(chain_spec, &block_env, cfg_env); + let block_context = block_context_from_envs(chain_spec, &block_env, overrides); let state = CachedState::new(state, ClassCache::global().clone()); state.with_mut_cached_state(|state| { @@ -116,11 +116,11 @@ pub fn call( chain_spec: &ChainSpec, state: P, block_env: BlockEnv, - cfg_env: &VersionedConstantsOverrides, + overrides: Option<&VersionedConstantsOverrides>, call: FunctionCall, max_call_gas: u64, ) -> Result, StarknetApiError> { - let block_context = Arc::new(block_context_from_envs(chain_spec, &block_env, &cfg_env)); + let block_context = Arc::new(block_context_from_envs(chain_spec, &block_env, overrides)); // `ClassCache::try_global` could only fail if the global cache has not been initialized. // This won't happen in a normal execution flow as we guarantee that the global cache is diff --git a/crates/rpc/rpc-server/src/starknet/mod.rs b/crates/rpc/rpc-server/src/starknet/mod.rs index f92b1c942..2a91f745b 100644 --- a/crates/rpc/rpc-server/src/starknet/mod.rs +++ b/crates/rpc/rpc-server/src/starknet/mod.rs @@ -6,15 +6,13 @@ use std::sync::Arc; use katana_chain_spec::ChainSpec; use katana_core::backend::storage::Database; -use katana_core::backend::Backend; use katana_core::utils::get_current_timestamp; -use katana_executor::ExecutorFactory; use katana_gas_price_oracle::GasPriceOracle; use katana_pool::TransactionPool; use katana_primitives::block::{BlockHashOrNumber, BlockIdOrTag, FinalityStatus, GasPrices}; use katana_primitives::class::{ClassHash, CompiledClass}; use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; -use katana_primitives::env::BlockEnv; +use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; use katana_primitives::event::MaybeForkedContinuationToken; use katana_primitives::transaction::{ExecutableTxWithHash, TxHash, TxNumber}; use katana_primitives::Felt; @@ -102,6 +100,7 @@ where estimate_fee_permit: Permits, config: StarknetApiConfig, pending_block_provider: PP, + versioned_constant_overrides: Option, } impl StarknetApi @@ -135,6 +134,7 @@ where Pool: TransactionPool + 'static, PP: PendingBlockProvider, { + #[allow(clippy::too_many_arguments)] pub fn new( chain_spec: Arc, storage: BlockchainProvider>, @@ -143,6 +143,7 @@ where config: StarknetApiConfig, pending_block_provider: PP, gas_oracle: GasPriceOracle, + versioned_constant_overrides: Option, ) -> Self { Self::new_inner( chain_spec, @@ -153,9 +154,11 @@ where config, pending_block_provider, gas_oracle, + versioned_constant_overrides, ) } + #[allow(clippy::too_many_arguments)] pub fn new_forked( chain_spec: Arc, storage: BlockchainProvider>, @@ -165,6 +168,7 @@ where config: StarknetApiConfig, pending_block_provider: PP, gas_oracle: GasPriceOracle, + versioned_constant_overrides: Option, ) -> Self { Self::new_inner( chain_spec, @@ -175,9 +179,11 @@ where config, pending_block_provider, gas_oracle, + versioned_constant_overrides, ) } + #[allow(clippy::too_many_arguments)] fn new_inner( chain_spec: Arc, storage: BlockchainProvider>, @@ -187,6 +193,7 @@ where config: StarknetApiConfig, pending_block_provider: PP, gas_oracle: GasPriceOracle, + versioned_constant_overrides: Option, ) -> Self { let total_permits = config .max_concurrent_estimate_fee_requests @@ -203,6 +210,7 @@ where config, pending_block_provider, gas_oracle, + versioned_constant_overrides, }; Self { inner: Arc::new(inner) } @@ -271,14 +279,14 @@ where // get the state and block env at the specified block for execution let state = self.state(&block_id)?; let env = self.block_env_at(&block_id)?; - let cfg_env = self.inner.chain_spec.versioned_constants_overrides().unwrap(); + let versioned_constant_overrides = self.inner.versioned_constant_overrides.as_ref(); // do estimations blockifier::estimate_fees( self.inner.chain_spec.as_ref(), state, env, - cfg_env, + versioned_constant_overrides, transactions, flags, ) diff --git a/crates/rpc/rpc-server/src/starknet/read.rs b/crates/rpc/rpc-server/src/starknet/read.rs index a3dbf3fc7..5bf00d2ed 100644 --- a/crates/rpc/rpc-server/src/starknet/read.rs +++ b/crates/rpc/rpc-server/src/starknet/read.rs @@ -148,7 +148,7 @@ where let state = this.state(&block_id)?; let env = this.block_env_at(&block_id)?; // let cfg_env = this.inner.backend.executor_factory.cfg().clone(); - let cfg_env = this.inner.chain_spec.versioned_constants_overrides().unwrap(); + let cfg_env = this.inner.versioned_constant_overrides.as_ref(); let max_call_gas = this.inner.config.max_call_gas.unwrap_or(1_000_000_000); let result = super::blockifier::call( diff --git a/crates/rpc/rpc-server/src/starknet/trace.rs b/crates/rpc/rpc-server/src/starknet/trace.rs index 0f7f29814..159a61671 100644 --- a/crates/rpc/rpc-server/src/starknet/trace.rs +++ b/crates/rpc/rpc-server/src/starknet/trace.rs @@ -65,7 +65,7 @@ where // use the blockifier utils function // let cfg_env = self.inner.backend.executor_factory.cfg().clone(); let chain_spec = self.inner.chain_spec.as_ref(); - let cfg_env = self.inner.chain_spec.versioned_constants_overrides().unwrap(); + let cfg_env = self.inner.versioned_constant_overrides.as_ref(); let results = super::blockifier::simulate(chain_spec, state, env, cfg_env, executables, flags); diff --git a/crates/rpc/rpc-types/src/broadcasted.rs b/crates/rpc/rpc-types/src/broadcasted.rs index 470710d18..fac7d5c93 100644 --- a/crates/rpc/rpc-types/src/broadcasted.rs +++ b/crates/rpc/rpc-types/src/broadcasted.rs @@ -503,9 +503,7 @@ impl From for BroadcastedDeclareTx { // Convert ContractClass to SierraClass // Note: This conversion may lose information for legacy classes let contract_class = match tx.class.as_ref() { - ContractClass::Class(sierra) => { - Arc::new(SierraClass::try_from(sierra.clone()).unwrap_or_default()) - } + ContractClass::Class(sierra) => Arc::new(SierraClass::from(sierra.clone())), ContractClass::Legacy(_) => Arc::new(SierraClass::default()), }; diff --git a/crates/sync/stage/src/classes.rs b/crates/sync/stage/src/classes.rs index 88b4ed147..c4e7d5213 100644 --- a/crates/sync/stage/src/classes.rs +++ b/crates/sync/stage/src/classes.rs @@ -12,7 +12,7 @@ use katana_provider::api::state_update::StateUpdateProvider; use katana_provider::api::ProviderError; use katana_rpc_types::class::ConversionError; use rayon::prelude::*; -use tracing::{debug, error, info, info_span, Instrument}; +use tracing::{debug, error, info_span, Instrument}; use super::{Stage, StageExecutionInput, StageExecutionOutput, StageResult}; use crate::downloader::{BatchDownloader, Downloader, DownloaderResult}; From 04d8f162d6305a1220951d0cc44bf7d5d787f9d7 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 11 Nov 2025 00:08:24 -0500 Subject: [PATCH 44/49] remove unused stuff --- Cargo.lock | 2 -- crates/executor/benches/execution.rs | 30 +++++++++---------- crates/executor/tests/executor.rs | 19 ++++++------ crates/gateway/gateway-client/src/lib.rs | 11 ------- crates/node/Cargo.toml | 1 - crates/rpc/rpc-server/src/starknet/mod.rs | 2 +- .../provider/provider-api/src/state_update.rs | 6 ---- crates/storage/provider/provider/src/lib.rs | 7 ----- .../provider/provider/src/providers/db/mod.rs | 28 ----------------- .../provider/src/providers/fork/mod.rs | 7 ----- .../provider/provider/src/test_utils.rs | 4 +-- crates/sync/stage/Cargo.toml | 1 - crates/sync/stage/tests/trie.rs | 7 ----- 13 files changed, 27 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62efda0ef..b1a7736cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6265,7 +6265,6 @@ dependencies = [ "katana-stage", "katana-starknet", "katana-tasks", - "num-traits", "parking_lot", "serde", "starknet", @@ -6594,7 +6593,6 @@ dependencies = [ "katana-provider", "katana-rpc-types", "katana-tasks", - "katana-trie", "num-traits", "rayon", "rstest 0.18.2", diff --git a/crates/executor/benches/execution.rs b/crates/executor/benches/execution.rs index c37d73391..d0c95e738 100644 --- a/crates/executor/benches/execution.rs +++ b/crates/executor/benches/execution.rs @@ -43,22 +43,22 @@ fn blockifier( // convert to blockifier block context let block_context = block_context_from_envs(chain_spec, block_envs, None); - // group.bench_function("Blockifier.Cold", |b| { - // // we need to set up the cached state for each iteration as it's not cloneable - // b.iter_batched( - // || { - // // setup state - // let state = provider.latest().expect("failed to get latest state"); - // let state = CachedState::new(StateProviderDb::new(state)); + group.bench_function("Blockifier.Cold", |b| { + // we need to set up the cached state for each iteration as it's not cloneable + b.iter_batched( + || { + // setup state + let state = provider.latest().expect("failed to get latest state"); + let state = CachedState::new(StateProviderDb::new(state)); - // (state, &block_context, execution_flags, tx.clone()) - // }, - // |(mut state, block_context, flags, tx)| { - // transact(&mut state, block_context, flags, tx, None) - // }, - // BatchSize::SmallInput, - // ) - // }); + (state, &block_context, execution_flags, tx.clone()) + }, + |(mut state, block_context, flags, tx)| { + transact(&mut state, block_context, flags, tx, None) + }, + BatchSize::SmallInput, + ) + }); } criterion_group! { diff --git a/crates/executor/tests/executor.rs b/crates/executor/tests/executor.rs index 4dd1f8632..690911e38 100644 --- a/crates/executor/tests/executor.rs +++ b/crates/executor/tests/executor.rs @@ -92,12 +92,11 @@ fn test_executor_with_valid_blocks_impl( .unwrap() .expect("storage should exist"); - // assert!( - // updated_main_acc_balance < Felt::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE), - // "sender balance should decrease" - // ); - // assert_eq!(actual_new_acc_balance, felt!("0x9999999999999999"), "account balance is - // updated"); + assert!( + updated_main_acc_balance < Felt::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE), + "sender balance should decrease" + ); + assert_eq!(actual_new_acc_balance, felt!("0x9999999999999999"), "account balance is updated"); // assert that the sierra class is declared let expected_class_hash = felt!("0x420"); @@ -161,10 +160,10 @@ fn test_executor_with_valid_blocks_impl( .unwrap() .expect("storage should exist"); - // assert!( - // updated_new_acc_balance < felt!("0x9999999999999999"), - // "account balance should be updated" - // ); + assert!( + updated_new_acc_balance < felt!("0x9999999999999999"), + "account balance should be updated" + ); // block 3 // diff --git a/crates/gateway/gateway-client/src/lib.rs b/crates/gateway/gateway-client/src/lib.rs index 5ad9a20ed..d5eeeb6de 100644 --- a/crates/gateway/gateway-client/src/lib.rs +++ b/crates/gateway/gateway-client/src/lib.rs @@ -196,17 +196,6 @@ impl Client { } } -// unknown format: -// -// -// 502 Server Error -// -// -//

Error: Server Error

-//

The server encountered a temporary error and could not complete your request.

Please try -// again in 30 seconds.

-// - #[derive(Debug, thiserror::Error)] pub enum Error { #[error(transparent)] diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 7e6198737..33714445e 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -30,7 +30,6 @@ katana-tasks.workspace = true anyhow.workspace = true starknet.workspace = true -num-traits.workspace = true futures.workspace = true http.workspace = true jsonrpsee.workspace = true diff --git a/crates/rpc/rpc-server/src/starknet/mod.rs b/crates/rpc/rpc-server/src/starknet/mod.rs index 218bbf1fe..a6b6a51b2 100644 --- a/crates/rpc/rpc-server/src/starknet/mod.rs +++ b/crates/rpc/rpc-server/src/starknet/mod.rs @@ -12,7 +12,7 @@ use katana_pool::TransactionPool; use katana_primitives::block::{BlockHashOrNumber, BlockIdOrTag, FinalityStatus, GasPrices}; use katana_primitives::class::{ClassHash, CompiledClass}; use katana_primitives::contract::{ContractAddress, Nonce, StorageKey, StorageValue}; -use katana_primitives::env::{BlockEnv, VersionedConstantsOverrides}; +use katana_primitives::env::BlockEnv; use katana_primitives::event::MaybeForkedContinuationToken; use katana_primitives::transaction::{ExecutableTxWithHash, TxHash, TxNumber}; use katana_primitives::Felt; diff --git a/crates/storage/provider/provider-api/src/state_update.rs b/crates/storage/provider/provider-api/src/state_update.rs index 143a55b16..7d706a285 100644 --- a/crates/storage/provider/provider-api/src/state_update.rs +++ b/crates/storage/provider/provider-api/src/state_update.rs @@ -18,12 +18,6 @@ pub trait StateUpdateProvider: Send + Sync { block_id: BlockHashOrNumber, ) -> ProviderResult>>; - /// Returns all declared deprecated class hashes at the given block. - fn declared_deprecated_classes( - &self, - block_id: BlockHashOrNumber, - ) -> ProviderResult>>; - /// Returns all deployed contracts at the given block. fn deployed_contracts( &self, diff --git a/crates/storage/provider/provider/src/lib.rs b/crates/storage/provider/provider/src/lib.rs index c9445f614..c634ff4bb 100644 --- a/crates/storage/provider/provider/src/lib.rs +++ b/crates/storage/provider/provider/src/lib.rs @@ -285,13 +285,6 @@ where self.provider.declared_classes(block_id) } - fn declared_deprecated_classes( - &self, - block_id: BlockHashOrNumber, - ) -> ProviderResult>> { - self.provider.declared_deprecated_classes(block_id) - } - fn deployed_contracts( &self, block_id: BlockHashOrNumber, diff --git a/crates/storage/provider/provider/src/providers/db/mod.rs b/crates/storage/provider/provider/src/providers/db/mod.rs index 253128a46..ff822dcd5 100644 --- a/crates/storage/provider/provider/src/providers/db/mod.rs +++ b/crates/storage/provider/provider/src/providers/db/mod.rs @@ -409,34 +409,6 @@ impl StateUpdateProvider for DbProvider { } } - fn declared_deprecated_classes( - &self, - block_id: BlockHashOrNumber, - ) -> ProviderResult>> { - let db_tx = self.0.tx()?; - let block_num = self.block_number_by_id(block_id)?; - - if let Some(block_num) = block_num { - let declared_classes = dup_entries::, _>( - &db_tx, - block_num, - |entry| { - let (_, class_hash) = entry?; - if db_tx.get::(class_hash)?.is_none() { - Ok(Some(class_hash)) - } else { - Ok(None) - } - }, - )?; - - db_tx.commit()?; - Ok(Some(declared_classes)) - } else { - Ok(None) - } - } - fn deployed_contracts( &self, block_id: BlockHashOrNumber, diff --git a/crates/storage/provider/provider/src/providers/fork/mod.rs b/crates/storage/provider/provider/src/providers/fork/mod.rs index 3f6e1d2df..8a88ffe78 100644 --- a/crates/storage/provider/provider/src/providers/fork/mod.rs +++ b/crates/storage/provider/provider/src/providers/fork/mod.rs @@ -135,13 +135,6 @@ impl StateUpdateProvider for ForkedProvider { self.provider.declared_classes(block_id) } - fn declared_deprecated_classes( - &self, - block_id: BlockHashOrNumber, - ) -> ProviderResult>> { - self.provider.declared_deprecated_classes(block_id) - } - fn deployed_contracts( &self, block_id: BlockHashOrNumber, diff --git a/crates/storage/provider/provider/src/test_utils.rs b/crates/storage/provider/provider/src/test_utils.rs index 11170b8ec..fa28f7f14 100644 --- a/crates/storage/provider/provider/src/test_utils.rs +++ b/crates/storage/provider/provider/src/test_utils.rs @@ -19,7 +19,7 @@ pub fn test_provider() -> DbProvider { /// Initializes the provider with a genesis block and states. fn initialize_test_provider(provider: &P) { - let chain = get_chain_for_testing(); + let chain = create_chain_for_testing(); let hash = BlockHash::ZERO; let status = FinalityStatus::AcceptedOnL2; @@ -34,7 +34,7 @@ fn initialize_test_provider(provider: &P) { /// Creates a genesis config specifically for testing purposes. /// This includes: /// - An account with simple `__execute__` function, deployed at address `0x1`. -pub fn get_chain_for_testing() -> katana_chain_spec::dev::ChainSpec { +fn create_chain_for_testing() -> katana_chain_spec::dev::ChainSpec { let mut chain = katana_chain_spec::dev::DEV_UNALLOCATED.clone(); let class_hash = felt!("0x111"); diff --git a/crates/sync/stage/Cargo.toml b/crates/sync/stage/Cargo.toml index 0a79c34a3..1e952ae28 100644 --- a/crates/sync/stage/Cargo.toml +++ b/crates/sync/stage/Cargo.toml @@ -12,7 +12,6 @@ katana-gateway-client.workspace = true katana-gateway-types.workspace = true katana-messaging.workspace = true katana-pool.workspace = true -katana-trie.workspace = true katana-primitives.workspace = true katana-provider.workspace = true katana-rpc-types.workspace = true diff --git a/crates/sync/stage/tests/trie.rs b/crates/sync/stage/tests/trie.rs index 74f9406b9..d2352162e 100644 --- a/crates/sync/stage/tests/trie.rs +++ b/crates/sync/stage/tests/trie.rs @@ -101,13 +101,6 @@ impl StateUpdateProvider for MockProvider { Ok(None) } - fn declared_deprecated_classes( - &self, - block_id: BlockHashOrNumber, - ) -> ProviderResult>> { - Ok(None) - } - fn deployed_contracts( &self, _block_id: BlockHashOrNumber, From 5d6b5b3da1e46a54e29a2ac1bcbae8095a4f1140 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 11 Nov 2025 00:10:27 -0500 Subject: [PATCH 45/49] fmt --- crates/chain-spec/src/rollup/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/chain-spec/src/rollup/file.rs b/crates/chain-spec/src/rollup/file.rs index 91d65bc2b..d0d4b10ce 100644 --- a/crates/chain-spec/src/rollup/file.rs +++ b/crates/chain-spec/src/rollup/file.rs @@ -309,7 +309,7 @@ mod tests { use tempfile::TempDir; use url::Url; - use super::{Error, FileFeeContract}; + use super::Error; use crate::rollup::file::{local_dir, ChainConfigDir, LocalChainConfigDir, KATANA_LOCAL_DIR}; use crate::rollup::ChainSpec; use crate::{FeeContracts, SettlementLayer}; From 94b2d3638234df6f96c5e4b43aad5fea6b501742 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 12 Nov 2025 11:52:51 -0500 Subject: [PATCH 46/49] revert --- .tool-versions | 1 - 1 file changed, 1 deletion(-) diff --git a/.tool-versions b/.tool-versions index 1a66fa783..cc60fd625 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1 @@ scarb 2.8.2 -katana 1.7.0 From 3c0500c1e092f67ac9a999e7d504bc62c8d75fd3 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 12 Nov 2025 11:55:34 -0500 Subject: [PATCH 47/49] remove unused dep --- Cargo.lock | 1 - crates/primitives/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1a7736cd..a76079aef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6348,7 +6348,6 @@ dependencies = [ "anyhow", "arbitrary", "assert_matches", - "bincode 1.3.3", "blockifier 0.0.0 (git+https://github.com/dojoengine/sequencer?rev=5d737b9c9)", "cainome-cairo-serde", "cairo-lang-starknet-classes", diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index fd574b47d..c7fce089d 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -33,7 +33,6 @@ strum_macros.workspace = true [dev-dependencies] assert_matches.workspace = true -bincode = "1.3" postcard.workspace = true rstest.workspace = true similar-asserts.workspace = true From 2d9ae1a8e2af3b68dfd053a85170945e1e742b54 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 12 Nov 2025 13:29:42 -0500 Subject: [PATCH 48/49] wip --- Cargo.lock | 1 - crates/gateway/gateway-types/src/receipt.rs | 376 ++++++++++++++++++ .../gateway/gateway-types/src/transaction.rs | 269 ++++++++++--- crates/node/Cargo.toml | 1 - crates/node/src/full/pending/mod.rs | 30 +- crates/node/src/full/pending/provider.rs | 92 ++++- crates/node/src/full/pending/state.rs | 1 + 7 files changed, 692 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a76079aef..f5203e33a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6267,7 +6267,6 @@ dependencies = [ "katana-tasks", "parking_lot", "serde", - "starknet", "strum 0.25.0", "strum_macros 0.25.3", "thiserror 1.0.69", diff --git a/crates/gateway/gateway-types/src/receipt.rs b/crates/gateway/gateway-types/src/receipt.rs index 6a9394df9..693c59787 100644 --- a/crates/gateway/gateway-types/src/receipt.rs +++ b/crates/gateway/gateway-types/src/receipt.rs @@ -58,3 +58,379 @@ pub struct L1ToL2Message { pub payload: Vec, pub nonce: Option, } + +//////////////////////////////////////////////////////////////////////////////// +// Conversion to Katana RPC types. +//////////////////////////////////////////////////////////////////////////////// + +impl From for katana_rpc_types::ExecutionResult { + fn from(value: ExecutionStatus) -> Self { + match value { + ExecutionStatus::Succeeded => katana_rpc_types::ExecutionResult::Succeeded, + ExecutionStatus::Reverted => { + // When converting from gateway ExecutionStatus::Reverted, we don't have the + // revert reason here. The caller should use the revert_error field from + // ReceiptBody if available. + katana_rpc_types::ExecutionResult::Reverted { + reason: String::from("Transaction reverted"), + } + } + } + } +} + +impl From for katana_rpc_types::ExecutionResources { + fn from(value: ExecutionResources) -> Self { + let gas = value.total_gas_consumed.unwrap_or_default(); + katana_rpc_types::ExecutionResources { + l1_gas: gas.l1_gas, + l1_data_gas: gas.l1_data_gas, + l2_gas: gas.l2_gas, + } + } +} + +impl ReceiptBody { + /// Convert the receipt body to an RPC execution result. + /// + /// This uses the `execution_status` field if available, otherwise falls back to checking + /// the `revert_error` field. If `revert_error` is present, the result is `Reverted`. + pub fn to_execution_result(&self) -> katana_rpc_types::ExecutionResult { + if let Some(revert_error) = &self.revert_error { + katana_rpc_types::ExecutionResult::Reverted { reason: revert_error.clone() } + } else if let Some(status) = &self.execution_status { + match status { + ExecutionStatus::Succeeded => katana_rpc_types::ExecutionResult::Succeeded, + ExecutionStatus::Reverted => { + // Reverted status without error message + katana_rpc_types::ExecutionResult::Reverted { + reason: String::from("Transaction reverted"), + } + } + } + } else { + // If no status is provided, assume success + katana_rpc_types::ExecutionResult::Succeeded + } + } + + /// Convert to an RPC FeePayment with the given price unit. + pub fn to_fee_payment( + &self, + unit: katana_primitives::fee::PriceUnit, + ) -> katana_rpc_types::FeePayment { + katana_rpc_types::FeePayment { amount: self.actual_fee, unit } + } +} + +impl ConfirmedReceipt { + /// Create an RPC Invoke receipt from this gateway receipt. + /// + /// # Arguments + /// * `finality_status` - The finality status of the transaction + /// * `fee_unit` - The price unit for the fee + pub fn to_rpc_invoke_receipt( + self, + finality_status: katana_primitives::block::FinalityStatus, + fee_unit: katana_primitives::fee::PriceUnit, + ) -> katana_rpc_types::RpcInvokeTxReceipt { + let execution_result = self.body.to_execution_result(); + let actual_fee = self.body.to_fee_payment(fee_unit); + + katana_rpc_types::RpcInvokeTxReceipt { + actual_fee, + finality_status, + messages_sent: self.body.l2_to_l1_messages, + events: self.body.events, + execution_resources: self.body.execution_resources.unwrap_or_default().into(), + execution_result, + } + } + + /// Create an RPC Declare receipt from this gateway receipt. + /// + /// # Arguments + /// * `finality_status` - The finality status of the transaction + /// * `fee_unit` - The price unit for the fee + pub fn to_rpc_declare_receipt( + self, + finality_status: katana_primitives::block::FinalityStatus, + fee_unit: katana_primitives::fee::PriceUnit, + ) -> katana_rpc_types::RpcDeclareTxReceipt { + let execution_result = self.body.to_execution_result(); + let actual_fee = self.body.to_fee_payment(fee_unit); + + katana_rpc_types::RpcDeclareTxReceipt { + actual_fee, + finality_status, + messages_sent: self.body.l2_to_l1_messages, + events: self.body.events, + execution_resources: self.body.execution_resources.unwrap_or_default().into(), + execution_result, + } + } + + /// Create an RPC Deploy receipt from this gateway receipt. + /// + /// # Arguments + /// * `finality_status` - The finality status of the transaction + /// * `fee_unit` - The price unit for the fee + /// * `contract_address` - The deployed contract address + pub fn to_rpc_deploy_receipt( + self, + finality_status: katana_primitives::block::FinalityStatus, + fee_unit: katana_primitives::fee::PriceUnit, + contract_address: ContractAddress, + ) -> katana_rpc_types::RpcDeployTxReceipt { + let execution_result = self.body.to_execution_result(); + let actual_fee = self.body.to_fee_payment(fee_unit); + + katana_rpc_types::RpcDeployTxReceipt { + actual_fee, + finality_status, + messages_sent: self.body.l2_to_l1_messages, + events: self.body.events, + execution_resources: self.body.execution_resources.unwrap_or_default().into(), + contract_address, + execution_result, + } + } + + /// Create an RPC DeployAccount receipt from this gateway receipt. + /// + /// # Arguments + /// * `finality_status` - The finality status of the transaction + /// * `fee_unit` - The price unit for the fee + /// * `contract_address` - The deployed account contract address + pub fn to_rpc_deploy_account_receipt( + self, + finality_status: katana_primitives::block::FinalityStatus, + fee_unit: katana_primitives::fee::PriceUnit, + contract_address: ContractAddress, + ) -> katana_rpc_types::RpcDeployAccountTxReceipt { + let execution_result = self.body.to_execution_result(); + let actual_fee = self.body.to_fee_payment(fee_unit); + + katana_rpc_types::RpcDeployAccountTxReceipt { + actual_fee, + finality_status, + messages_sent: self.body.l2_to_l1_messages, + events: self.body.events, + execution_resources: self.body.execution_resources.unwrap_or_default().into(), + contract_address, + execution_result, + } + } + + /// Create an RPC L1Handler receipt from this gateway receipt. + /// + /// # Arguments + /// * `finality_status` - The finality status of the transaction + /// * `fee_unit` - The price unit for the fee + /// * `message_hash` - The L1 to L2 message hash + pub fn to_rpc_l1_handler_receipt( + self, + finality_status: katana_primitives::block::FinalityStatus, + fee_unit: katana_primitives::fee::PriceUnit, + message_hash: katana_primitives::B256, + ) -> katana_rpc_types::RpcL1HandlerTxReceipt { + let execution_result = self.body.to_execution_result(); + let actual_fee = self.body.to_fee_payment(fee_unit); + + katana_rpc_types::RpcL1HandlerTxReceipt { + actual_fee, + finality_status, + messages_sent: self.body.l2_to_l1_messages, + events: self.body.events, + execution_resources: self.body.execution_resources.unwrap_or_default().into(), + message_hash, + execution_result, + } + } +} + +impl Default for ExecutionResources { + fn default() -> Self { + Self { + vm_resources: Default::default(), + data_availability: None, + total_gas_consumed: Some(Default::default()), + } + } +} + +#[cfg(test)] +mod tests { + use katana_primitives::block::FinalityStatus; + use katana_primitives::fee::PriceUnit; + use katana_primitives::receipt::GasUsed; + use katana_primitives::{address, felt}; + + use super::*; + + fn create_test_receipt_body() -> ReceiptBody { + ReceiptBody { + execution_resources: Some(ExecutionResources { + vm_resources: Default::default(), + data_availability: None, + total_gas_consumed: Some(GasUsed { l1_gas: 100, l1_data_gas: 50, l2_gas: 200 }), + }), + l1_to_l2_consumed_message: None, + l2_to_l1_messages: vec![], + events: vec![], + actual_fee: felt!("0x1234"), + execution_status: Some(ExecutionStatus::Succeeded), + revert_error: None, + } + } + + #[test] + fn test_execution_status_to_execution_result() { + let succeeded: katana_rpc_types::ExecutionResult = ExecutionStatus::Succeeded.into(); + assert_eq!(succeeded, katana_rpc_types::ExecutionResult::Succeeded); + + let reverted: katana_rpc_types::ExecutionResult = ExecutionStatus::Reverted.into(); + match reverted { + katana_rpc_types::ExecutionResult::Reverted { reason } => { + assert_eq!(reason, "Transaction reverted"); + } + _ => panic!("Expected Reverted result"), + } + } + + #[test] + fn test_execution_resources_conversion() { + let gateway_resources = ExecutionResources { + vm_resources: Default::default(), + data_availability: None, + total_gas_consumed: Some(GasUsed { l1_gas: 100, l1_data_gas: 50, l2_gas: 200 }), + }; + + let rpc_resources: katana_rpc_types::ExecutionResources = gateway_resources.into(); + assert_eq!(rpc_resources.l1_gas, 100); + assert_eq!(rpc_resources.l1_data_gas, 50); + assert_eq!(rpc_resources.l2_gas, 200); + } + + #[test] + fn test_receipt_body_to_execution_result_with_revert_error() { + let body = ReceiptBody { + execution_resources: None, + l1_to_l2_consumed_message: None, + l2_to_l1_messages: vec![], + events: vec![], + actual_fee: felt!("0x0"), + execution_status: Some(ExecutionStatus::Succeeded), + revert_error: Some("Out of gas".to_string()), + }; + + let result = body.to_execution_result(); + match result { + katana_rpc_types::ExecutionResult::Reverted { reason } => { + assert_eq!(reason, "Out of gas"); + } + _ => panic!("Expected Reverted result"), + } + } + + #[test] + fn test_receipt_body_to_execution_result_succeeded() { + let body = create_test_receipt_body(); + let result = body.to_execution_result(); + assert_eq!(result, katana_rpc_types::ExecutionResult::Succeeded); + } + + #[test] + fn test_to_rpc_invoke_receipt() { + let gateway_receipt = ConfirmedReceipt { + transaction_hash: felt!("0xabc"), + transaction_index: 5, + body: create_test_receipt_body(), + }; + + let rpc_receipt = gateway_receipt + .clone() + .to_rpc_invoke_receipt(FinalityStatus::AcceptedOnL2, PriceUnit::Wei); + + assert_eq!(rpc_receipt.actual_fee.amount, felt!("0x1234")); + assert_eq!(rpc_receipt.actual_fee.unit, PriceUnit::Wei); + assert_eq!(rpc_receipt.finality_status, FinalityStatus::AcceptedOnL2); + assert_eq!(rpc_receipt.execution_resources.l1_gas, 100); + assert_eq!(rpc_receipt.execution_resources.l2_gas, 200); + assert_eq!(rpc_receipt.execution_result, katana_rpc_types::ExecutionResult::Succeeded); + } + + #[test] + fn test_to_rpc_declare_receipt() { + let gateway_receipt = ConfirmedReceipt { + transaction_hash: felt!("0xdef"), + transaction_index: 10, + body: create_test_receipt_body(), + }; + + let rpc_receipt = gateway_receipt + .clone() + .to_rpc_declare_receipt(FinalityStatus::AcceptedOnL1, PriceUnit::Fri); + + assert_eq!(rpc_receipt.actual_fee.amount, felt!("0x1234")); + assert_eq!(rpc_receipt.actual_fee.unit, PriceUnit::Fri); + assert_eq!(rpc_receipt.finality_status, FinalityStatus::AcceptedOnL1); + } + + #[test] + fn test_to_rpc_deploy_receipt() { + let gateway_receipt = ConfirmedReceipt { + transaction_hash: felt!("0x123"), + transaction_index: 1, + body: create_test_receipt_body(), + }; + + let contract_address = address!("0x456"); + let rpc_receipt = gateway_receipt.clone().to_rpc_deploy_receipt( + FinalityStatus::AcceptedOnL2, + PriceUnit::Wei, + contract_address, + ); + + assert_eq!(rpc_receipt.contract_address, contract_address); + assert_eq!(rpc_receipt.actual_fee.amount, felt!("0x1234")); + } + + #[test] + fn test_to_rpc_deploy_account_receipt() { + let gateway_receipt = ConfirmedReceipt { + transaction_hash: felt!("0x789"), + transaction_index: 2, + body: create_test_receipt_body(), + }; + + let contract_address = address!("0xabc"); + let rpc_receipt = gateway_receipt.clone().to_rpc_deploy_account_receipt( + FinalityStatus::AcceptedOnL2, + PriceUnit::Wei, + contract_address, + ); + + assert_eq!(rpc_receipt.contract_address, contract_address); + assert_eq!(rpc_receipt.execution_result, katana_rpc_types::ExecutionResult::Succeeded); + } + + #[test] + fn test_to_rpc_l1_handler_receipt() { + let gateway_receipt = ConfirmedReceipt { + transaction_hash: felt!("0xfff"), + transaction_index: 3, + body: create_test_receipt_body(), + }; + + let message_hash = katana_primitives::B256::from([1u8; 32]); + let rpc_receipt = gateway_receipt.clone().to_rpc_l1_handler_receipt( + FinalityStatus::AcceptedOnL2, + PriceUnit::Wei, + message_hash, + ); + + assert_eq!(rpc_receipt.message_hash, message_hash); + assert_eq!(rpc_receipt.actual_fee.amount, felt!("0x1234")); + } +} diff --git a/crates/gateway/gateway-types/src/transaction.rs b/crates/gateway/gateway-types/src/transaction.rs index ae24a88ef..a3c78dc55 100644 --- a/crates/gateway/gateway-types/src/transaction.rs +++ b/crates/gateway/gateway-types/src/transaction.rs @@ -310,6 +310,19 @@ pub enum TypedTransaction { DeployAccount(DeployAccountTx), } +impl TypedTransaction { + /// Returns the type of the transaction. + pub fn r#type(&self) -> TxType { + match self { + TypedTransaction::Deploy(_) => TxType::Deploy, + TypedTransaction::Declare(_) => TxType::Declare, + TypedTransaction::L1Handler(_) => TxType::L1Handler, + TypedTransaction::InvokeFunction(_) => TxType::Invoke, + TypedTransaction::DeployAccount(_) => TxType::DeployAccount, + } + } +} + /// Invoke transaction enum with version-specific variants #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "version")] @@ -490,27 +503,94 @@ impl<'de> Deserialize<'de> for DataAvailabilityMode { } } +fn deserialize_resource_bounds_mapping<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result { + #[derive(Deserialize)] + struct FeederGatewayResourceBounds { + #[serde(rename = "L1_GAS")] + l1_gas: ResourceBounds, + #[serde(rename = "L2_GAS")] + l2_gas: ResourceBounds, + #[serde(rename = "L1_DATA_GAS")] + l1_data_gas: Option, + } + + let bounds = FeederGatewayResourceBounds::deserialize(deserializer)?; + + if let Some(l1_data_gas) = bounds.l1_data_gas { + Ok(ResourceBoundsMapping::All(AllResourceBoundsMapping { + l1_gas: bounds.l1_gas, + l2_gas: bounds.l2_gas, + l1_data_gas, + })) + } else { + Ok(ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { + l1_gas: bounds.l1_gas, + l2_gas: bounds.l2_gas, + })) + } +} + +fn serialize_resource_bounds_mapping( + bounds: &ResourceBoundsMapping, + serializer: S, +) -> Result { + #[derive(Serialize)] + struct FeederGatewayResourceBounds<'a> { + #[serde(rename = "L1_GAS")] + l1_gas: &'a ResourceBounds, + #[serde(rename = "L2_GAS")] + l2_gas: &'a ResourceBounds, + #[serde(rename = "L1_DATA_GAS")] + l1_data_gas: Option<&'a ResourceBounds>, + } + + let feeder_bounds = match bounds { + ResourceBoundsMapping::All(all_bounds) => FeederGatewayResourceBounds { + l1_gas: &all_bounds.l1_gas, + l2_gas: &all_bounds.l2_gas, + l1_data_gas: Some(&all_bounds.l1_data_gas), + }, + ResourceBoundsMapping::L1Gas(l1_gas_bounds) => FeederGatewayResourceBounds { + l1_gas: &l1_gas_bounds.l1_gas, + l2_gas: &l1_gas_bounds.l2_gas, + l1_data_gas: None, + }, + }; + + feeder_bounds.serialize(serializer) +} + +//////////////////////////////////////////////////////////////////////////////// +// Conversion to katana-primitives types +//////////////////////////////////////////////////////////////////////////////// + #[derive(Debug, thiserror::Error)] pub enum TxTryFromError { #[error("unsupported transaction version; type: {r#type:?}, version: {version:#x}")] UnsupportedVersion { r#type: TxType, version: Felt }, } -// -- Conversion to Katana primitive types. - impl TryFrom for TxWithHash { type Error = TxTryFromError; fn try_from(tx: ConfirmedTransaction) -> Result { - let transaction = match tx.transaction { + Ok(TxWithHash { hash: tx.transaction_hash, transaction: tx.transaction.try_into()? }) + } +} + +impl TryFrom for Tx { + type Error = TxTryFromError; + + fn try_from(tx: TypedTransaction) -> Result { + Ok(match tx { TypedTransaction::Deploy(tx) => Tx::Deploy(tx), - TypedTransaction::Declare(tx) => Tx::Declare(tx.try_into()?), TypedTransaction::L1Handler(tx) => Tx::L1Handler(tx.into()), + TypedTransaction::Declare(tx) => Tx::Declare(tx.try_into()?), TypedTransaction::InvokeFunction(tx) => Tx::Invoke(tx.try_into()?), TypedTransaction::DeployAccount(tx) => Tx::DeployAccount(tx.try_into()?), - }; - - Ok(TxWithHash { hash: tx.transaction_hash, transaction }) + }) } } @@ -659,63 +739,142 @@ impl From for katana_primitives::da::DataAvailabilityMode } } -fn deserialize_resource_bounds_mapping<'de, D: Deserializer<'de>>( - deserializer: D, -) -> Result { - #[derive(Deserialize)] - struct FeederGatewayResourceBounds { - #[serde(rename = "L1_GAS")] - l1_gas: ResourceBounds, - #[serde(rename = "L2_GAS")] - l2_gas: ResourceBounds, - #[serde(rename = "L1_DATA_GAS")] - l1_data_gas: Option, +//////////////////////////////////////////////////////////////////////////////// +// Conversion to katana-rpc-types types +//////////////////////////////////////////////////////////////////////////////// + +impl From for katana_rpc_types::RpcTxWithHash { + fn from(value: ConfirmedTransaction) -> Self { + Self { transaction_hash: value.transaction_hash, transaction: value.transaction.into() } } +} - let bounds = FeederGatewayResourceBounds::deserialize(deserializer)?; +impl From for katana_rpc_types::RpcTx { + fn from(value: TypedTransaction) -> Self { + match value { + TypedTransaction::Deploy(tx) => { + katana_rpc_types::RpcTx::Deploy(katana_rpc_types::RpcDeployTx { + version: tx.version, + class_hash: tx.class_hash, + constructor_calldata: tx.constructor_calldata, + contract_address_salt: tx.contract_address_salt, + }) + } + TypedTransaction::L1Handler(tx) => katana_rpc_types::RpcTx::L1Handler(tx.into()), + TypedTransaction::Declare(tx) => katana_rpc_types::RpcTx::Declare(tx.into()), + TypedTransaction::InvokeFunction(tx) => katana_rpc_types::RpcTx::Invoke(tx.into()), + TypedTransaction::DeployAccount(tx) => { + katana_rpc_types::RpcTx::DeployAccount(tx.into()) + } + } + } +} - if let Some(l1_data_gas) = bounds.l1_data_gas { - Ok(ResourceBoundsMapping::All(AllResourceBoundsMapping { - l1_gas: bounds.l1_gas, - l2_gas: bounds.l2_gas, - l1_data_gas, - })) - } else { - Ok(ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: bounds.l1_gas, - l2_gas: bounds.l2_gas, - })) +impl From for katana_rpc_types::RpcInvokeTx { + fn from(value: InvokeTx) -> Self { + match value { + InvokeTx::V0(tx) => katana_rpc_types::RpcInvokeTx::V0(tx), + InvokeTx::V1(tx) => katana_rpc_types::RpcInvokeTx::V1(tx), + InvokeTx::V3(tx) => katana_rpc_types::RpcInvokeTx::V3(tx.into()), + } } } -fn serialize_resource_bounds_mapping( - bounds: &ResourceBoundsMapping, - serializer: S, -) -> Result { - #[derive(Serialize)] - struct FeederGatewayResourceBounds<'a> { - #[serde(rename = "L1_GAS")] - l1_gas: &'a ResourceBounds, - #[serde(rename = "L2_GAS")] - l2_gas: &'a ResourceBounds, - #[serde(rename = "L1_DATA_GAS")] - l1_data_gas: Option<&'a ResourceBounds>, +impl From for katana_rpc_types::RpcInvokeTxV3 { + fn from(value: InvokeTxV3) -> Self { + Self { + sender_address: value.sender_address, + calldata: value.calldata, + signature: value.signature, + nonce: value.nonce, + resource_bounds: value.resource_bounds, + tip: value.tip, + paymaster_data: value.paymaster_data, + account_deployment_data: value.account_deployment_data, + nonce_data_availability_mode: value.nonce_data_availability_mode.into(), + fee_data_availability_mode: value.fee_data_availability_mode.into(), + } } +} - let feeder_bounds = match bounds { - ResourceBoundsMapping::All(all_bounds) => FeederGatewayResourceBounds { - l1_gas: &all_bounds.l1_gas, - l2_gas: &all_bounds.l2_gas, - l1_data_gas: Some(&all_bounds.l1_data_gas), - }, - ResourceBoundsMapping::L1Gas(l1_gas_bounds) => FeederGatewayResourceBounds { - l1_gas: &l1_gas_bounds.l1_gas, - l2_gas: &l1_gas_bounds.l2_gas, - l1_data_gas: None, - }, - }; +impl From for katana_rpc_types::RpcDeclareTx { + fn from(value: DeclareTx) -> Self { + match value { + DeclareTx::V0(tx) => katana_rpc_types::RpcDeclareTx::V0(tx), + DeclareTx::V1(tx) => katana_rpc_types::RpcDeclareTx::V1(tx), + DeclareTx::V2(tx) => katana_rpc_types::RpcDeclareTx::V2(tx), + DeclareTx::V3(tx) => katana_rpc_types::RpcDeclareTx::V3(tx.into()), + } + } +} - feeder_bounds.serialize(serializer) +impl From for katana_rpc_types::RpcDeclareTxV3 { + fn from(value: DeclareTxV3) -> Self { + Self { + sender_address: value.sender_address, + compiled_class_hash: value.compiled_class_hash, + signature: value.signature, + nonce: value.nonce, + class_hash: value.class_hash, + resource_bounds: value.resource_bounds, + tip: value.tip, + paymaster_data: value.paymaster_data, + account_deployment_data: value.account_deployment_data, + nonce_data_availability_mode: value.nonce_data_availability_mode.into(), + fee_data_availability_mode: value.fee_data_availability_mode.into(), + } + } +} + +impl From for katana_rpc_types::RpcDeployAccountTx { + fn from(value: DeployAccountTx) -> Self { + match value { + DeployAccountTx::V1(tx) => katana_rpc_types::RpcDeployAccountTx::V1(tx.into()), + DeployAccountTx::V3(tx) => katana_rpc_types::RpcDeployAccountTx::V3(tx.into()), + } + } +} + +impl From for katana_rpc_types::RpcDeployAccountTxV1 { + fn from(value: DeployAccountTxV1) -> Self { + Self { + max_fee: value.max_fee, + signature: value.signature, + nonce: value.nonce, + contract_address_salt: value.contract_address_salt, + constructor_calldata: value.constructor_calldata, + class_hash: value.class_hash, + } + } +} + +impl From for katana_rpc_types::RpcDeployAccountTxV3 { + fn from(value: DeployAccountTxV3) -> Self { + Self { + signature: value.signature, + nonce: value.nonce, + contract_address_salt: value.contract_address_salt, + constructor_calldata: value.constructor_calldata, + class_hash: value.class_hash, + resource_bounds: value.resource_bounds, + tip: value.tip, + paymaster_data: value.paymaster_data, + nonce_data_availability_mode: value.nonce_data_availability_mode.into(), + fee_data_availability_mode: value.fee_data_availability_mode.into(), + } + } +} + +impl From for katana_rpc_types::RpcL1HandlerTx { + fn from(value: L1HandlerTx) -> Self { + Self { + version: value.version, + nonce: value.nonce.unwrap_or_default(), + contract_address: value.contract_address, + entry_point_selector: value.entry_point_selector, + calldata: value.calldata, + } + } } #[cfg(test)] diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 33714445e..115d8f78a 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -29,7 +29,6 @@ katana-stage.workspace = true katana-tasks.workspace = true anyhow.workspace = true -starknet.workspace = true futures.workspace = true http.workspace = true jsonrpsee.workspace = true diff --git a/crates/node/src/full/pending/mod.rs b/crates/node/src/full/pending/mod.rs index 99c4ae2c4..db98e91bd 100644 --- a/crates/node/src/full/pending/mod.rs +++ b/crates/node/src/full/pending/mod.rs @@ -71,27 +71,27 @@ impl PreconfStateFactory

{ } pub fn state_updates(&self) -> Option { - if let Some(preconf_data) = self.shared_preconf_block.inner.lock().as_ref() { - Some(preconf_data.preconf_state_updates.clone()) - } else { - None - } + self.shared_preconf_block + .inner + .lock() + .as_ref() + .map(|preconf_data| preconf_data.preconf_state_updates.clone()) } pub fn block(&self) -> Option { - if let Some(preconf_data) = self.shared_preconf_block.inner.lock().as_ref() { - Some(preconf_data.preconf_block.clone()) - } else { - None - } + self.shared_preconf_block + .inner + .lock() + .as_ref() + .map(|preconf_data| preconf_data.preconf_block.clone()) } pub fn transactions(&self) -> Option> { - if let Some(preconf_data) = self.shared_preconf_block.inner.lock().as_ref() { - Some(preconf_data.preconf_block.transactions.clone()) - } else { - None - } + self.shared_preconf_block + .inner + .lock() + .as_ref() + .map(|preconf_data| preconf_data.preconf_block.transactions.clone()) } } diff --git a/crates/node/src/full/pending/provider.rs b/crates/node/src/full/pending/provider.rs index 8a5945639..bae82c04e 100644 --- a/crates/node/src/full/pending/provider.rs +++ b/crates/node/src/full/pending/provider.rs @@ -1,10 +1,15 @@ use std::fmt::Debug; use katana_gateway_types::TxTryFromError; -use katana_primitives::transaction::{TxHash, TxNumber, TxWithHash}; +use katana_primitives::block::FinalityStatus; +use katana_primitives::fee::PriceUnit; +use katana_primitives::transaction::{TxHash, TxNumber, TxType, TxWithHash}; +use katana_primitives::Felt; use katana_provider::api::state::{StateFactoryProvider, StateProvider}; use katana_rpc_server::starknet::{PendingBlockProvider, StarknetApiResult}; -use katana_rpc_types::RpcTxWithHash; +use katana_rpc_types::{ + PreConfirmedStateUpdate, ReceiptBlockInfo, RpcTxReceipt, RpcTxWithHash, TxReceiptWithBlockInfo, +}; use crate::full::pending::PreconfStateFactory; @@ -91,27 +96,101 @@ where &self, hash: TxHash, ) -> StarknetApiResult> { - Ok(None) + if let Some(preconf_block) = self.block() { + let receipt = preconf_block + .transaction_receipts + .iter() + .zip(preconf_block.transactions) + .filter_map(|(receipt, tx)| { + if let Some(receipt) = receipt { + Some((receipt.clone(), tx.transaction.r#type())) + } else { + None + } + }) + .find(|(receipt, ..)| receipt.transaction_hash == hash); + + let Some((receipt, r#type)) = receipt else { return Ok(None) }; + + let status = FinalityStatus::PreConfirmed; + let transaction_hash = receipt.transaction_hash; + let block = ReceiptBlockInfo::PreConfirmed { block_number: 0 }; + + let receipt = match r#type { + TxType::Invoke => { + RpcTxReceipt::Invoke(receipt.to_rpc_invoke_receipt(status, PriceUnit::Fri)) + } + + TxType::Declare => { + RpcTxReceipt::Declare(receipt.to_rpc_declare_receipt(status, PriceUnit::Fri)) + } + + TxType::Deploy => RpcTxReceipt::Deploy(receipt.to_rpc_deploy_receipt( + status, + PriceUnit::Fri, + Default::default(), + )), + + TxType::L1Handler => RpcTxReceipt::L1Handler(receipt.to_rpc_l1_handler_receipt( + status, + PriceUnit::Fri, + Default::default(), + )), + + TxType::DeployAccount => { + RpcTxReceipt::DeployAccount(receipt.to_rpc_deploy_account_receipt( + status, + PriceUnit::Fri, + Default::default(), + )) + } + }; + + Ok(Some(TxReceiptWithBlockInfo { transaction_hash, receipt, block })) + } else { + Ok(None) + } } fn get_pending_state_update( &self, ) -> StarknetApiResult> { - Ok(None) + if let Some(state_diff) = self.state_updates() { + Ok(Some(PreConfirmedStateUpdate { + old_root: Felt::ZERO, + state_diff: state_diff.into(), + })) + } else { + Ok(None) + } } fn get_pending_transaction( &self, hash: TxHash, ) -> StarknetApiResult> { - Ok(None) + if let Some(preconf_transactions) = self.transactions() { + let transaction = preconf_transactions + .iter() + .find(|tx| tx.transaction_hash == hash) + .cloned() + .map(RpcTxWithHash::from); + + Ok(transaction) + } else { + Ok(None) + } } fn get_pending_transaction_by_index( &self, index: TxNumber, ) -> StarknetApiResult> { - Ok(None) + if let Some(preconf_transactions) = self.transactions() { + Ok(preconf_transactions.get(index as usize).cloned().map(RpcTxWithHash::from)) + } else { + Ok(None) + } } fn pending_state(&self) -> StarknetApiResult>> { @@ -122,6 +201,7 @@ where &self, hash: TxHash, ) -> StarknetApiResult> { + let _ = hash; Ok(None) } } diff --git a/crates/node/src/full/pending/state.rs b/crates/node/src/full/pending/state.rs index 316a4ecf1..bc3ba33eb 100644 --- a/crates/node/src/full/pending/state.rs +++ b/crates/node/src/full/pending/state.rs @@ -9,6 +9,7 @@ use katana_provider::{ProviderError, ProviderResult}; use katana_rpc_types::ConversionError; use tokio::runtime; +#[allow(unused)] pub struct PreconfStateProvider { pub base: Box, pub preconf_block_id: Option, From dcd8a608894280970bd18682f26835ad3a0eed0b Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Wed, 12 Nov 2025 14:19:54 -0500 Subject: [PATCH 49/49] remove unused code --- crates/rpc/rpc-types/src/broadcasted.rs | 319 +----------------------- 1 file changed, 3 insertions(+), 316 deletions(-) diff --git a/crates/rpc/rpc-types/src/broadcasted.rs b/crates/rpc/rpc-types/src/broadcasted.rs index fac7d5c93..03bfe7910 100644 --- a/crates/rpc/rpc-types/src/broadcasted.rs +++ b/crates/rpc/rpc-types/src/broadcasted.rs @@ -7,13 +7,10 @@ use katana_primitives::class::{ }; use katana_primitives::contract::Nonce; use katana_primitives::da::DataAvailabilityMode; -use katana_primitives::fee::{ - L1GasResourceBoundsMapping, ResourceBounds, ResourceBoundsMapping, Tip, -}; +use katana_primitives::fee::{ResourceBoundsMapping, Tip}; use katana_primitives::transaction::{ - DeclareTx, DeclareTxV0, DeclareTxV1, DeclareTxV2, DeclareTxV3, DeclareTxWithClass, - DeployAccountTx, DeployAccountTxV1, DeployAccountTxV3, ExecutableTx, ExecutableTxWithHash, - InvokeTx, InvokeTxV0, InvokeTxV1, InvokeTxV3, TxHash, TxType, + DeclareTx, DeclareTxV3, DeclareTxWithClass, DeployAccountTx, DeployAccountTxV3, ExecutableTx, + ExecutableTxWithHash, InvokeTx, InvokeTxV3, TxHash, TxType, }; use katana_primitives::utils::get_contract_address; use katana_primitives::{ContractAddress, Felt}; @@ -326,316 +323,6 @@ impl From for UntypedBroadcastedTx { } } -// From implementations for primitive types to broadcasted types - -impl From for BroadcastedInvokeTx { - fn from(tx: InvokeTx) -> Self { - match tx { - InvokeTx::V0(tx) => tx.into(), - InvokeTx::V1(tx) => tx.into(), - InvokeTx::V3(tx) => tx.into(), - } - } -} - -impl From for BroadcastedInvokeTx { - fn from(tx: InvokeTxV0) -> Self { - BroadcastedInvokeTx { - sender_address: tx.contract_address, - calldata: tx.calldata, - signature: tx.signature, - nonce: Felt::ZERO, - paymaster_data: vec![], - tip: 0.into(), - account_deployment_data: vec![], - resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, - l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, - }), - fee_data_availability_mode: DataAvailabilityMode::L1, - nonce_data_availability_mode: DataAvailabilityMode::L1, - is_query: false, - } - } -} - -impl From for BroadcastedInvokeTx { - fn from(tx: InvokeTxV1) -> Self { - BroadcastedInvokeTx { - sender_address: tx.sender_address, - calldata: tx.calldata, - signature: tx.signature, - nonce: tx.nonce, - paymaster_data: vec![], - tip: 0.into(), - account_deployment_data: vec![], - resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, - l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, - }), - fee_data_availability_mode: DataAvailabilityMode::L1, - nonce_data_availability_mode: DataAvailabilityMode::L1, - is_query: false, - } - } -} - -impl From for BroadcastedInvokeTx { - fn from(tx: InvokeTxV3) -> Self { - BroadcastedInvokeTx { - sender_address: tx.sender_address, - calldata: tx.calldata, - signature: tx.signature, - nonce: tx.nonce, - paymaster_data: tx.paymaster_data, - tip: tx.tip.into(), - account_deployment_data: tx.account_deployment_data, - resource_bounds: tx.resource_bounds, - fee_data_availability_mode: tx.fee_data_availability_mode, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - is_query: false, - } - } -} - -impl From for BroadcastedDeclareTx { - fn from(tx: DeclareTx) -> Self { - match tx { - DeclareTx::V0(tx) => tx.into(), - DeclareTx::V1(tx) => tx.into(), - DeclareTx::V2(tx) => tx.into(), - DeclareTx::V3(tx) => tx.into(), - } - } -} - -impl From for BroadcastedDeclareTx { - fn from(tx: DeclareTxV0) -> Self { - // Note: V0 declare transactions use legacy classes, but BroadcastedDeclareTx expects - // Sierra. The actual contract class needs to be provided separately. - BroadcastedDeclareTx { - sender_address: tx.sender_address, - compiled_class_hash: CompiledClassHash::ZERO, - signature: tx.signature, - nonce: Felt::ZERO, - contract_class: Arc::new(SierraClass::default()), - paymaster_data: vec![], - tip: 0.into(), - account_deployment_data: vec![], - resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, - l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, - }), - fee_data_availability_mode: DataAvailabilityMode::L1, - nonce_data_availability_mode: DataAvailabilityMode::L1, - is_query: false, - } - } -} - -impl From for BroadcastedDeclareTx { - fn from(tx: DeclareTxV1) -> Self { - // Note: V1 declare transactions use legacy classes, but BroadcastedDeclareTx expects - // Sierra. The actual contract class needs to be provided separately. - BroadcastedDeclareTx { - sender_address: tx.sender_address, - compiled_class_hash: CompiledClassHash::ZERO, - signature: tx.signature, - nonce: tx.nonce, - contract_class: Arc::new(SierraClass::default()), - paymaster_data: vec![], - tip: 0.into(), - account_deployment_data: vec![], - resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, - l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, - }), - fee_data_availability_mode: DataAvailabilityMode::L1, - nonce_data_availability_mode: DataAvailabilityMode::L1, - is_query: false, - } - } -} - -impl From for BroadcastedDeclareTx { - fn from(tx: DeclareTxV2) -> Self { - BroadcastedDeclareTx { - sender_address: tx.sender_address, - compiled_class_hash: tx.compiled_class_hash, - signature: tx.signature, - nonce: tx.nonce, - contract_class: Arc::new(SierraClass::default()), - paymaster_data: vec![], - tip: 0.into(), - account_deployment_data: vec![], - resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, - l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, - }), - fee_data_availability_mode: DataAvailabilityMode::L1, - nonce_data_availability_mode: DataAvailabilityMode::L1, - is_query: false, - } - } -} - -impl From for BroadcastedDeclareTx { - fn from(tx: DeclareTxV3) -> Self { - BroadcastedDeclareTx { - sender_address: tx.sender_address, - compiled_class_hash: tx.compiled_class_hash, - signature: tx.signature, - nonce: tx.nonce, - contract_class: Arc::new(SierraClass::default()), - paymaster_data: tx.paymaster_data, - tip: tx.tip.into(), - account_deployment_data: tx.account_deployment_data, - resource_bounds: tx.resource_bounds, - fee_data_availability_mode: tx.fee_data_availability_mode, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - is_query: false, - } - } -} - -impl From for BroadcastedDeclareTx { - fn from(tx: DeclareTxWithClass) -> Self { - // Convert ContractClass to SierraClass - // Note: This conversion may lose information for legacy classes - let contract_class = match tx.class.as_ref() { - ContractClass::Class(sierra) => Arc::new(SierraClass::from(sierra.clone())), - ContractClass::Legacy(_) => Arc::new(SierraClass::default()), - }; - - match tx.transaction { - DeclareTx::V0(tx) => BroadcastedDeclareTx { - sender_address: tx.sender_address, - compiled_class_hash: CompiledClassHash::ZERO, - signature: tx.signature, - nonce: Felt::ZERO, - contract_class, - paymaster_data: vec![], - tip: 0.into(), - account_deployment_data: vec![], - resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, - l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, - }), - fee_data_availability_mode: DataAvailabilityMode::L1, - nonce_data_availability_mode: DataAvailabilityMode::L1, - is_query: false, - }, - DeclareTx::V1(tx) => BroadcastedDeclareTx { - sender_address: tx.sender_address, - compiled_class_hash: CompiledClassHash::ZERO, - signature: tx.signature, - nonce: tx.nonce, - contract_class, - paymaster_data: vec![], - tip: 0.into(), - account_deployment_data: vec![], - resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, - l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, - }), - fee_data_availability_mode: DataAvailabilityMode::L1, - nonce_data_availability_mode: DataAvailabilityMode::L1, - is_query: false, - }, - DeclareTx::V2(tx) => BroadcastedDeclareTx { - sender_address: tx.sender_address, - compiled_class_hash: tx.compiled_class_hash, - signature: tx.signature, - nonce: tx.nonce, - contract_class, - paymaster_data: vec![], - tip: 0.into(), - account_deployment_data: vec![], - resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, - l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, - }), - fee_data_availability_mode: DataAvailabilityMode::L1, - nonce_data_availability_mode: DataAvailabilityMode::L1, - is_query: false, - }, - DeclareTx::V3(tx) => BroadcastedDeclareTx { - sender_address: tx.sender_address, - compiled_class_hash: tx.compiled_class_hash, - signature: tx.signature, - nonce: tx.nonce, - contract_class, - paymaster_data: tx.paymaster_data, - tip: tx.tip.into(), - account_deployment_data: tx.account_deployment_data, - resource_bounds: tx.resource_bounds, - fee_data_availability_mode: tx.fee_data_availability_mode, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - is_query: false, - }, - } - } -} - -impl From for BroadcastedDeployAccountTx { - fn from(tx: DeployAccountTx) -> Self { - match tx { - DeployAccountTx::V1(tx) => tx.into(), - DeployAccountTx::V3(tx) => tx.into(), - } - } -} - -impl From for BroadcastedDeployAccountTx { - fn from(tx: DeployAccountTxV1) -> Self { - BroadcastedDeployAccountTx { - signature: tx.signature, - nonce: tx.nonce, - contract_address_salt: tx.contract_address_salt, - constructor_calldata: tx.constructor_calldata, - class_hash: tx.class_hash, - paymaster_data: vec![], - tip: 0.into(), - resource_bounds: ResourceBoundsMapping::L1Gas(L1GasResourceBoundsMapping { - l1_gas: ResourceBounds { max_amount: 0, max_price_per_unit: tx.max_fee }, - l2_gas: ResourceBounds { max_amount: 0, max_price_per_unit: 0 }, - }), - fee_data_availability_mode: DataAvailabilityMode::L1, - nonce_data_availability_mode: DataAvailabilityMode::L1, - is_query: false, - } - } -} - -impl From for BroadcastedDeployAccountTx { - fn from(tx: DeployAccountTxV3) -> Self { - BroadcastedDeployAccountTx { - signature: tx.signature, - nonce: tx.nonce, - contract_address_salt: tx.contract_address_salt, - constructor_calldata: tx.constructor_calldata, - class_hash: tx.class_hash, - paymaster_data: tx.paymaster_data, - tip: tx.tip.into(), - resource_bounds: tx.resource_bounds, - fee_data_availability_mode: tx.fee_data_availability_mode, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - is_query: false, - } - } -} - -impl From for BroadcastedTx { - fn from(tx: ExecutableTx) -> Self { - match tx { - ExecutableTx::Invoke(tx) => BroadcastedTx::Invoke(tx.into()), - ExecutableTx::Declare(tx) => BroadcastedTx::Declare(tx.into()), - ExecutableTx::DeployAccount(tx) => BroadcastedTx::DeployAccount(tx.into()), - ExecutableTx::L1Handler(_) => unimplemented!(), - } - } -} - /// A broadcasted transaction. #[derive(Debug, Clone, Serialize)] pub struct BroadcastedTxWithChainId {