From 67c02182c769b11a267ccc5c52e9e261c91001b4 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Tue, 7 Jan 2025 12:33:11 -0500 Subject: [PATCH] feat(katana): starknet gas oracle placeholder (#2874) Starknet doesn't yet provide a way to query the L2 gas prices from the current RPC specs (but soon will be in [RPC v0.80 ](https://github.com/starkware-libs/starknet-specs/blob/c94df2c5866e11c866abd3d234b0d5df681073c3/api/starknet_api_openrpc.json#L1603-L1607)), so this is merely a placeholder to make sure that we can run the node with Starknet as the settlement layer when specified in the `ChainSpec`. Reference: #2870 --- crates/katana/chain-spec/src/lib.rs | 14 +++--- crates/katana/core/src/backend/gas_oracle.rs | 50 ++++++++++++-------- crates/katana/core/src/backend/mod.rs | 4 +- crates/katana/node/src/lib.rs | 13 ++--- 4 files changed, 42 insertions(+), 39 deletions(-) diff --git a/crates/katana/chain-spec/src/lib.rs b/crates/katana/chain-spec/src/lib.rs index 2bb3667fab..3da189d86f 100644 --- a/crates/katana/chain-spec/src/lib.rs +++ b/crates/katana/chain-spec/src/lib.rs @@ -10,7 +10,6 @@ use katana_primitives::chain::ChainId; use katana_primitives::class::ClassHash; use katana_primitives::contract::ContractAddress; use katana_primitives::da::L1DataAvailabilityMode; -use katana_primitives::eth::{self, Address as EthAddress}; use katana_primitives::genesis::allocation::{DevAllocationsGenerator, GenesisAllocation}; use katana_primitives::genesis::constant::{ get_fee_token_balance_base_storage_address, DEFAULT_ACCOUNT_CLASS_PUBKEY_STORAGE_SLOT, @@ -24,7 +23,7 @@ use katana_primitives::genesis::Genesis; use katana_primitives::state::StateUpdatesWithClasses; use katana_primitives::utils::split_u256; use katana_primitives::version::{ProtocolVersion, CURRENT_STARKNET_VERSION}; -use katana_primitives::Felt; +use katana_primitives::{eth, Felt}; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use starknet::core::utils::cairo_short_string_to_felt; @@ -63,7 +62,7 @@ pub struct FeeContracts { } #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(tag = "type", rename_all = "camelCase")] pub enum SettlementLayer { Ethereum { // The id of the settlement chain. @@ -72,11 +71,11 @@ pub enum SettlementLayer { // url for ethereum rpc provider rpc_url: Url, - account: EthAddress, + /// account on the ethereum network + account: eth::Address, // - The core appchain contract used to settlement - // - This is deployed on the L1 - core_contract: EthAddress, + core_contract: eth::Address, }, Starknet { @@ -86,11 +85,10 @@ pub enum SettlementLayer { // url for starknet rpc provider rpc_url: Url, - // the account address that was used to initialized the l1 deployments + /// account on the starknet network account: ContractAddress, // - The core appchain contract used to settlement - // - This is deployed on the L1 core_contract: ContractAddress, }, } diff --git a/crates/katana/core/src/backend/gas_oracle.rs b/crates/katana/core/src/backend/gas_oracle.rs index e0eb648116..1cf2ad3f70 100644 --- a/crates/katana/core/src/backend/gas_oracle.rs +++ b/crates/katana/core/src/backend/gas_oracle.rs @@ -16,24 +16,22 @@ const BUFFER_SIZE: usize = 60; const INTERVAL: Duration = Duration::from_secs(60); const ONE_GWEI: u128 = 1_000_000_000; -// TODO: implement a proper gas oracle function - sample the l1 gas and data gas prices -// currently this just return the hardcoded value set from the cli or if not set, the default value. #[derive(Debug)] -pub enum L1GasOracle { - Fixed(FixedL1GasOracle), - Sampled(SampledL1GasOracle), +pub enum GasOracle { + Fixed(FixedGasOracle), + Sampled(EthereumSampledGasOracle), } #[derive(Debug)] -pub struct FixedL1GasOracle { +pub struct FixedGasOracle { gas_prices: GasPrices, data_gas_prices: GasPrices, } #[derive(Debug, Clone)] -pub struct SampledL1GasOracle { +pub struct EthereumSampledGasOracle { prices: Arc>, - l1_provider: Url, + provider: Url, } #[derive(Debug, Default)] @@ -50,29 +48,39 @@ pub struct GasOracleWorker { pub data_gas_price_buffer: GasPriceBuffer, } -impl L1GasOracle { +impl GasOracle { pub fn fixed(gas_prices: GasPrices, data_gas_prices: GasPrices) -> Self { - L1GasOracle::Fixed(FixedL1GasOracle { gas_prices, data_gas_prices }) + GasOracle::Fixed(FixedGasOracle { gas_prices, data_gas_prices }) } - pub fn sampled(l1_provider: Url) -> Self { + /// Creates a new gas oracle that samples the gas prices from an Ethereum chain. + pub fn sampled_ethereum(eth_provider: Url) -> Self { let prices: Arc> = Arc::new(Mutex::new(SampledPrices::default())); - L1GasOracle::Sampled(SampledL1GasOracle { prices, l1_provider }) + GasOracle::Sampled(EthereumSampledGasOracle { prices, provider: eth_provider }) + } + + /// This is just placeholder for now, as Starknet doesn't provide a way to get the L2 gas + /// prices, we just return a fixed gas price values of 0. This is equivalent to calling + /// [`GasOracle::fixed`] with 0 values for both gas and data prices. + /// + /// The result of this is the same as running the node with fee disabled. + pub fn sampled_starknet() -> Self { + Self::fixed(GasPrices { eth: 0, strk: 0 }, GasPrices { eth: 0, strk: 0 }) } /// Returns the current gas prices. pub fn current_gas_prices(&self) -> GasPrices { match self { - L1GasOracle::Fixed(fixed) => fixed.current_gas_prices(), - L1GasOracle::Sampled(sampled) => sampled.prices.lock().gas_prices.clone(), + GasOracle::Fixed(fixed) => fixed.current_gas_prices(), + GasOracle::Sampled(sampled) => sampled.prices.lock().gas_prices.clone(), } } /// Returns the current data gas prices. pub fn current_data_gas_prices(&self) -> GasPrices { match self { - L1GasOracle::Fixed(fixed) => fixed.current_data_gas_prices(), - L1GasOracle::Sampled(sampled) => sampled.prices.lock().data_gas_prices.clone(), + GasOracle::Fixed(fixed) => fixed.current_data_gas_prices(), + GasOracle::Sampled(sampled) => sampled.prices.lock().data_gas_prices.clone(), } } @@ -81,7 +89,7 @@ impl L1GasOracle { Self::Fixed(..) => {} Self::Sampled(oracle) => { let prices = oracle.prices.clone(); - let l1_provider = oracle.l1_provider.clone(); + let l1_provider = oracle.provider.clone(); task_spawner.build_task().critical().name("L1 Gas Oracle Worker").spawn( async move { @@ -97,7 +105,7 @@ impl L1GasOracle { } } -impl SampledL1GasOracle { +impl EthereumSampledGasOracle { pub fn current_data_gas_prices(&self) -> GasPrices { self.prices.lock().data_gas_prices.clone() } @@ -107,7 +115,7 @@ impl SampledL1GasOracle { } } -impl FixedL1GasOracle { +impl FixedGasOracle { pub fn current_data_gas_prices(&self) -> GasPrices { self.data_gas_prices.clone() } @@ -289,10 +297,10 @@ mod tests { #[ignore = "Requires external assumption"] async fn test_gas_oracle() { let url = Url::parse("https://eth.merkle.io/").expect("Invalid URL"); - let oracle = L1GasOracle::sampled(url.clone()); + let oracle = GasOracle::sampled_ethereum(url.clone()); let shared_prices = match &oracle { - L1GasOracle::Sampled(sampled) => sampled.prices.clone(), + GasOracle::Sampled(sampled) => sampled.prices.clone(), _ => panic!("Expected sampled oracle"), }; diff --git a/crates/katana/core/src/backend/mod.rs b/crates/katana/core/src/backend/mod.rs index 12ab721309..94e0201f80 100644 --- a/crates/katana/core/src/backend/mod.rs +++ b/crates/katana/core/src/backend/mod.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use gas_oracle::L1GasOracle; +use gas_oracle::GasOracle; use katana_chain_spec::ChainSpec; use katana_executor::{ExecutionOutput, ExecutionResult, ExecutorFactory}; use katana_primitives::block::{ @@ -41,7 +41,7 @@ pub struct Backend { pub executor_factory: Arc, - pub gas_oracle: L1GasOracle, + pub gas_oracle: GasOracle, } impl Backend { diff --git a/crates/katana/node/src/lib.rs b/crates/katana/node/src/lib.rs index 02df9cd816..15037d291b 100644 --- a/crates/katana/node/src/lib.rs +++ b/crates/katana/node/src/lib.rs @@ -18,7 +18,7 @@ use dojo_metrics::{Report, Server as MetricsServer}; use hyper::Method; use jsonrpsee::RpcModule; use katana_chain_spec::SettlementLayer; -use katana_core::backend::gas_oracle::L1GasOracle; +use katana_core::backend::gas_oracle::GasOracle; use katana_core::backend::storage::Blockchain; use katana_core::backend::Backend; use katana_core::env::BlockContextGenerator; @@ -202,20 +202,17 @@ pub async fn build(mut config: Config) -> Result { // Check if the user specify a fixed gas price in the dev config. let gas_oracle = if let Some(fixed_prices) = &config.dev.fixed_gas_prices { // Use fixed gas prices if provided in the configuration - L1GasOracle::fixed(fixed_prices.gas_price.clone(), fixed_prices.data_gas_price.clone()) + GasOracle::fixed(fixed_prices.gas_price.clone(), fixed_prices.data_gas_price.clone()) } else if let Some(settlement) = &config.chain.settlement { match settlement { + SettlementLayer::Starknet { .. } => GasOracle::sampled_starknet(), SettlementLayer::Ethereum { rpc_url, .. } => { - // Default to a sampled gas oracle using the given provider - L1GasOracle::sampled(rpc_url.clone()) - } - SettlementLayer::Starknet { .. } => { - todo!("starknet gas oracle") + GasOracle::sampled_ethereum(rpc_url.clone()) } } } else { // Use default fixed gas prices if no url and if no fixed prices are provided - L1GasOracle::fixed( + GasOracle::fixed( GasPrices { eth: DEFAULT_ETH_L1_GAS_PRICE, strk: DEFAULT_STRK_L1_GAS_PRICE }, GasPrices { eth: DEFAULT_ETH_L1_DATA_GAS_PRICE, strk: DEFAULT_STRK_L1_DATA_GAS_PRICE }, )