From e40eb73739ac25910420f7147580f135bc42c3be Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 11 Jun 2024 00:18:58 +0300 Subject: [PATCH 01/48] node framework + config flow --- Cargo.lock | 17 +++++ Cargo.toml | 2 + core/bin/zksync_server/src/node_builder.rs | 11 ++++ .../config/src/configs/base_token_adjuster.rs | 26 ++++++++ core/lib/config/src/configs/general.rs | 2 + core/lib/config/src/configs/mod.rs | 1 + core/lib/config/src/lib.rs | 5 +- .../src/base_token_adjuster.rs | 20 ++++++ core/lib/protobuf_config/src/general.rs | 6 ++ core/lib/protobuf_config/src/lib.rs | 1 + .../proto/config/base_token_adjuster.proto | 7 ++ .../src/proto/config/general.proto | 2 + core/lib/zksync_core_leftovers/src/lib.rs | 3 + core/node/base_token_adjuster/Cargo.toml | 24 +++++++ core/node/base_token_adjuster/README.md | 11 ++++ .../src/base_token_adjuster.rs | 57 +++++++++++++++++ core/node/base_token_adjuster/src/lib.rs | 5 ++ core/node/node_framework/Cargo.toml | 1 + .../layers/base_token_adjuster.rs | 64 +++++++++++++++++++ .../src/implementations/layers/mod.rs | 1 + 20 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 core/lib/config/src/configs/base_token_adjuster.rs create mode 100644 core/lib/protobuf_config/src/base_token_adjuster.rs create mode 100644 core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto create mode 100644 core/node/base_token_adjuster/Cargo.toml create mode 100644 core/node/base_token_adjuster/README.md create mode 100644 core/node/base_token_adjuster/src/base_token_adjuster.rs create mode 100644 core/node/base_token_adjuster/src/lib.rs create mode 100644 core/node/node_framework/src/implementations/layers/base_token_adjuster.rs diff --git a/Cargo.lock b/Cargo.lock index ffea732c3bec..b0595bffb91d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7978,6 +7978,22 @@ dependencies = [ "sha3 0.10.8", ] +[[package]] +name = "zksync_base_token_adjuster" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "rand 0.8.5", + "tokio", + "tracing", + "vise", + "zksync_config", + "zksync_dal", + "zksync_types", + "zksync_utils", +] + [[package]] name = "zksync_basic_types" version = "0.1.0" @@ -8907,6 +8923,7 @@ dependencies = [ "tokio", "tracing", "vlog", + "zksync_base_token_adjuster", "zksync_block_reverter", "zksync_circuit_breaker", "zksync_commitment_generator", diff --git a/Cargo.toml b/Cargo.toml index de664288e150..6498d0f144be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = [ "core/node/contract_verification_server", "core/node/api_server", "core/node/tee_verifier_input_producer", + "core/node/base_token_adjuster", # Libraries "core/lib/db_connection", "core/lib/zksync_core_leftovers", @@ -263,3 +264,4 @@ zksync_node_consensus = { path = "core/node/consensus" } zksync_contract_verification_server = { path = "core/node/contract_verification_server" } zksync_node_api_server = { path = "core/node/api_server" } zksync_tee_verifier_input_producer = { path = "core/node/tee_verifier_input_producer" } +zksync_base_token_adjuster = {path = "core/node/base_token_adjuster"} diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 551683605479..74f7cc9e1f9e 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -15,6 +15,7 @@ use zksync_node_api_server::{ }; use zksync_node_framework::{ implementations::layers::{ + base_token_adjuster::BaseTokenAdjusterLayer, circuit_breaker_checker::CircuitBreakerCheckerLayer, commitment_generator::CommitmentGeneratorLayer, consensus::{ConsensusLayer, Mode as ConsensusMode}, @@ -409,6 +410,13 @@ impl MainNodeBuilder { Ok(self) } + fn add_base_token_adjuster_layer(mut self) -> anyhow::Result { + let config = try_load_config!(self.configs.base_token_adjuster); + self.node.add_layer(BaseTokenAdjusterLayer::new(config)); + + Ok(self) + } + pub fn build(mut self, mut components: Vec) -> anyhow::Result { // Add "base" layers (resources and helper tasks). self = self @@ -493,6 +501,9 @@ impl MainNodeBuilder { Component::VmRunnerProtectiveReads => { self = self.add_vm_runner_protective_reads_layer()?; } + Component::BaseTokenAdjuster => { + self = self.add_base_token_adjuster_layer()?; + } } } Ok(self.node.build()?) diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs new file mode 100644 index 000000000000..507ca7099d2f --- /dev/null +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -0,0 +1,26 @@ +use std::time::Duration; + +use serde::Deserialize; + +pub const DEFAULT_POLLING_INTERVAL_MS: u32 = 5000; + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct BaseTokenAdjusterConfig { + /// How often to poll external APIs for a new ETH<->BaseToken price. + pub price_polling_interval: Option, +} + +impl BaseTokenAdjusterConfig { + pub fn for_tests() -> Self { + Self { + polling_interval_ms: Some(DEFAULT_POLLING_INTERVAL_MS), + } + } + + pub fn price_polling_interval(&self) -> Duration { + match &self.polling_interval_ms { + Some(interval) => Duration::from_millis(interval as u64), + None => Duration::from_millis(DEFAULT_POLLING_INTERVAL_MS as u64), + } + } +} diff --git a/core/lib/config/src/configs/general.rs b/core/lib/config/src/configs/general.rs index 9f249d655f57..97bff71de89f 100644 --- a/core/lib/config/src/configs/general.rs +++ b/core/lib/config/src/configs/general.rs @@ -1,5 +1,6 @@ use crate::{ configs::{ + base_token_adjuster::BaseTokenAdjusterConfig, chain::{CircuitBreakerConfig, MempoolConfig, OperationsManagerConfig, StateKeeperConfig}, fri_prover_group::FriProverGroupConfig, house_keeper::HouseKeeperConfig, @@ -36,4 +37,5 @@ pub struct GeneralConfig { pub observability: Option, pub protective_reads_writer_config: Option, pub core_object_store: Option, + pub base_token_adjuster: Option, } diff --git a/core/lib/config/src/configs/mod.rs b/core/lib/config/src/configs/mod.rs index b2d9571ad292..f73abd994452 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -24,6 +24,7 @@ pub use self::{ }; pub mod api; +pub mod base_token_adjuster; pub mod chain; pub mod consensus; pub mod contract_verifier; diff --git a/core/lib/config/src/lib.rs b/core/lib/config/src/lib.rs index 66656e60b702..98dc194a56e8 100644 --- a/core/lib/config/src/lib.rs +++ b/core/lib/config/src/lib.rs @@ -1,8 +1,9 @@ #![allow(clippy::upper_case_acronyms, clippy::derive_partial_eq_without_eq)] pub use crate::configs::{ - ApiConfig, ContractVerifierConfig, ContractsConfig, DBConfig, EthConfig, EthWatchConfig, - GasAdjusterConfig, GenesisConfig, ObjectStoreConfig, PostgresConfig, SnapshotsCreatorConfig, + ApiConfig, BaseTokenAdjusterConfig, ContractVerifierConfig, ContractsConfig, DBConfig, + EthConfig, EthWatchConfig, GasAdjusterConfig, GenesisConfig, ObjectStoreConfig, PostgresConfig, + SnapshotsCreatorConfig, }; pub mod configs; diff --git a/core/lib/protobuf_config/src/base_token_adjuster.rs b/core/lib/protobuf_config/src/base_token_adjuster.rs new file mode 100644 index 000000000000..74590e6a0070 --- /dev/null +++ b/core/lib/protobuf_config/src/base_token_adjuster.rs @@ -0,0 +1,20 @@ +use zksync_config::configs::{self}; +use zksync_protobuf::ProtoRepr; + +use crate::proto::base_token_adjuster as proto; + +impl ProtoRepr for proto::BaseTokenAdjuster { + type Type = configs::base_token_adjuster::BaseTokenAdjusterConfig; + + fn read(&self) -> anyhow::Result { + Ok(configs::base_token_adjuster::BaseTokenAdjusterConfig { + polling_interval_ms: self.polling_interval, + }) + } + + fn build(this: &Self::Type) -> Self { + Self { + polling_interval: this.polling_interval_ms, + } + } +} diff --git a/core/lib/protobuf_config/src/general.rs b/core/lib/protobuf_config/src/general.rs index 834977759ae2..c2c1726d118f 100644 --- a/core/lib/protobuf_config/src/general.rs +++ b/core/lib/protobuf_config/src/general.rs @@ -41,6 +41,8 @@ impl ProtoRepr for proto::GeneralConfig { .context("protective_reads_writer")?, core_object_store: read_optional_repr(&self.core_object_store) .context("core_object_store")?, + base_token_adjuster_config: read_optional_repr(&self.base_token_adjuster) + .context("base_token_adjuster")?, }) } @@ -77,6 +79,10 @@ impl ProtoRepr for proto::GeneralConfig { .as_ref() .map(ProtoRepr::build), core_object_store: this.core_object_store.as_ref().map(ProtoRepr::build), + base_token_adjuster: this + .base_token_adjuster_config + .as_ref() + .map(ProtoRepr::build), } } } diff --git a/core/lib/protobuf_config/src/lib.rs b/core/lib/protobuf_config/src/lib.rs index 2fd9bbd9e059..023f1e343350 100644 --- a/core/lib/protobuf_config/src/lib.rs +++ b/core/lib/protobuf_config/src/lib.rs @@ -5,6 +5,7 @@ //! * protobuf json format mod api; +mod base_token_adjuster; mod chain; mod circuit_breaker; mod consensus; diff --git a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto new file mode 100644 index 000000000000..6e92cfc19cb5 --- /dev/null +++ b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package zksync.config.base_token_adjuster; + +message BaseTokenAdjuster { + optional uint32 polling_interval = 1; +} diff --git a/core/lib/protobuf_config/src/proto/config/general.proto b/core/lib/protobuf_config/src/proto/config/general.proto index fdfe257aecf1..8290463cf583 100644 --- a/core/lib/protobuf_config/src/proto/config/general.proto +++ b/core/lib/protobuf_config/src/proto/config/general.proto @@ -15,6 +15,7 @@ import "zksync/config/snapshots_creator.proto"; import "zksync/config/utils.proto"; import "zksync/config/vm_runner.proto"; import "zksync/config/object_store.proto"; +import "zksync/config/base_token_adjuster.proto"; message GeneralConfig { optional config.database.Postgres postgres = 1; @@ -39,4 +40,5 @@ message GeneralConfig { optional config.observability.Observability observability = 32; optional config.vm_runner.ProtectiveReadsWriter protective_reads_writer = 33; optional config.object_store.ObjectStore core_object_store = 34; + optional config.base_token_adjuster.BaseTokenAdjuster base_token_adjuster = 35; } diff --git a/core/lib/zksync_core_leftovers/src/lib.rs b/core/lib/zksync_core_leftovers/src/lib.rs index 1ed84263c2d5..e777844606d0 100644 --- a/core/lib/zksync_core_leftovers/src/lib.rs +++ b/core/lib/zksync_core_leftovers/src/lib.rs @@ -156,6 +156,8 @@ pub enum Component { CommitmentGenerator, /// VM runner-based component that saves protective reads to Postgres. VmRunnerProtectiveReads, + /// A component to adjust anything related to a chain's custom base token. + BaseTokenAdjuster, } #[derive(Debug)] @@ -195,6 +197,7 @@ impl FromStr for Components { "vm_runner_protective_reads" => { Ok(Components(vec![Component::VmRunnerProtectiveReads])) } + "base_token_adjuster" => Ok(Components(vec![Component::BaseTokenAdjuster])), other => Err(format!("{} is not a valid component name", other)), } } diff --git a/core/node/base_token_adjuster/Cargo.toml b/core/node/base_token_adjuster/Cargo.toml new file mode 100644 index 000000000000..2dd60cdee776 --- /dev/null +++ b/core/node/base_token_adjuster/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "zksync_base_token_adjuster" +version.workspace = true +edition.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true +keywords.workspace = true +categories.workspace = true + + +[dependencies] +vise.workspace = true +zksync_dal.workspace = true +zksync_utils.workspace = true +zksync_config.workspace = true +zksync_types.workspace = true + +tokio = { workspace = true, features = ["time"] } +anyhow.workspace = true +tracing.workspace = true +chrono.workspace = true +rand.workspace = true diff --git a/core/node/base_token_adjuster/README.md b/core/node/base_token_adjuster/README.md new file mode 100644 index 000000000000..18baeab97e7c --- /dev/null +++ b/core/node/base_token_adjuster/README.md @@ -0,0 +1,11 @@ +# Base Token Adjuster + +This crate contains an implementation of + +## Overview + +The Base Token Adjuster has 3 roles: + +1. bla +2. bli +3. blue diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs new file mode 100644 index 000000000000..ad0d0fa484fb --- /dev/null +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -0,0 +1,57 @@ +use std::{future::Future, time::Duration}; + +use anyhow::Context; +use chrono::{NaiveDateTime, Utc}; +use rand::Rng; +use tokio::sync::watch; +// use zksync_config::BaseTokenAdjusterConfig; +use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; +use zksync_da_client::{ + types::{DAError, IsTransient}, + DataAvailabilityClient, +}; +use zksync_dal::{ConnectionPool, Core, CoreDal}; +use zksync_types::L1BatchNumber; + +use crate::metrics::METRICS; + +#[derive(Debug)] +pub struct BaseTokenAdjuster { + pool: ConnectionPool, + config: BaseTokenAdjusterConfig, +} + +impl BaseTokenAdjuster { + pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { + Self { pool, config } + } + + pub async fn run(self, stop_receiver: watch::Receiver) -> anyhow::Result<()> { + let pool = self.pool.clone(); + loop { + if *stop_receiver.borrow() { + tracing::info!("Stop signal received, base_token_adjuster is shutting down"); + break; + } + + if let Err(err) = self.adjust(&pool).await { + tracing::warn!("adjust error {err:?}"); + } + } + Ok(()) + } + + /// adjust me + async fn adjust(&self, pool: &ConnectionPool) -> anyhow::Result<()> { + let mut conn = pool.connection_tagged("da_dispatcher").await?; + let batches = conn + .data_availability_dal() + .get_ready_for_da_dispatch_l1_batches(self.config.query_rows_limit() as usize) + .await?; + drop(conn); + + Ok(()) + } +} + +// TODO: Interesting to note age of price used by each batch. diff --git a/core/node/base_token_adjuster/src/lib.rs b/core/node/base_token_adjuster/src/lib.rs new file mode 100644 index 000000000000..8c9d94b567a3 --- /dev/null +++ b/core/node/base_token_adjuster/src/lib.rs @@ -0,0 +1,5 @@ +extern crate core; + +pub use self::base_token_adjuster::BaseTokenAdjuster; + +mod base_token_adjuster; diff --git a/core/node/node_framework/Cargo.toml b/core/node/node_framework/Cargo.toml index 8e2c915d5749..4f7571dd363a 100644 --- a/core/node/node_framework/Cargo.toml +++ b/core/node/node_framework/Cargo.toml @@ -45,6 +45,7 @@ zksync_tee_verifier_input_producer.workspace = true zksync_queued_job_processor.workspace = true zksync_reorg_detector.workspace = true zksync_vm_runner.workspace = true +zksync_base_token_adjuster.workspace = true tracing.workspace = true thiserror.workspace = true diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs new file mode 100644 index 000000000000..37004a3b2715 --- /dev/null +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -0,0 +1,64 @@ +use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; +use zksync_dal::Core; +use zksync_db_connection::connection_pool::ConnectionPool; + +use crate::{ + implementations::resources::{ + da_client::DAClientResource, + pools::{MasterPool, PoolResource}, + }, + service::{ServiceContext, StopReceiver}, + task::{Task, TaskId}, + wiring_layer::{WiringError, WiringLayer}, +}; + +/// A layer that wires the Base Token Adjuster task. +#[derive(Debug)] +pub struct BaseTokenAdjusterLayer { + config: BaseTokenAdjusterConfig, +} + +impl BaseTokenAdjusterLayer { + pub fn new(config: BaseTokenAdjusterConfig) -> Self { + Self { config } + } +} + +#[async_trait::async_trait] +impl WiringLayer for BaseTokenAdjusterLayer { + fn layer_name(&self) -> &'static str { + "base_token_adjuster" + } + + async fn wire(self: Box, mut context: ServiceContext<'_>) -> Result<(), WiringError> { + let master_pool_resource = context.get_resource::>().await?; + let master_pool = master_pool_resource.get().await?; + + context.add_task(Box::new(BaseTokenAdjusterTask { + main_pool: master_pool, + config: self.config, + })); + + Ok(()) + } +} + +#[derive(Debug)] +struct BaseTokenAdjusterTask { + main_pool: ConnectionPool, + config: BaseTokenAdjusterConfig, +} + +#[async_trait::async_trait] +impl Task for BaseTokenAdjusterTask { + fn id(&self) -> TaskId { + "base_token_adjuster".into() + } + + async fn run(self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { + let adjuster = + zksync_base_token_adjuster::BaseTokenAdjuster::new(self.main_pool, self.config); + + adjuster.run(stop_receiver.0).await + } +} diff --git a/core/node/node_framework/src/implementations/layers/mod.rs b/core/node/node_framework/src/implementations/layers/mod.rs index 1c171e84b5ba..6433cb46e05c 100644 --- a/core/node/node_framework/src/implementations/layers/mod.rs +++ b/core/node/node_framework/src/implementations/layers/mod.rs @@ -1,3 +1,4 @@ +pub mod base_token_adjuster; pub mod circuit_breaker_checker; pub mod commitment_generator; pub mod consensus; From 476addc490e31833dec068654da502daa2d4b738 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 11 Jun 2024 19:09:27 +0300 Subject: [PATCH 02/48] migration + dal --- Cargo.lock | 12 +++ Cargo.toml | 1 + .../config/src/configs/base_token_adjuster.rs | 10 +-- ...121747_add_base_token_price_table.down.sql | 1 + ...11121747_add_base_token_price_table.up.sql | 11 +++ core/lib/dal/src/base_token_price_dal.rs | 1 + core/lib/dal/src/lib.rs | 1 + core/lib/dal/src/models/mod.rs | 1 + .../src/models/storage_base_token_price.rs | 25 ++++++ core/lib/external_price_api/Cargo.toml | 19 +++++ core/lib/external_price_api/README.md | 7 ++ core/lib/external_price_api/src/lib.rs | 11 +++ .../src/base_token_adjuster.rs | 4 +- .../proto/config/base_token_adjuster.proto | 2 +- core/lib/types/src/base_token_price.rs | 10 +++ core/lib/types/src/lib.rs | 1 + .../src/base_token_adjuster.rs | 82 +++++++++++++++---- 17 files changed, 177 insertions(+), 22 deletions(-) create mode 100644 core/lib/dal/migrations/20240611121747_add_base_token_price_table.down.sql create mode 100644 core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql create mode 100644 core/lib/dal/src/base_token_price_dal.rs create mode 100644 core/lib/dal/src/models/storage_base_token_price.rs create mode 100644 core/lib/external_price_api/Cargo.toml create mode 100644 core/lib/external_price_api/README.md create mode 100644 core/lib/external_price_api/src/lib.rs create mode 100644 core/lib/types/src/base_token_price.rs diff --git a/Cargo.lock b/Cargo.lock index b0595bffb91d..d946a1e82946 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8656,6 +8656,18 @@ dependencies = [ "zksync_web3_decl", ] +[[package]] +name = "zksync_external_price_api" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "serde", + "tracing", + "zksync_config", + "zksync_types", +] + [[package]] name = "zksync_health_check" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 6498d0f144be..b7f1f7b27ee0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,7 @@ members = [ "core/lib/web3_decl", "core/lib/snapshots_applier", "core/lib/crypto_primitives", + "core/lib/external_price_api", # Test infrastructure "core/tests/test_account", "core/tests/loadnext", diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 507ca7099d2f..e5908e761197 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -2,25 +2,25 @@ use std::time::Duration; use serde::Deserialize; -pub const DEFAULT_POLLING_INTERVAL_MS: u32 = 5000; +pub const DEFAULT_INTERVAL_MS: u32 = 5000; #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct BaseTokenAdjusterConfig { /// How often to poll external APIs for a new ETH<->BaseToken price. - pub price_polling_interval: Option, + pub price_polling_interval_ms: u32, } impl BaseTokenAdjusterConfig { pub fn for_tests() -> Self { Self { - polling_interval_ms: Some(DEFAULT_POLLING_INTERVAL_MS), + price_polling_interval_ms: Some(DEFAULT_INTERVAL_MS), } } pub fn price_polling_interval(&self) -> Duration { - match &self.polling_interval_ms { + match &self.price_polling_interval_ms { Some(interval) => Duration::from_millis(interval as u64), - None => Duration::from_millis(DEFAULT_POLLING_INTERVAL_MS as u64), + None => Duration::from_millis(DEFAULT_INTERVAL_MS as u64), } } } diff --git a/core/lib/dal/migrations/20240611121747_add_base_token_price_table.down.sql b/core/lib/dal/migrations/20240611121747_add_base_token_price_table.down.sql new file mode 100644 index 000000000000..db15b29ecaa3 --- /dev/null +++ b/core/lib/dal/migrations/20240611121747_add_base_token_price_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS base_token_price; diff --git a/core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql b/core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql new file mode 100644 index 000000000000..4d3aa4c46cd3 --- /dev/null +++ b/core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE base_token_prices ( + id SERIAL PRIMARY KEY, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL, + + ratio_timestamp TIMESTAMP NOT NULL, + numerator NUMERIC NOT NULL, + denominator NUMERIC NOT NULL, + + used_in_l1 BOOLEAN NOT NULL DEFAULT FALSE +); diff --git a/core/lib/dal/src/base_token_price_dal.rs b/core/lib/dal/src/base_token_price_dal.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/core/lib/dal/src/base_token_price_dal.rs @@ -0,0 +1 @@ + diff --git a/core/lib/dal/src/lib.rs b/core/lib/dal/src/lib.rs index 45d1f94b486d..59ac02080530 100644 --- a/core/lib/dal/src/lib.rs +++ b/core/lib/dal/src/lib.rs @@ -26,6 +26,7 @@ use crate::{ transactions_web3_dal::TransactionsWeb3Dal, vm_runner_dal::VmRunnerDal, }; +mod base_token_price_dal; pub mod blocks_dal; pub mod blocks_web3_dal; pub mod consensus; diff --git a/core/lib/dal/src/models/mod.rs b/core/lib/dal/src/models/mod.rs index bc0e2c657da5..97661aa66ba9 100644 --- a/core/lib/dal/src/models/mod.rs +++ b/core/lib/dal/src/models/mod.rs @@ -3,6 +3,7 @@ use anyhow::Context as _; use zksync_db_connection::error::SqlxContext; use zksync_types::{ProtocolVersionId, H160, H256}; +pub mod storage_base_token_price; pub mod storage_eth_tx; pub mod storage_event; pub mod storage_log; diff --git a/core/lib/dal/src/models/storage_base_token_price.rs b/core/lib/dal/src/models/storage_base_token_price.rs new file mode 100644 index 000000000000..0aab094d16a2 --- /dev/null +++ b/core/lib/dal/src/models/storage_base_token_price.rs @@ -0,0 +1,25 @@ +use bigdecimal::BigDecimal; +use chrono::NaiveDateTime; +use zksync_types::base_token_price::BaseTokenPrice; + +/// Represents a row in the `storage_base_token_price` table. +#[derive(Debug, Clone)] +pub struct StorageBaseTokenPrice { + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub ratio_timestamp: NaiveDateTime, + pub numerator: BigDecimal, + pub denominator: BigDecimal, + pub used_in_l1: bool, +} + +impl From for BaseTokenPrice { + fn from(row: StorageBaseTokenPrice) -> BaseTokenPrice { + BaseTokenPrice { + ratio_timestamp: row.ratio_timestamp.and_utc(), + numerator: row.numerator, + denominator: row.denominator, + used_in_l1: row.used_in_l1, + } + } +} diff --git a/core/lib/external_price_api/Cargo.toml b/core/lib/external_price_api/Cargo.toml new file mode 100644 index 000000000000..646a82a4aebe --- /dev/null +++ b/core/lib/external_price_api/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "zksync_external_price_api" +version.workspace = true +edition.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true +keywords.workspace = true +categories.workspace = true + +[dependencies] +serde = { workspace = true, features = ["derive"] } +tracing = "0.1.40" +async-trait = "0.1.74" +anyhow.workspace = true + +zksync_config.workspace = true +zksync_types.workspace = true diff --git a/core/lib/external_price_api/README.md b/core/lib/external_price_api/README.md new file mode 100644 index 000000000000..d1604bbae7e7 --- /dev/null +++ b/core/lib/external_price_api/README.md @@ -0,0 +1,7 @@ +# Price API Client + +This crate provides a simple trait to be implemented by clients interacting with external price APIs to fetch +ETH<->BaseToken ratio. + +All clients should be implemented here and used by the node framework layer, which will be agnostic to the number of +clients available. diff --git a/core/lib/external_price_api/src/lib.rs b/core/lib/external_price_api/src/lib.rs new file mode 100644 index 000000000000..e179a787ff54 --- /dev/null +++ b/core/lib/external_price_api/src/lib.rs @@ -0,0 +1,11 @@ +use std::fmt; + +use async_trait::async_trait; +use zksync_types::Address; + +/// Trait that defines the interface for a client connecting with an external API to get prices. +#[async_trait] +pub trait PriceAPIClient: Sync + Send + fmt::Debug { + /// Returns the price for the input token address in dollars. + async fn fetch_price(&self, token_address: Address) -> Result; +} diff --git a/core/lib/protobuf_config/src/base_token_adjuster.rs b/core/lib/protobuf_config/src/base_token_adjuster.rs index 74590e6a0070..f19f36a84a4d 100644 --- a/core/lib/protobuf_config/src/base_token_adjuster.rs +++ b/core/lib/protobuf_config/src/base_token_adjuster.rs @@ -8,13 +8,13 @@ impl ProtoRepr for proto::BaseTokenAdjuster { fn read(&self) -> anyhow::Result { Ok(configs::base_token_adjuster::BaseTokenAdjusterConfig { - polling_interval_ms: self.polling_interval, + price_polling_interval_ms: self.price_polling_interval_ms, }) } fn build(this: &Self::Type) -> Self { Self { - polling_interval: this.polling_interval_ms, + price_polling_interval_ms: this.price_polling_interval_ms, } } } diff --git a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto index 6e92cfc19cb5..bf76323e5bac 100644 --- a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto +++ b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto @@ -3,5 +3,5 @@ syntax = "proto3"; package zksync.config.base_token_adjuster; message BaseTokenAdjuster { - optional uint32 polling_interval = 1; + optional uint32 price_polling_interval_ms = 1; } diff --git a/core/lib/types/src/base_token_price.rs b/core/lib/types/src/base_token_price.rs new file mode 100644 index 000000000000..213d009b27fd --- /dev/null +++ b/core/lib/types/src/base_token_price.rs @@ -0,0 +1,10 @@ +use chrono::{DateTime, Utc}; + +/// Represents the base token price at a given point in time. +#[derive(Debug, Clone)] +pub struct BaseTokenPrice { + pub ratio_timestamp: DateTime, + pub numerator: BigDecimal, + pub denominator: BigDecimal, + pub used_in_l1: bool, +} diff --git a/core/lib/types/src/lib.rs b/core/lib/types/src/lib.rs index fd5af40e35fb..545f88641fab 100644 --- a/core/lib/types/src/lib.rs +++ b/core/lib/types/src/lib.rs @@ -57,6 +57,7 @@ pub mod vm_trace; pub mod zk_evm_types; pub mod api; +pub mod base_token_price; pub mod eth_sender; pub mod helpers; pub mod proto; diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index ad0d0fa484fb..195420d2e5d8 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -1,4 +1,7 @@ -use std::{future::Future, time::Duration}; +use std::{ + future::Future, + time::{Duration, Instant}, +}; use anyhow::Context; use chrono::{NaiveDateTime, Utc}; @@ -10,7 +13,7 @@ use zksync_da_client::{ types::{DAError, IsTransient}, DataAvailabilityClient, }; -use zksync_dal::{ConnectionPool, Core, CoreDal}; +use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; use zksync_types::L1BatchNumber; use crate::metrics::METRICS; @@ -19,6 +22,7 @@ use crate::metrics::METRICS; pub struct BaseTokenAdjuster { pool: ConnectionPool, config: BaseTokenAdjusterConfig, + // PriceAPIClient } impl BaseTokenAdjuster { @@ -34,24 +38,74 @@ impl BaseTokenAdjuster { break; } - if let Err(err) = self.adjust(&pool).await { - tracing::warn!("adjust error {err:?}"); + let start_time = Instant::now(); + + match self.fetch_new_ratio().await { + Ok((new_numerator, new_denominator)) => { + let ratio_timestamp = Utc::now().naive_utc(); + + if let Err(err) = self + .persist_ratio(&new_numerator, &new_denominator, ratio_timestamp) + .await + { + tracing::error!("Error persisting ratio: {:?}", err); + } + + if let Err(err) = self + .maybe_update_l1(&new_numerator, &new_denominator, ratio_timestamp) + .await + { + tracing::error!("Error updating L1 ratio: {:?}", err); + } + } + Err(err) => tracing::error!("Error fetching new ratio: {:?}", err), } + + self.sleep_until_next_fetch(start_time).await; } Ok(()) } - /// adjust me - async fn adjust(&self, pool: &ConnectionPool) -> anyhow::Result<()> { - let mut conn = pool.connection_tagged("da_dispatcher").await?; - let batches = conn - .data_availability_dal() - .get_ready_for_da_dispatch_l1_batches(self.config.query_rows_limit() as usize) - .await?; - drop(conn); + async fn fetch_new_ratio(&self) -> anyhow::Result<(BigDecimal, BigDecimal)> { + let new_numerator = BigDecimal::from(1); + let new_denominator = BigDecimal::from(100); + Ok((new_numerator, new_denominator)) + } + async fn persist_ratio( + &self, + numerator: &BigDecimal, + denominator: &BigDecimal, + ratio_timestamp: NaiveDateTime, + ) -> anyhow::Result<()> { + let mut conn = pool.connection_tagged("base_token_adjuster").await?; + // let dal = TokenPriceDal::new(&conn); + // dal.insert_ratio( + // //TODO + // ).await?; Ok(()) } -} -// TODO: Interesting to note age of price used by each batch. + async fn maybe_update_l1( + &self, + numerator: &BigDecimal, + denominator: &BigDecimal, + ratio_timestamp: NaiveDateTime, + ) -> anyhow::Result<()> { + Ok(()) + } + + // Sleep for the remaining duration of the polling period + async fn sleep_until_next_fetch(&self, start_time: Instant) { + let elapsed_time = start_time.elapsed(); + let sleep_duration = if elapsed_time + >= Duration::from_millis(&self.config.price_polling_interval_ms as u64) + { + Duration::from_secs(0) + } else { + Duration::from_secs(self.config.external_fetching_poll_period) - elapsed_time + }; + + tokio::time::sleep(sleep_duration).await; + } +} From 8967d8692f8eb8f705059a9857594bd6f40c4fea Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 12 Jun 2024 08:59:39 +0300 Subject: [PATCH 03/48] use dal --- Cargo.lock | 1 + .../config/src/configs/base_token_adjuster.rs | 8 +-- core/lib/config/src/configs/mod.rs | 1 + core/lib/dal/src/base_token_dal.rs | 63 +++++++++++++++++++ core/lib/dal/src/base_token_price_dal.rs | 1 - core/lib/dal/src/lib.rs | 8 ++- .../src/models/storage_base_token_price.rs | 2 + core/lib/external_price_api/src/lib.rs | 4 +- core/lib/types/Cargo.toml | 1 + core/lib/types/src/base_token_price.rs | 11 ++++ .../src/base_token_adjuster.rs | 54 ++++++++-------- 11 files changed, 116 insertions(+), 38 deletions(-) create mode 100644 core/lib/dal/src/base_token_dal.rs delete mode 100644 core/lib/dal/src/base_token_price_dal.rs diff --git a/Cargo.lock b/Cargo.lock index d946a1e82946..1b5ada8ec607 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9403,6 +9403,7 @@ name = "zksync_types" version = "0.1.0" dependencies = [ "anyhow", + "bigdecimal", "bincode", "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono", diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index e5908e761197..50df1fea6c2c 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -1,13 +1,13 @@ use std::time::Duration; - use serde::Deserialize; -pub const DEFAULT_INTERVAL_MS: u32 = 5000; +// Fetch new prices every 30 seconds by default. +pub const DEFAULT_INTERVAL_MS: u32 = 30_000; #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct BaseTokenAdjusterConfig { /// How often to poll external APIs for a new ETH<->BaseToken price. - pub price_polling_interval_ms: u32, + pub price_polling_interval_ms: Option, } impl BaseTokenAdjusterConfig { @@ -18,7 +18,7 @@ impl BaseTokenAdjusterConfig { } pub fn price_polling_interval(&self) -> Duration { - match &self.price_polling_interval_ms { + match self.price_polling_interval_ms { Some(interval) => Duration::from_millis(interval as u64), None => Duration::from_millis(DEFAULT_INTERVAL_MS as u64), } diff --git a/core/lib/config/src/configs/mod.rs b/core/lib/config/src/configs/mod.rs index f73abd994452..0b3d8ca78bcb 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -21,6 +21,7 @@ pub use self::{ snapshots_creator::SnapshotsCreatorConfig, utils::PrometheusConfig, vm_runner::ProtectiveReadsWriterConfig, + base_token_adjuster::BaseTokenAdjusterConfig, }; pub mod api; diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs new file mode 100644 index 000000000000..adb3aeb19ff1 --- /dev/null +++ b/core/lib/dal/src/base_token_dal.rs @@ -0,0 +1,63 @@ +use bigdecimal::BigDecimal; +use zksync_db_connection::{ + connection::Connection, + error::DalResult, +}; + +use crate::{ + models::storage_base_token_price::StorageBaseTokenPrice, + Core, +}; + +#[derive(Debug)] +pub struct BaseTokenDal<'a, 'c> { + pub(crate) storage: &'a mut Connection<'c, Core>, +} + +impl BaseTokenDal<'_, '_> { + pub async fn insert_token_price( + &mut self, + numerator: BigDecimal, + denominator: BigDecimal, + ratio_timestamp: chrono::NaiveDateTime, + ) -> DalResult<()> { + let insert_result = sqlx::query!( + r#" + INSERT INTO + base_token_prices (numerator, denominator, ratio_timestamp) + VALUES + ($1, $2, $3) + "#, + numerator.to_string(), + denominator.to_string(), + ratio_timestamp, + ) + .instrument("insert_base_token_price") + .execute(self.storage) + .await?; + Ok(()) + } + + // TODO (PE-128): pub async fn mark_l1_update() + + pub async fn get_latest_price( + &mut self, + ) -> DalResult { + let row = sqlx::query_as!( + StorageBaseTokenPrice, + r#" + SELECT * + FROM + base_token_prices + ORDER BY + created_at DESC + LIMIT 1 + "#, + ) + .instrument("get_latest_base_token_price") + .fetch_one(self.storage) + .await?; + Ok(row) + } + +} \ No newline at end of file diff --git a/core/lib/dal/src/base_token_price_dal.rs b/core/lib/dal/src/base_token_price_dal.rs deleted file mode 100644 index 8b137891791f..000000000000 --- a/core/lib/dal/src/base_token_price_dal.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/lib/dal/src/lib.rs b/core/lib/dal/src/lib.rs index 59ac02080530..90120072a1ff 100644 --- a/core/lib/dal/src/lib.rs +++ b/core/lib/dal/src/lib.rs @@ -23,10 +23,10 @@ use crate::{ sync_dal::SyncDal, system_dal::SystemDal, tee_proof_generation_dal::TeeProofGenerationDal, tee_verifier_input_producer_dal::TeeVerifierInputProducerDal, tokens_dal::TokensDal, tokens_web3_dal::TokensWeb3Dal, transactions_dal::TransactionsDal, - transactions_web3_dal::TransactionsWeb3Dal, vm_runner_dal::VmRunnerDal, + transactions_web3_dal::TransactionsWeb3Dal, vm_runner_dal::VmRunnerDal, base_token_dal::BaseTokenDal, }; -mod base_token_price_dal; +pub mod base_token_dal; pub mod blocks_dal; pub mod blocks_web3_dal; pub mod consensus; @@ -126,6 +126,8 @@ where fn pruning_dal(&mut self) -> PruningDal<'_, 'a>; fn vm_runner_dal(&mut self) -> VmRunnerDal<'_, 'a>; + + fn base_token_dal(&mut self) -> BaseTokenDal<'_, 'a>; } #[derive(Clone, Debug)] @@ -244,4 +246,6 @@ impl<'a> CoreDal<'a> for Connection<'a, Core> { fn vm_runner_dal(&mut self) -> VmRunnerDal<'_, 'a> { VmRunnerDal { storage: self } } + + fn base_token_dal(&mut self) -> BaseTokenDal<'_, 'a> { BaseTokenDal { storage: self } } } diff --git a/core/lib/dal/src/models/storage_base_token_price.rs b/core/lib/dal/src/models/storage_base_token_price.rs index 0aab094d16a2..ed4b015b0ddf 100644 --- a/core/lib/dal/src/models/storage_base_token_price.rs +++ b/core/lib/dal/src/models/storage_base_token_price.rs @@ -5,6 +5,7 @@ use zksync_types::base_token_price::BaseTokenPrice; /// Represents a row in the `storage_base_token_price` table. #[derive(Debug, Clone)] pub struct StorageBaseTokenPrice { + pub id: i64, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub ratio_timestamp: NaiveDateTime, @@ -16,6 +17,7 @@ pub struct StorageBaseTokenPrice { impl From for BaseTokenPrice { fn from(row: StorageBaseTokenPrice) -> BaseTokenPrice { BaseTokenPrice { + id: row.id, ratio_timestamp: row.ratio_timestamp.and_utc(), numerator: row.numerator, denominator: row.denominator, diff --git a/core/lib/external_price_api/src/lib.rs b/core/lib/external_price_api/src/lib.rs index e179a787ff54..307e80635205 100644 --- a/core/lib/external_price_api/src/lib.rs +++ b/core/lib/external_price_api/src/lib.rs @@ -1,11 +1,11 @@ use std::fmt; use async_trait::async_trait; -use zksync_types::Address; +use zksync_types::{Address, base_token_price::BaseTokenAPIPrice}; /// Trait that defines the interface for a client connecting with an external API to get prices. #[async_trait] pub trait PriceAPIClient: Sync + Send + fmt::Debug { /// Returns the price for the input token address in dollars. - async fn fetch_price(&self, token_address: Address) -> Result; + async fn fetch_price(&self, token_address: Address) -> anyhow::Result; } diff --git a/core/lib/types/Cargo.toml b/core/lib/types/Cargo.toml index a562cccacbc1..10e59639629c 100644 --- a/core/lib/types/Cargo.toml +++ b/core/lib/types/Cargo.toml @@ -27,6 +27,7 @@ once_cell.workspace = true rlp.workspace = true serde.workspace = true serde_json.workspace = true +bigdecimal.workspace = true strum = { workspace = true, features = ["derive"] } thiserror.workspace = true num_enum.workspace = true diff --git a/core/lib/types/src/base_token_price.rs b/core/lib/types/src/base_token_price.rs index 213d009b27fd..ee59500dfa62 100644 --- a/core/lib/types/src/base_token_price.rs +++ b/core/lib/types/src/base_token_price.rs @@ -1,10 +1,21 @@ use chrono::{DateTime, Utc}; +use bigdecimal::BigDecimal; /// Represents the base token price at a given point in time. #[derive(Debug, Clone)] pub struct BaseTokenPrice { + pub id: i64, pub ratio_timestamp: DateTime, pub numerator: BigDecimal, pub denominator: BigDecimal, pub used_in_l1: bool, } + +/// Struct to represent API response containing denominator, numerator and optional timestamp. +#[derive(Debug)] +pub struct BaseTokenAPIPrice { + pub numerator: BigDecimal, + pub denominator: BigDecimal, + // Either the timestamp of the quote or the timestamp of the request. + pub ratio_timestamp: DateTime, +} \ No newline at end of file diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 195420d2e5d8..c51f7794933f 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -4,17 +4,12 @@ use std::{ }; use anyhow::Context; -use chrono::{NaiveDateTime, Utc}; +use chrono::{DateTime, NaiveDateTime, Utc}; use rand::Rng; use tokio::sync::watch; -// use zksync_config::BaseTokenAdjusterConfig; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; -use zksync_da_client::{ - types::{DAError, IsTransient}, - DataAvailabilityClient, -}; -use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; -use zksync_types::L1BatchNumber; +use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal, BaseTokenDal}; +use zksync_types::{L1BatchNumber, base_token_price::BaseTokenAPIPrice}; use crate::metrics::METRICS; @@ -41,11 +36,9 @@ impl BaseTokenAdjuster { let start_time = Instant::now(); match self.fetch_new_ratio().await { - Ok((new_numerator, new_denominator)) => { - let ratio_timestamp = Utc::now().naive_utc(); - + Ok(new_ratio) => { if let Err(err) = self - .persist_ratio(&new_numerator, &new_denominator, ratio_timestamp) + .persist_ratio(&new_ratio, &pool) .await { tracing::error!("Error persisting ratio: {:?}", err); @@ -66,35 +59,38 @@ impl BaseTokenAdjuster { Ok(()) } - async fn fetch_new_ratio(&self) -> anyhow::Result<(BigDecimal, BigDecimal)> { + async fn fetch_new_ratio(&self) -> anyhow::Result { let new_numerator = BigDecimal::from(1); let new_denominator = BigDecimal::from(100); - Ok((new_numerator, new_denominator)) + let ratio_timestamp = DateTime::from(Utc::now()); + + Ok(BaseTokenAPIPrice { + numerator: new_numerator, + denominator: new_denominator, + ratio_timestamp: ratio_timestamp, + }) } async fn persist_ratio( &self, - numerator: &BigDecimal, - denominator: &BigDecimal, - ratio_timestamp: NaiveDateTime, + api_price: &BaseTokenAPIPrice, + pool: &ConnectionPool, ) -> anyhow::Result<()> { let mut conn = pool.connection_tagged("base_token_adjuster").await?; - // let dal = TokenPriceDal::new(&conn); - // dal.insert_ratio( - // //TODO - // ).await?; - Ok(()) - } + conn.base_token_dal() + .insert_token_price( + api_price.numerator, + api_price.denominator, + api_price.ratio_timestamp.naive_utc(), + ) + .await?; + drop(conn); - async fn maybe_update_l1( - &self, - numerator: &BigDecimal, - denominator: &BigDecimal, - ratio_timestamp: NaiveDateTime, - ) -> anyhow::Result<()> { Ok(()) } + // TODO: async fn maybe_update_l1() + // Sleep for the remaining duration of the polling period async fn sleep_until_next_fetch(&self, start_time: Instant) { let elapsed_time = start_time.elapsed(); From 7e19e9483b40f34350928c02eb933e2bf081c8c2 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 12 Jun 2024 12:41:07 +0300 Subject: [PATCH 04/48] fmt --- .../config/src/configs/base_token_adjuster.rs | 2 +- core/lib/config/src/configs/mod.rs | 2 +- core/lib/dal/src/base_token_dal.rs | 29 +++++++------------ core/lib/dal/src/lib.rs | 15 ++++++---- core/lib/external_price_api/src/lib.rs | 2 +- core/lib/types/src/base_token_price.rs | 4 +-- .../src/base_token_adjuster.rs | 9 ++---- 7 files changed, 27 insertions(+), 36 deletions(-) diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 50df1fea6c2c..113c475aa401 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -1,5 +1,5 @@ -use std::time::Duration; use serde::Deserialize; +use std::time::Duration; // Fetch new prices every 30 seconds by default. pub const DEFAULT_INTERVAL_MS: u32 = 30_000; diff --git a/core/lib/config/src/configs/mod.rs b/core/lib/config/src/configs/mod.rs index 0b3d8ca78bcb..ea69ab512cd7 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -1,6 +1,7 @@ // Public re-exports pub use self::{ api::ApiConfig, + base_token_adjuster::BaseTokenAdjusterConfig, contract_verifier::ContractVerifierConfig, contracts::{ContractsConfig, EcosystemContracts}, database::{DBConfig, PostgresConfig}, @@ -21,7 +22,6 @@ pub use self::{ snapshots_creator::SnapshotsCreatorConfig, utils::PrometheusConfig, vm_runner::ProtectiveReadsWriterConfig, - base_token_adjuster::BaseTokenAdjusterConfig, }; pub mod api; diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index adb3aeb19ff1..ab2709b2244e 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -1,13 +1,7 @@ use bigdecimal::BigDecimal; -use zksync_db_connection::{ - connection::Connection, - error::DalResult, -}; +use zksync_db_connection::{connection::Connection, error::DalResult}; -use crate::{ - models::storage_base_token_price::StorageBaseTokenPrice, - Core, -}; +use crate::{models::storage_base_token_price::StorageBaseTokenPrice, Core}; #[derive(Debug)] pub struct BaseTokenDal<'a, 'c> { @@ -32,17 +26,15 @@ impl BaseTokenDal<'_, '_> { denominator.to_string(), ratio_timestamp, ) - .instrument("insert_base_token_price") - .execute(self.storage) - .await?; + .instrument("insert_base_token_price") + .execute(self.storage) + .await?; Ok(()) } // TODO (PE-128): pub async fn mark_l1_update() - pub async fn get_latest_price( - &mut self, - ) -> DalResult { + pub async fn get_latest_price(&mut self) -> DalResult { let row = sqlx::query_as!( StorageBaseTokenPrice, r#" @@ -54,10 +46,9 @@ impl BaseTokenDal<'_, '_> { LIMIT 1 "#, ) - .instrument("get_latest_base_token_price") - .fetch_one(self.storage) - .await?; + .instrument("get_latest_base_token_price") + .fetch_one(self.storage) + .await?; Ok(row) } - -} \ No newline at end of file +} diff --git a/core/lib/dal/src/lib.rs b/core/lib/dal/src/lib.rs index 90120072a1ff..3cb19e497866 100644 --- a/core/lib/dal/src/lib.rs +++ b/core/lib/dal/src/lib.rs @@ -12,10 +12,11 @@ pub use zksync_db_connection::{ }; use crate::{ - blocks_dal::BlocksDal, blocks_web3_dal::BlocksWeb3Dal, consensus_dal::ConsensusDal, - contract_verification_dal::ContractVerificationDal, eth_sender_dal::EthSenderDal, - events_dal::EventsDal, events_web3_dal::EventsWeb3Dal, factory_deps_dal::FactoryDepsDal, - proof_generation_dal::ProofGenerationDal, protocol_versions_dal::ProtocolVersionsDal, + base_token_dal::BaseTokenDal, blocks_dal::BlocksDal, blocks_web3_dal::BlocksWeb3Dal, + consensus_dal::ConsensusDal, contract_verification_dal::ContractVerificationDal, + eth_sender_dal::EthSenderDal, events_dal::EventsDal, events_web3_dal::EventsWeb3Dal, + factory_deps_dal::FactoryDepsDal, proof_generation_dal::ProofGenerationDal, + protocol_versions_dal::ProtocolVersionsDal, protocol_versions_web3_dal::ProtocolVersionsWeb3Dal, pruning_dal::PruningDal, snapshot_recovery_dal::SnapshotRecoveryDal, snapshots_creator_dal::SnapshotsCreatorDal, snapshots_dal::SnapshotsDal, storage_logs_dal::StorageLogsDal, @@ -23,7 +24,7 @@ use crate::{ sync_dal::SyncDal, system_dal::SystemDal, tee_proof_generation_dal::TeeProofGenerationDal, tee_verifier_input_producer_dal::TeeVerifierInputProducerDal, tokens_dal::TokensDal, tokens_web3_dal::TokensWeb3Dal, transactions_dal::TransactionsDal, - transactions_web3_dal::TransactionsWeb3Dal, vm_runner_dal::VmRunnerDal, base_token_dal::BaseTokenDal, + transactions_web3_dal::TransactionsWeb3Dal, vm_runner_dal::VmRunnerDal, }; pub mod base_token_dal; @@ -247,5 +248,7 @@ impl<'a> CoreDal<'a> for Connection<'a, Core> { VmRunnerDal { storage: self } } - fn base_token_dal(&mut self) -> BaseTokenDal<'_, 'a> { BaseTokenDal { storage: self } } + fn base_token_dal(&mut self) -> BaseTokenDal<'_, 'a> { + BaseTokenDal { storage: self } + } } diff --git a/core/lib/external_price_api/src/lib.rs b/core/lib/external_price_api/src/lib.rs index 307e80635205..ced897cfff50 100644 --- a/core/lib/external_price_api/src/lib.rs +++ b/core/lib/external_price_api/src/lib.rs @@ -1,7 +1,7 @@ use std::fmt; use async_trait::async_trait; -use zksync_types::{Address, base_token_price::BaseTokenAPIPrice}; +use zksync_types::{base_token_price::BaseTokenAPIPrice, Address}; /// Trait that defines the interface for a client connecting with an external API to get prices. #[async_trait] diff --git a/core/lib/types/src/base_token_price.rs b/core/lib/types/src/base_token_price.rs index ee59500dfa62..b8fee80c7e32 100644 --- a/core/lib/types/src/base_token_price.rs +++ b/core/lib/types/src/base_token_price.rs @@ -1,5 +1,5 @@ -use chrono::{DateTime, Utc}; use bigdecimal::BigDecimal; +use chrono::{DateTime, Utc}; /// Represents the base token price at a given point in time. #[derive(Debug, Clone)] @@ -18,4 +18,4 @@ pub struct BaseTokenAPIPrice { pub denominator: BigDecimal, // Either the timestamp of the quote or the timestamp of the request. pub ratio_timestamp: DateTime, -} \ No newline at end of file +} diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index c51f7794933f..adf554abb8e2 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -8,8 +8,8 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use rand::Rng; use tokio::sync::watch; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; -use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal, BaseTokenDal}; -use zksync_types::{L1BatchNumber, base_token_price::BaseTokenAPIPrice}; +use zksync_dal::{BaseTokenDal, BigDecimal, ConnectionPool, Core, CoreDal}; +use zksync_types::{base_token_price::BaseTokenAPIPrice, L1BatchNumber}; use crate::metrics::METRICS; @@ -37,10 +37,7 @@ impl BaseTokenAdjuster { match self.fetch_new_ratio().await { Ok(new_ratio) => { - if let Err(err) = self - .persist_ratio(&new_ratio, &pool) - .await - { + if let Err(err) = self.persist_ratio(&new_ratio, &pool).await { tracing::error!("Error persisting ratio: {:?}", err); } From 4ab267ef52c81b3afa698579330acab1426c973f Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 12 Jun 2024 12:42:32 +0300 Subject: [PATCH 05/48] fmt --- core/lib/config/src/configs/base_token_adjuster.rs | 3 ++- core/lib/dal/src/base_token_dal.rs | 14 ++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 113c475aa401..2167d620bf7c 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -1,6 +1,7 @@ -use serde::Deserialize; use std::time::Duration; +use serde::Deserialize; + // Fetch new prices every 30 seconds by default. pub const DEFAULT_INTERVAL_MS: u32 = 30_000; diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index ab2709b2244e..952ae02025e0 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -38,12 +38,14 @@ impl BaseTokenDal<'_, '_> { let row = sqlx::query_as!( StorageBaseTokenPrice, r#" - SELECT * - FROM - base_token_prices - ORDER BY - created_at DESC - LIMIT 1 + SELECT + * + FROM + base_token_prices + ORDER BY + created_at DESC + LIMIT + 1 "#, ) .instrument("get_latest_base_token_price") From 876a6e10a3926c03853d44eb5b5b027b033f6d33 Mon Sep 17 00:00:00 2001 From: Ivan Schasny Date: Wed, 12 Jun 2024 12:42:41 +0100 Subject: [PATCH 06/48] fix compilation --- core/bin/zksync_server/src/main.rs | 1 + .../config/src/configs/base_token_adjuster.rs | 4 +- core/lib/dal/src/base_token_dal.rs | 14 +++--- core/lib/protobuf_config/src/general.rs | 7 +-- .../proto/config/base_token_adjuster.proto | 2 +- .../src/temp_config_store/mod.rs | 6 ++- .../src/base_token_adjuster.rs | 44 ++++++++++--------- .../layers/base_token_adjuster.rs | 5 +-- 8 files changed, 42 insertions(+), 41 deletions(-) diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index c51cc5380253..781d45365240 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -308,5 +308,6 @@ fn load_env_config() -> anyhow::Result { snapshot_creator: SnapshotsCreatorConfig::from_env().ok(), protective_reads_writer_config: ProtectiveReadsWriterConfig::from_env().ok(), core_object_store: ObjectStoreConfig::from_env().ok(), + base_token_adjuster_config: None, }) } diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 2167d620bf7c..213707c9b481 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -3,12 +3,12 @@ use std::time::Duration; use serde::Deserialize; // Fetch new prices every 30 seconds by default. -pub const DEFAULT_INTERVAL_MS: u32 = 30_000; +pub const DEFAULT_INTERVAL_MS: u64 = 30_000; #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct BaseTokenAdjusterConfig { /// How often to poll external APIs for a new ETH<->BaseToken price. - pub price_polling_interval_ms: Option, + pub price_polling_interval_ms: Option, } impl BaseTokenAdjusterConfig { diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index 952ae02025e0..9e4f3f4374aa 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -1,5 +1,5 @@ use bigdecimal::BigDecimal; -use zksync_db_connection::{connection::Connection, error::DalResult}; +use zksync_db_connection::{connection::Connection, error::DalResult, instrument::InstrumentExt}; use crate::{models::storage_base_token_price::StorageBaseTokenPrice, Core}; @@ -11,19 +11,19 @@ pub struct BaseTokenDal<'a, 'c> { impl BaseTokenDal<'_, '_> { pub async fn insert_token_price( &mut self, - numerator: BigDecimal, - denominator: BigDecimal, - ratio_timestamp: chrono::NaiveDateTime, + numerator: &BigDecimal, + denominator: &BigDecimal, + ratio_timestamp: &chrono::NaiveDateTime, ) -> DalResult<()> { - let insert_result = sqlx::query!( + sqlx::query!( r#" INSERT INTO base_token_prices (numerator, denominator, ratio_timestamp) VALUES ($1, $2, $3) "#, - numerator.to_string(), - denominator.to_string(), + numerator, + denominator, ratio_timestamp, ) .instrument("insert_base_token_price") diff --git a/core/lib/protobuf_config/src/general.rs b/core/lib/protobuf_config/src/general.rs index c2c1726d118f..7f6a0d3a4b21 100644 --- a/core/lib/protobuf_config/src/general.rs +++ b/core/lib/protobuf_config/src/general.rs @@ -41,7 +41,7 @@ impl ProtoRepr for proto::GeneralConfig { .context("protective_reads_writer")?, core_object_store: read_optional_repr(&self.core_object_store) .context("core_object_store")?, - base_token_adjuster_config: read_optional_repr(&self.base_token_adjuster) + base_token_adjuster: read_optional_repr(&self.base_token_adjuster) .context("base_token_adjuster")?, }) } @@ -79,10 +79,7 @@ impl ProtoRepr for proto::GeneralConfig { .as_ref() .map(ProtoRepr::build), core_object_store: this.core_object_store.as_ref().map(ProtoRepr::build), - base_token_adjuster: this - .base_token_adjuster_config - .as_ref() - .map(ProtoRepr::build), + base_token_adjuster: this.base_token_adjuster.as_ref().map(ProtoRepr::build), } } } diff --git a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto index bf76323e5bac..67e97dd14cda 100644 --- a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto +++ b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto @@ -3,5 +3,5 @@ syntax = "proto3"; package zksync.config.base_token_adjuster; message BaseTokenAdjuster { - optional uint32 price_polling_interval_ms = 1; + optional uint64 price_polling_interval_ms = 1; } diff --git a/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs index 1f4c410ed9c1..3e1b779132ee 100644 --- a/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs +++ b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs @@ -12,8 +12,8 @@ use zksync_config::{ FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, GeneralConfig, ObservabilityConfig, PrometheusConfig, ProofDataHandlerConfig, ProtectiveReadsWriterConfig, }, - ApiConfig, ContractVerifierConfig, DBConfig, EthConfig, EthWatchConfig, GasAdjusterConfig, - ObjectStoreConfig, PostgresConfig, SnapshotsCreatorConfig, + ApiConfig, BaseTokenAdjusterConfig, ContractVerifierConfig, DBConfig, EthConfig, + EthWatchConfig, GasAdjusterConfig, ObjectStoreConfig, PostgresConfig, SnapshotsCreatorConfig, }; use zksync_protobuf::{repr::ProtoRepr, ProtoFmt}; @@ -62,6 +62,7 @@ pub struct TempConfigStore { pub snapshot_creator: Option, pub protective_reads_writer_config: Option, pub core_object_store: Option, + pub base_token_adjuster_config: Option, } impl TempConfigStore { @@ -89,6 +90,7 @@ impl TempConfigStore { observability: self.observability.clone(), protective_reads_writer_config: self.protective_reads_writer_config.clone(), core_object_store: self.core_object_store.clone(), + base_token_adjuster: self.base_token_adjuster_config.clone(), } } diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index adf554abb8e2..3a07f815250b 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -1,17 +1,10 @@ -use std::{ - future::Future, - time::{Duration, Instant}, -}; - -use anyhow::Context; -use chrono::{DateTime, NaiveDateTime, Utc}; -use rand::Rng; +use std::time::{Duration, Instant}; + +use chrono::{DateTime, Utc}; use tokio::sync::watch; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; -use zksync_dal::{BaseTokenDal, BigDecimal, ConnectionPool, Core, CoreDal}; -use zksync_types::{base_token_price::BaseTokenAPIPrice, L1BatchNumber}; - -use crate::metrics::METRICS; +use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; +use zksync_types::base_token_price::BaseTokenAPIPrice; #[derive(Debug)] pub struct BaseTokenAdjuster { @@ -42,7 +35,11 @@ impl BaseTokenAdjuster { } if let Err(err) = self - .maybe_update_l1(&new_numerator, &new_denominator, ratio_timestamp) + .maybe_update_l1( + &new_ratio.numerator, + &new_ratio.denominator, + &new_ratio.ratio_timestamp, + ) .await { tracing::error!("Error updating L1 ratio: {:?}", err); @@ -76,9 +73,9 @@ impl BaseTokenAdjuster { let mut conn = pool.connection_tagged("base_token_adjuster").await?; conn.base_token_dal() .insert_token_price( - api_price.numerator, - api_price.denominator, - api_price.ratio_timestamp.naive_utc(), + &api_price.numerator, + &api_price.denominator, + &api_price.ratio_timestamp.naive_utc(), ) .await?; drop(conn); @@ -86,17 +83,24 @@ impl BaseTokenAdjuster { Ok(()) } + async fn maybe_update_l1( + &self, + _numerator: &BigDecimal, + _denominator: &BigDecimal, + _ratio_timestamp: &DateTime, + ) -> anyhow::Result<()> { + Ok(()) + } + // TODO: async fn maybe_update_l1() // Sleep for the remaining duration of the polling period async fn sleep_until_next_fetch(&self, start_time: Instant) { let elapsed_time = start_time.elapsed(); - let sleep_duration = if elapsed_time - >= Duration::from_millis(&self.config.price_polling_interval_ms as u64) - { + let sleep_duration = if elapsed_time >= self.config.price_polling_interval() { Duration::from_secs(0) } else { - Duration::from_secs(self.config.external_fetching_poll_period) - elapsed_time + self.config.price_polling_interval() - elapsed_time }; tokio::time::sleep(sleep_duration).await; diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index 37004a3b2715..4b397cbebd17 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -3,10 +3,7 @@ use zksync_dal::Core; use zksync_db_connection::connection_pool::ConnectionPool; use crate::{ - implementations::resources::{ - da_client::DAClientResource, - pools::{MasterPool, PoolResource}, - }, + implementations::resources::pools::{MasterPool, PoolResource}, service::{ServiceContext, StopReceiver}, task::{Task, TaskId}, wiring_layer::{WiringError, WiringLayer}, From 3eb58d1972c861490e4a80208852230c31dccfa0 Mon Sep 17 00:00:00 2001 From: Ivan Schasny Date: Thu, 13 Jun 2024 12:56:34 +0100 Subject: [PATCH 07/48] wired base token adjuster into the fee model --- Cargo.lock | 4 +++ core/bin/zksync_server/src/node_builder.rs | 2 ++ core/lib/env_config/src/chain.rs | 14 ++++++++-- core/lib/types/src/base_token_price.rs | 4 +-- core/lib/types/src/fee_model.rs | 3 ++- core/lib/zksync_core_leftovers/Cargo.toml | 1 + core/lib/zksync_core_leftovers/src/lib.rs | 12 +++++++++ .../src/base_token_adjuster.rs | 23 ++++++++++++---- core/node/base_token_adjuster/src/lib.rs | 2 +- core/node/fee_model/Cargo.toml | 2 ++ core/node/fee_model/src/lib.rs | 27 ++++++++++++++++--- .../node/node_framework/examples/main_node.rs | 6 +++-- .../layers/base_token_adjuster.rs | 2 +- .../src/implementations/layers/l1_gas.rs | 17 ++++++++++-- core/node/state_keeper/Cargo.toml | 2 ++ core/node/state_keeper/src/io/tests/tester.rs | 12 +++++++++ 16 files changed, 114 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b5ada8ec607..fd21953c3960 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8379,6 +8379,7 @@ dependencies = [ "vise", "vlog", "vm_utils", + "zksync_base_token_adjuster", "zksync_circuit_breaker", "zksync_commitment_generator", "zksync_concurrency", @@ -8907,10 +8908,12 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "bigdecimal", "test-casing", "tokio", "tracing", "vise", + "zksync_base_token_adjuster", "zksync_config", "zksync_dal", "zksync_eth_client", @@ -9300,6 +9303,7 @@ dependencies = [ "tracing", "vise", "vm_utils", + "zksync_base_token_adjuster", "zksync_config", "zksync_contracts", "zksync_dal", diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 74f7cc9e1f9e..254bc2d6edd0 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -139,11 +139,13 @@ impl MainNodeBuilder { .context("Gas adjuster")?; let state_keeper_config = try_load_config!(self.configs.state_keeper_config); let eth_sender_config = try_load_config!(self.configs.eth); + let base_token_adjuster_config = try_load_config!(self.configs.base_token_adjuster); let sequencer_l1_gas_layer = SequencerL1GasLayer::new( gas_adjuster_config, self.genesis_config.clone(), state_keeper_config, try_load_config!(eth_sender_config.sender).pubdata_sending_mode, + base_token_adjuster_config, ); self.node.add_layer(sequencer_l1_gas_layer); Ok(self) diff --git a/core/lib/env_config/src/chain.rs b/core/lib/env_config/src/chain.rs index 5aaae9216736..89fa8d64d2cc 100644 --- a/core/lib/env_config/src/chain.rs +++ b/core/lib/env_config/src/chain.rs @@ -1,5 +1,9 @@ -use zksync_config::configs::chain::{ - CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, StateKeeperConfig, +use zksync_config::{ + configs::chain::{ + CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, + StateKeeperConfig, + }, + BaseTokenAdjusterConfig, }; use crate::{envy_load, FromEnv}; @@ -34,6 +38,12 @@ impl FromEnv for MempoolConfig { } } +impl FromEnv for BaseTokenAdjusterConfig { + fn from_env() -> anyhow::Result { + envy_load("base_token_adjuster", "BASE_TOKEN_ADJUSTER_") + } +} + #[cfg(test)] mod tests { use zksync_basic_types::{commitment::L1BatchCommitmentMode, L2ChainId}; diff --git a/core/lib/types/src/base_token_price.rs b/core/lib/types/src/base_token_price.rs index b8fee80c7e32..f1024cbdd3aa 100644 --- a/core/lib/types/src/base_token_price.rs +++ b/core/lib/types/src/base_token_price.rs @@ -2,7 +2,7 @@ use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; /// Represents the base token price at a given point in time. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct BaseTokenPrice { pub id: i64, pub ratio_timestamp: DateTime, @@ -12,7 +12,7 @@ pub struct BaseTokenPrice { } /// Struct to represent API response containing denominator, numerator and optional timestamp. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct BaseTokenAPIPrice { pub numerator: BigDecimal, pub denominator: BigDecimal, diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 79c9a94eda9f..4f368ba47b5a 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use zksync_config::configs::chain::{FeeModelVersion, StateKeeperConfig}; use zksync_system_constants::L1_GAS_PER_PUBDATA_BYTE; -use crate::ProtocolVersionId; +use crate::{base_token_price::BaseTokenPrice, ProtocolVersionId}; /// Fee input to be provided into the VM. It contains two options: /// - `L1Pegged`: L1 gas price is provided to the VM, and the pubdata price is derived from it. Using this option is required for the @@ -239,6 +239,7 @@ pub struct FeeParamsV2 { pub config: FeeModelConfigV2, pub l1_gas_price: u64, pub l1_pubdata_price: u64, + pub base_token_price: Option, } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] diff --git a/core/lib/zksync_core_leftovers/Cargo.toml b/core/lib/zksync_core_leftovers/Cargo.toml index c394342c6996..bf17e97ab33b 100644 --- a/core/lib/zksync_core_leftovers/Cargo.toml +++ b/core/lib/zksync_core_leftovers/Cargo.toml @@ -53,6 +53,7 @@ zksync_node_consensus.workspace = true zksync_contract_verification_server.workspace = true zksync_node_api_server.workspace = true zksync_tee_verifier_input_producer.workspace = true +zksync_base_token_adjuster.workspace = true multivm.workspace = true # Consensus dependenices diff --git a/core/lib/zksync_core_leftovers/src/lib.rs b/core/lib/zksync_core_leftovers/src/lib.rs index e777844606d0..b228a3d3d0d5 100644 --- a/core/lib/zksync_core_leftovers/src/lib.rs +++ b/core/lib/zksync_core_leftovers/src/lib.rs @@ -14,6 +14,7 @@ use tokio::{ sync::{oneshot, watch}, task::JoinHandle, }; +use zksync_base_token_adjuster::NodeBaseTokenAdjuster; use zksync_circuit_breaker::{ l1_txs::FailedL1TransactionChecker, replication_lag::ReplicationLagChecker, CircuitBreakerChecker, CircuitBreakers, @@ -295,6 +296,14 @@ pub async fn initialize_components( genesis_config.l1_batch_commit_data_generator_mode, ); + let arc_base_token_adjuster = Arc::new(NodeBaseTokenAdjuster::new( + connection_pool.clone(), + configs + .base_token_adjuster + .clone() + .context("base_token_adjuster")?, + )); + let (stop_sender, stop_receiver) = watch::channel(false); // Prometheus exporter and circuit breaker checker should run for every component configuration. @@ -377,6 +386,7 @@ pub async fn initialize_components( .context("gas_adjuster.get_or_init()")?; let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( bounded_gas_adjuster, + arc_base_token_adjuster.clone(), FeeModelConfig::from_state_keeper_config(&state_keeper_config), )); run_http_api( @@ -426,6 +436,7 @@ pub async fn initialize_components( .context("gas_adjuster.get_or_init()")?; let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( bounded_gas_adjuster, + arc_base_token_adjuster.clone(), FeeModelConfig::from_state_keeper_config(&state_keeper_config), )); run_ws_api( @@ -493,6 +504,7 @@ pub async fn initialize_components( .context("state_keeper_config")?; let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( bounded_gas_adjuster, + arc_base_token_adjuster.clone(), FeeModelConfig::from_state_keeper_config(&state_keeper_config), )); add_state_keeper_to_task_futures( diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 3a07f815250b..d7651b1c1c7f 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -1,24 +1,31 @@ -use std::time::{Duration, Instant}; +use std::{ + fmt::Debug, + time::{Duration, Instant}, +}; use chrono::{DateTime, Utc}; use tokio::sync::watch; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; -use zksync_types::base_token_price::BaseTokenAPIPrice; +use zksync_types::base_token_price::{BaseTokenAPIPrice, BaseTokenPrice}; + +pub trait BaseTokenAdjuster: Debug + Send + Sync { + fn get_last_ratio(&self) -> Option; +} #[derive(Debug)] -pub struct BaseTokenAdjuster { +pub struct NodeBaseTokenAdjuster { pool: ConnectionPool, config: BaseTokenAdjusterConfig, // PriceAPIClient } -impl BaseTokenAdjuster { +impl NodeBaseTokenAdjuster { pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { Self { pool, config } } - pub async fn run(self, stop_receiver: watch::Receiver) -> anyhow::Result<()> { + pub async fn run(&self, stop_receiver: watch::Receiver) -> anyhow::Result<()> { let pool = self.pool.clone(); loop { if *stop_receiver.borrow() { @@ -106,3 +113,9 @@ impl BaseTokenAdjuster { tokio::time::sleep(sleep_duration).await; } } + +impl BaseTokenAdjuster for NodeBaseTokenAdjuster { + fn get_last_ratio(&self) -> Option { + return None; + } +} diff --git a/core/node/base_token_adjuster/src/lib.rs b/core/node/base_token_adjuster/src/lib.rs index 8c9d94b567a3..d08199752626 100644 --- a/core/node/base_token_adjuster/src/lib.rs +++ b/core/node/base_token_adjuster/src/lib.rs @@ -1,5 +1,5 @@ extern crate core; -pub use self::base_token_adjuster::BaseTokenAdjuster; +pub use self::base_token_adjuster::{BaseTokenAdjuster, NodeBaseTokenAdjuster}; mod base_token_adjuster; diff --git a/core/node/fee_model/Cargo.toml b/core/node/fee_model/Cargo.toml index 7ac3c1d32e88..006a2c22da7e 100644 --- a/core/node/fee_model/Cargo.toml +++ b/core/node/fee_model/Cargo.toml @@ -17,6 +17,8 @@ zksync_config.workspace = true zksync_eth_client.workspace = true zksync_utils.workspace = true zksync_web3_decl.workspace = true +zksync_base_token_adjuster.workspace = true +bigdecimal.workspace = true tokio = { workspace = true, features = ["time"] } anyhow.workspace = true diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 793b5d4f8441..40feb352a508 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -1,6 +1,8 @@ -use std::{fmt, sync::Arc}; +use std::{fmt, ops::Div, sync::Arc}; use anyhow::Context as _; +use bigdecimal::ToPrimitive; +use zksync_base_token_adjuster::BaseTokenAdjuster; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ fee_model::{ @@ -59,6 +61,7 @@ impl dyn BatchFeeModelInputProvider { #[derive(Debug)] pub struct MainNodeFeeInputProvider { provider: Arc, + base_token_adjuster: Arc, config: FeeModelConfig, } @@ -73,14 +76,26 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { config, l1_gas_price: self.provider.estimate_effective_gas_price(), l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), + base_token_price: self + .base_token_adjuster + .get_last_ratio() + .and_then(|x| x.numerator.div(x.denominator).to_f64()), }), } } } impl MainNodeFeeInputProvider { - pub fn new(provider: Arc, config: FeeModelConfig) -> Self { - Self { provider, config } + pub fn new( + provider: Arc, + base_token_adjuster: Arc, + config: FeeModelConfig, + ) -> Self { + Self { + provider, + base_token_adjuster, + config, + } } } @@ -160,6 +175,7 @@ fn compute_batch_fee_model_input_v2( config, l1_gas_price, l1_pubdata_price, + base_token_price, } = params; let FeeModelConfigV2 { @@ -265,6 +281,7 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, + base_token_price: None, }; // We'll use scale factor of 3.0 @@ -291,6 +308,7 @@ mod tests { config, l1_gas_price: SMALL_L1_GAS_PRICE, l1_pubdata_price: SMALL_L1_GAS_PRICE, + base_token_price: None, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -316,6 +334,7 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, + base_token_price: None, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -342,6 +361,7 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, + base_token_price: None, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -368,6 +388,7 @@ mod tests { config: base_config, l1_gas_price: 1_000_000_000, l1_pubdata_price: 1_000_000_000, + base_token_price: None, }; let base_input = compute_batch_fee_model_input_v2(base_params, 1.0, 1.0); diff --git a/core/node/node_framework/examples/main_node.rs b/core/node/node_framework/examples/main_node.rs index a62f04af0334..edf7276f585d 100644 --- a/core/node/node_framework/examples/main_node.rs +++ b/core/node/node_framework/examples/main_node.rs @@ -15,8 +15,8 @@ use zksync_config::{ DatabaseSecrets, FriProofCompressorConfig, FriProverConfig, FriWitnessGeneratorConfig, L1Secrets, ObservabilityConfig, ProofDataHandlerConfig, }, - ApiConfig, ContractVerifierConfig, ContractsConfig, DBConfig, EthConfig, EthWatchConfig, - GasAdjusterConfig, GenesisConfig, ObjectStoreConfig, PostgresConfig, + ApiConfig, BaseTokenAdjusterConfig, ContractVerifierConfig, ContractsConfig, DBConfig, + EthConfig, EthWatchConfig, GasAdjusterConfig, GenesisConfig, ObjectStoreConfig, PostgresConfig, }; use zksync_env_config::FromEnv; use zksync_metadata_calculator::MetadataCalculatorConfig; @@ -111,6 +111,7 @@ impl MainNodeBuilder { let state_keeper_config = StateKeeperConfig::from_env()?; let genesis_config = GenesisConfig::from_env()?; let eth_sender_config = EthConfig::from_env()?; + let base_token_adjuster_config = BaseTokenAdjusterConfig::from_env()?; let sequencer_l1_gas_layer = SequencerL1GasLayer::new( gas_adjuster_config, genesis_config, @@ -119,6 +120,7 @@ impl MainNodeBuilder { .sender .context("eth_sender")? .pubdata_sending_mode, + base_token_adjuster_config, ); self.node.add_layer(sequencer_l1_gas_layer); Ok(self) diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index 4b397cbebd17..e2b37e64a6c8 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -54,7 +54,7 @@ impl Task for BaseTokenAdjusterTask { async fn run(self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { let adjuster = - zksync_base_token_adjuster::BaseTokenAdjuster::new(self.main_pool, self.config); + zksync_base_token_adjuster::NodeBaseTokenAdjuster::new(self.main_pool, self.config); adjuster.run(stop_receiver.0).await } diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index 8deafd4e2949..9efcddf11f8c 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -1,17 +1,20 @@ use std::sync::Arc; use anyhow::Context; +use zksync_base_token_adjuster::NodeBaseTokenAdjuster; use zksync_config::{ configs::{chain::StateKeeperConfig, eth_sender::PubdataSendingMode}, - GasAdjusterConfig, GenesisConfig, + BaseTokenAdjusterConfig, GasAdjusterConfig, GenesisConfig, }; use zksync_node_fee_model::{l1_gas_price::GasAdjuster, MainNodeFeeInputProvider}; use zksync_types::fee_model::FeeModelConfig; use crate::{ implementations::resources::{ - eth_interface::EthInterfaceResource, fee_input::FeeInputResource, + eth_interface::EthInterfaceResource, + fee_input::FeeInputResource, l1_tx_params::L1TxParamsResource, + pools::{PoolResource, ReplicaPool}, }, service::{ServiceContext, StopReceiver}, task::{Task, TaskId}, @@ -24,6 +27,7 @@ pub struct SequencerL1GasLayer { genesis_config: GenesisConfig, pubdata_sending_mode: PubdataSendingMode, state_keeper_config: StateKeeperConfig, + base_token_adjuster_config: BaseTokenAdjusterConfig, } impl SequencerL1GasLayer { @@ -32,12 +36,14 @@ impl SequencerL1GasLayer { genesis_config: GenesisConfig, state_keeper_config: StateKeeperConfig, pubdata_sending_mode: PubdataSendingMode, + base_token_adjuster_config: BaseTokenAdjusterConfig, ) -> Self { Self { gas_adjuster_config, genesis_config, pubdata_sending_mode, state_keeper_config, + base_token_adjuster_config, } } } @@ -60,8 +66,15 @@ impl WiringLayer for SequencerL1GasLayer { .context("GasAdjuster::new()")?; let gas_adjuster = Arc::new(adjuster); + let pool_resource = context.get_resource::>().await?; + let replica_pool = pool_resource.get().await?; + + let base_token_adjuster = + NodeBaseTokenAdjuster::new(replica_pool.clone(), self.base_token_adjuster_config); + let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( gas_adjuster.clone(), + Arc::new(base_token_adjuster), FeeModelConfig::from_state_keeper_config(&self.state_keeper_config), )); context.insert_resource(FeeInputResource(batch_fee_input_provider))?; diff --git a/core/node/state_keeper/Cargo.toml b/core/node/state_keeper/Cargo.toml index afc2d6ed826f..cc75a5fd50c7 100644 --- a/core/node/state_keeper/Cargo.toml +++ b/core/node/state_keeper/Cargo.toml @@ -26,6 +26,8 @@ zksync_contracts.workspace = true zksync_protobuf.workspace = true zksync_node_test_utils.workspace = true vm_utils.workspace = true +zksync_base_token_adjuster.workspace = true + anyhow.workspace = true async-trait.workspace = true diff --git a/core/node/state_keeper/src/io/tests/tester.rs b/core/node/state_keeper/src/io/tests/tester.rs index 84dfd4354b3c..280a4e854774 100644 --- a/core/node/state_keeper/src/io/tests/tester.rs +++ b/core/node/state_keeper/src/io/tests/tester.rs @@ -3,6 +3,7 @@ use std::{slice, sync::Arc, time::Duration}; use multivm::vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT; +use zksync_base_token_adjuster::BaseTokenAdjuster; use zksync_config::{ configs::{chain::StateKeeperConfig, eth_sender::PubdataSendingMode, wallets::Wallets}, GasAdjusterConfig, @@ -16,6 +17,7 @@ use zksync_node_test_utils::{ create_l1_batch, create_l2_block, create_l2_transaction, execute_l2_transaction, }; use zksync_types::{ + base_token_price::BaseTokenPrice, block::L2BlockHeader, commitment::L1BatchCommitmentMode, fee::TransactionExecutionMetrics, @@ -80,6 +82,7 @@ impl Tester { let gas_adjuster = Arc::new(self.create_gas_adjuster().await); MainNodeFeeInputProvider::new( gas_adjuster, + Arc::new(MockBaseTokenAdjuster {}), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), @@ -98,6 +101,7 @@ impl Tester { let gas_adjuster = Arc::new(self.create_gas_adjuster().await); let batch_fee_input_provider = MainNodeFeeInputProvider::new( gas_adjuster, + Arc::new(MockBaseTokenAdjuster {}), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), @@ -228,3 +232,11 @@ impl Tester { tx } } + +#[derive(Debug)] +struct MockBaseTokenAdjuster {} +impl BaseTokenAdjuster for MockBaseTokenAdjuster { + fn get_last_ratio(&self) -> Option { + None + } +} From e6c268630d9feb4a5b141ae63dcc6d38006f085b Mon Sep 17 00:00:00 2001 From: Ivan Schasny Date: Thu, 13 Jun 2024 16:06:26 +0100 Subject: [PATCH 08/48] Wired base token price to fee calculator --- core/node/fee_model/src/lib.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 40feb352a508..75224e4747c6 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -225,10 +225,19 @@ fn compute_batch_fee_model_input_v2( l1_pubdata_price + pubdata_overhead_wei }; - PubdataIndependentBatchFeeModelInput { - l1_gas_price, - fair_l2_gas_price, - fair_pubdata_price, + // if there is base token quote available - multiplying the final results onto it + // if not - ETH is used as a base token + match params.base_token_price { + Some(x) => PubdataIndependentBatchFeeModelInput { + l1_gas_price: (x * l1_gas_price as f64) as u64, + fair_l2_gas_price: (x * fair_l2_gas_price as f64) as u64, + fair_pubdata_price: (x * fair_pubdata_price as f64) as u64, + }, + None => PubdataIndependentBatchFeeModelInput { + l1_gas_price, + fair_l2_gas_price, + fair_pubdata_price, + }, } } From 63777b0364d4c784ab5148175818c77e110bc8e4 Mon Sep 17 00:00:00 2001 From: Ivan Schasny Date: Thu, 13 Jun 2024 16:12:16 +0100 Subject: [PATCH 09/48] add sqlx queries --- ...7d28571cad5438cbe24fb411494dc082c5acb.json | 16 ++++++ ...ba6b5dc624747adb73c9f94afe84e61f7513d.json | 56 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 core/lib/dal/.sqlx/query-8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb.json create mode 100644 core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json diff --git a/core/lib/dal/.sqlx/query-8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb.json b/core/lib/dal/.sqlx/query-8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb.json new file mode 100644 index 000000000000..a2d549b85dd4 --- /dev/null +++ b/core/lib/dal/.sqlx/query-8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO\n base_token_prices (numerator, denominator, ratio_timestamp)\n VALUES\n ($1, $2, $3)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Numeric", + "Numeric", + "Timestamp" + ] + }, + "nullable": [] + }, + "hash": "8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb" +} diff --git a/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json b/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json new file mode 100644 index 000000000000..00224846ffca --- /dev/null +++ b/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json @@ -0,0 +1,56 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n *\n FROM\n base_token_prices\n ORDER BY\n created_at DESC\n LIMIT\n 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "created_at", + "type_info": "Timestamp" + }, + { + "ordinal": 2, + "name": "updated_at", + "type_info": "Timestamp" + }, + { + "ordinal": 3, + "name": "ratio_timestamp", + "type_info": "Timestamp" + }, + { + "ordinal": 4, + "name": "numerator", + "type_info": "Numeric" + }, + { + "ordinal": 5, + "name": "denominator", + "type_info": "Numeric" + }, + { + "ordinal": 6, + "name": "used_in_l1", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d" +} From d3ba4c7d7ca2314cc9a3a1d00f8ae3722e3b9e2b Mon Sep 17 00:00:00 2001 From: Ivan Schasny Date: Thu, 13 Jun 2024 16:37:45 +0100 Subject: [PATCH 10/48] Lint fixes --- core/bin/zksync_server/src/main.rs | 7 ++++--- core/lib/config/src/configs/base_token_adjuster.rs | 6 +++--- core/lib/types/src/fee_model.rs | 2 +- core/node/base_token_adjuster/src/base_token_adjuster.rs | 6 +++--- core/node/fee_model/src/lib.rs | 2 +- prover/config/src/lib.rs | 9 +++++---- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index 781d45365240..2fd09d183cfe 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -16,8 +16,9 @@ use zksync_config::{ L1Secrets, ObservabilityConfig, PrometheusConfig, ProofDataHandlerConfig, ProtectiveReadsWriterConfig, Secrets, }, - ApiConfig, ContractVerifierConfig, DBConfig, EthConfig, EthWatchConfig, GasAdjusterConfig, - GenesisConfig, ObjectStoreConfig, PostgresConfig, SnapshotsCreatorConfig, + ApiConfig, BaseTokenAdjusterConfig, ContractVerifierConfig, DBConfig, EthConfig, + EthWatchConfig, GasAdjusterConfig, GenesisConfig, ObjectStoreConfig, PostgresConfig, + SnapshotsCreatorConfig, }; use zksync_core_leftovers::{ genesis_init, initialize_components, is_genesis_needed, setup_sigint_handler, @@ -308,6 +309,6 @@ fn load_env_config() -> anyhow::Result { snapshot_creator: SnapshotsCreatorConfig::from_env().ok(), protective_reads_writer_config: ProtectiveReadsWriterConfig::from_env().ok(), core_object_store: ObjectStoreConfig::from_env().ok(), - base_token_adjuster_config: None, + base_token_adjuster_config: BaseTokenAdjusterConfig::from_env().ok(), }) } diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 213707c9b481..d94a26409d72 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -7,7 +7,7 @@ pub const DEFAULT_INTERVAL_MS: u64 = 30_000; #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct BaseTokenAdjusterConfig { - /// How often to poll external APIs for a new ETH<->BaseToken price. + /// How often to poll external APIs for a new ETH<->Base-Token price. pub price_polling_interval_ms: Option, } @@ -20,8 +20,8 @@ impl BaseTokenAdjusterConfig { pub fn price_polling_interval(&self) -> Duration { match self.price_polling_interval_ms { - Some(interval) => Duration::from_millis(interval as u64), - None => Duration::from_millis(DEFAULT_INTERVAL_MS as u64), + Some(interval) => Duration::from_millis(interval), + None => Duration::from_millis(DEFAULT_INTERVAL_MS), } } } diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 4f368ba47b5a..1b39bd4e597b 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use zksync_config::configs::chain::{FeeModelVersion, StateKeeperConfig}; use zksync_system_constants::L1_GAS_PER_PUBDATA_BYTE; -use crate::{base_token_price::BaseTokenPrice, ProtocolVersionId}; +use crate::ProtocolVersionId; /// Fee input to be provided into the VM. It contains two options: /// - `L1Pegged`: L1 gas price is provided to the VM, and the pubdata price is derived from it. Using this option is required for the diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index d7651b1c1c7f..22a79d13c03e 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -63,12 +63,12 @@ impl NodeBaseTokenAdjuster { async fn fetch_new_ratio(&self) -> anyhow::Result { let new_numerator = BigDecimal::from(1); let new_denominator = BigDecimal::from(100); - let ratio_timestamp = DateTime::from(Utc::now()); + let ratio_timestamp = Utc::now(); Ok(BaseTokenAPIPrice { numerator: new_numerator, denominator: new_denominator, - ratio_timestamp: ratio_timestamp, + ratio_timestamp, }) } @@ -116,6 +116,6 @@ impl NodeBaseTokenAdjuster { impl BaseTokenAdjuster for NodeBaseTokenAdjuster { fn get_last_ratio(&self) -> Option { - return None; + None } } diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 75224e4747c6..cb5528427953 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -227,7 +227,7 @@ fn compute_batch_fee_model_input_v2( // if there is base token quote available - multiplying the final results onto it // if not - ETH is used as a base token - match params.base_token_price { + match base_token_price { Some(x) => PubdataIndependentBatchFeeModelInput { l1_gas_price: (x * l1_gas_price as f64) as u64, fair_l2_gas_price: (x * fair_l2_gas_price as f64) as u64, diff --git a/prover/config/src/lib.rs b/prover/config/src/lib.rs index f501dd2d6e06..3d27751b6d57 100644 --- a/prover/config/src/lib.rs +++ b/prover/config/src/lib.rs @@ -8,10 +8,10 @@ use zksync_config::{ }, fri_prover_group::FriProverGroupConfig, house_keeper::HouseKeeperConfig, - DatabaseSecrets, FriProofCompressorConfig, FriProverConfig, FriProverGatewayConfig, - FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, GeneralConfig, - ObjectStoreConfig, ObservabilityConfig, PrometheusConfig, ProofDataHandlerConfig, - ProtectiveReadsWriterConfig, + BaseTokenAdjusterConfig, DatabaseSecrets, FriProofCompressorConfig, FriProverConfig, + FriProverGatewayConfig, FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, + GeneralConfig, ObjectStoreConfig, ObservabilityConfig, PrometheusConfig, + ProofDataHandlerConfig, ProtectiveReadsWriterConfig, }, ApiConfig, ContractVerifierConfig, DBConfig, EthConfig, EthWatchConfig, GasAdjusterConfig, PostgresConfig, SnapshotsCreatorConfig, @@ -50,6 +50,7 @@ fn load_env_config() -> anyhow::Result { snapshot_creator: SnapshotsCreatorConfig::from_env().ok(), protective_reads_writer_config: ProtectiveReadsWriterConfig::from_env().ok(), core_object_store: ObjectStoreConfig::from_env().ok(), + base_token_adjuster_config: BaseTokenAdjusterConfig::from_env().ok(), }) } From 9b9fdf645ea263fd29a49e584f46a01ee6cdbfbc Mon Sep 17 00:00:00 2001 From: Ivan Schasny Date: Thu, 13 Jun 2024 16:56:16 +0100 Subject: [PATCH 11/48] update cargo lock --- prover/Cargo.lock | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 4bdd726e3082..a6c0aa45ecb5 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8387,6 +8387,22 @@ dependencies = [ "zkevm-assembly 1.5.0", ] +[[package]] +name = "zksync_base_token_adjuster" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "rand 0.8.5", + "tokio", + "tracing", + "vise", + "zksync_config", + "zksync_dal", + "zksync_types", + "zksync_utils", +] + [[package]] name = "zksync_basic_types" version = "0.1.0" @@ -8679,6 +8695,7 @@ dependencies = [ "vise", "vlog", "vm_utils", + "zksync_base_token_adjuster", "zksync_circuit_breaker", "zksync_commitment_generator", "zksync_concurrency", @@ -9069,9 +9086,11 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "bigdecimal", "tokio", "tracing", "vise", + "zksync_base_token_adjuster", "zksync_config", "zksync_dal", "zksync_eth_client", @@ -9442,6 +9461,7 @@ dependencies = [ "tracing", "vise", "vm_utils", + "zksync_base_token_adjuster", "zksync_config", "zksync_contracts", "zksync_dal", @@ -9525,6 +9545,7 @@ name = "zksync_types" version = "0.1.0" dependencies = [ "anyhow", + "bigdecimal", "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono", "derive_more", From 6f4cce6604eaf4bacd46abff7e022cd8233371f2 Mon Sep 17 00:00:00 2001 From: Ivan Schasny Date: Fri, 14 Jun 2024 09:36:11 +0100 Subject: [PATCH 12/48] Adjusted interface --- core/node/base_token_adjuster/src/base_token_adjuster.rs | 4 ++-- core/node/fee_model/src/lib.rs | 5 +---- core/node/state_keeper/src/io/tests/tester.rs | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 22a79d13c03e..b6c160ea8f31 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -10,7 +10,7 @@ use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; use zksync_types::base_token_price::{BaseTokenAPIPrice, BaseTokenPrice}; pub trait BaseTokenAdjuster: Debug + Send + Sync { - fn get_last_ratio(&self) -> Option; + fn get_last_ratio(&self) -> Option; } #[derive(Debug)] @@ -115,7 +115,7 @@ impl NodeBaseTokenAdjuster { } impl BaseTokenAdjuster for NodeBaseTokenAdjuster { - fn get_last_ratio(&self) -> Option { + fn get_last_ratio(&self) -> Option { None } } diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index cb5528427953..d7442bb756d7 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -76,10 +76,7 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { config, l1_gas_price: self.provider.estimate_effective_gas_price(), l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), - base_token_price: self - .base_token_adjuster - .get_last_ratio() - .and_then(|x| x.numerator.div(x.denominator).to_f64()), + base_token_price: self.base_token_adjuster.get_last_ratio(), }), } } diff --git a/core/node/state_keeper/src/io/tests/tester.rs b/core/node/state_keeper/src/io/tests/tester.rs index 280a4e854774..f47152e85694 100644 --- a/core/node/state_keeper/src/io/tests/tester.rs +++ b/core/node/state_keeper/src/io/tests/tester.rs @@ -236,7 +236,7 @@ impl Tester { #[derive(Debug)] struct MockBaseTokenAdjuster {} impl BaseTokenAdjuster for MockBaseTokenAdjuster { - fn get_last_ratio(&self) -> Option { + fn get_last_ratio(&self) -> Option { None } } From a2b45e14f72408e417b9c2abac70236c8ff675ed Mon Sep 17 00:00:00 2001 From: Ivan Schasny Date: Fri, 14 Jun 2024 16:21:32 +0100 Subject: [PATCH 13/48] add fee model tests --- core/node/fee_model/src/lib.rs | 198 ++++++++++++++++++++++++++++----- 1 file changed, 170 insertions(+), 28 deletions(-) diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index d7442bb756d7..327644b77cd7 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -1,7 +1,7 @@ use std::{fmt, ops::Div, sync::Arc}; use anyhow::Context as _; -use bigdecimal::ToPrimitive; +use bigdecimal::{ToPrimitive, Zero}; use zksync_base_token_adjuster::BaseTokenAdjuster; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ @@ -225,11 +225,26 @@ fn compute_batch_fee_model_input_v2( // if there is base token quote available - multiplying the final results onto it // if not - ETH is used as a base token match base_token_price { - Some(x) => PubdataIndependentBatchFeeModelInput { - l1_gas_price: (x * l1_gas_price as f64) as u64, - fair_l2_gas_price: (x * fair_l2_gas_price as f64) as u64, - fair_pubdata_price: (x * fair_pubdata_price as f64) as u64, - }, + Some(x) => { + let adjust_base_token = |n: u64| -> u64 { + // disregard quote as invalid if it's not a finite positive number + if !(x.is_finite() && x.is_sign_positive() && !x.is_zero()) { + return n; + } + let v = x * n as f64; + if v.is_finite() { + v as u64 + } else { + u64::MAX + } + }; + + PubdataIndependentBatchFeeModelInput { + l1_gas_price: adjust_base_token(l1_gas_price), + fair_l2_gas_price: adjust_base_token(fair_l2_gas_price), + fair_pubdata_price: adjust_base_token(fair_pubdata_price), + } + } None => PubdataIndependentBatchFeeModelInput { l1_gas_price, fair_l2_gas_price, @@ -257,6 +272,8 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { #[cfg(test)] mod tests { + use test_casing::test_casing; + use super::*; // To test that overflow never happens, we'll use giant L1 gas price, i.e. @@ -267,8 +284,19 @@ mod tests { // As a small small L2 gas price we'll use the value of 1 wei. const SMALL_L1_GAS_PRICE: u64 = 1; + // base token ration to use in the tests + const BASE_TOKEN_RATIO: f64 = 1.5; + + fn apply_base_token_ratio(base_token_ratio: Option, value: u64) -> u64 { + match base_token_ratio { + Some(x) => (x * value as f64) as u64, + None => value, + } + } + + #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_giant_numbers() { + fn test_compute_batch_fee_model_input_v2_giant_numbers(base_token_ratio: Option) { let config = FeeModelConfigV2 { minimal_l2_gas_price: GIANT_L1_GAS_PRICE, // We generally don't expect those values to be larger than 1. Still, in theory the operator @@ -287,19 +315,29 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token_price: None, + base_token_price: base_token_ratio, }; // We'll use scale factor of 3.0 let input = compute_batch_fee_model_input_v2(params, 3.0, 3.0); - assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE * 3); - assert_eq!(input.fair_l2_gas_price, 130_000_000_000_000); - assert_eq!(input.fair_pubdata_price, 15_300_000_000_000_000); + assert_eq!( + input.l1_gas_price, + apply_base_token_ratio(base_token_ratio, GIANT_L1_GAS_PRICE * 3) + ); + assert_eq!( + input.fair_l2_gas_price, + apply_base_token_ratio(base_token_ratio, 130_000_000_000_000) + ); + assert_eq!( + input.fair_pubdata_price, + apply_base_token_ratio(base_token_ratio, 15_300_000_000_000_000) + ); } + #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_small_numbers() { + fn test_compute_batch_fee_model_input_v2_small_numbers(base_token_ratio: Option) { // Here we assume that the operator wants to make the lives of users as cheap as possible. let config = FeeModelConfigV2 { minimal_l2_gas_price: SMALL_L1_GAS_PRICE, @@ -314,18 +352,28 @@ mod tests { config, l1_gas_price: SMALL_L1_GAS_PRICE, l1_pubdata_price: SMALL_L1_GAS_PRICE, - base_token_price: None, + base_token_price: base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); - assert_eq!(input.l1_gas_price, SMALL_L1_GAS_PRICE); - assert_eq!(input.fair_l2_gas_price, SMALL_L1_GAS_PRICE); - assert_eq!(input.fair_pubdata_price, SMALL_L1_GAS_PRICE); + assert_eq!( + input.l1_gas_price, + apply_base_token_ratio(base_token_ratio, SMALL_L1_GAS_PRICE) + ); + assert_eq!( + input.fair_l2_gas_price, + apply_base_token_ratio(base_token_ratio, SMALL_L1_GAS_PRICE) + ); + assert_eq!( + input.fair_pubdata_price, + apply_base_token_ratio(base_token_ratio, SMALL_L1_GAS_PRICE) + ); } + #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_only_pubdata_overhead() { + fn test_compute_batch_fee_model_input_v2_only_pubdata_overhead(base_token_ratio: Option) { // Here we use sensible config, but when only pubdata is used to close the batch let config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -340,19 +388,29 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token_price: None, + base_token_price: base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); - assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE); + assert_eq!( + input.l1_gas_price, + apply_base_token_ratio(base_token_ratio, GIANT_L1_GAS_PRICE) + ); // The fair L2 gas price is identical to the minimal one. - assert_eq!(input.fair_l2_gas_price, 100_000_000_000); + assert_eq!( + input.fair_l2_gas_price, + apply_base_token_ratio(base_token_ratio, 100_000_000_000) + ); // The fair pubdata price is the minimal one plus the overhead. - assert_eq!(input.fair_pubdata_price, 800_000_000_000_000); + assert_eq!( + input.fair_pubdata_price, + apply_base_token_ratio(base_token_ratio, 800_000_000_000_000) + ); } + #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_only_compute_overhead() { + fn test_compute_batch_fee_model_input_v2_only_compute_overhead(base_token_ratio: Option) { // Here we use sensible config, but when only compute is used to close the batch let config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -367,19 +425,29 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token_price: None, + base_token_price: base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); - assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE); + assert_eq!( + input.l1_gas_price, + apply_base_token_ratio(base_token_ratio, GIANT_L1_GAS_PRICE) + ); // The fair L2 gas price is identical to the minimal one, plus the overhead - assert_eq!(input.fair_l2_gas_price, 240_000_000_000); + assert_eq!( + input.fair_l2_gas_price, + apply_base_token_ratio(base_token_ratio, 240_000_000_000) + ); // The fair pubdata price is equal to the original one. - assert_eq!(input.fair_pubdata_price, GIANT_L1_GAS_PRICE); + assert_eq!( + input.fair_pubdata_price, + apply_base_token_ratio(base_token_ratio, GIANT_L1_GAS_PRICE) + ); } + #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_param_tweaking() { + fn test_compute_batch_fee_model_input_v2_param_tweaking(base_token_ratio: Option) { // In this test we generally checking that each param behaves as expected let base_config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -394,7 +462,7 @@ mod tests { config: base_config, l1_gas_price: 1_000_000_000, l1_pubdata_price: 1_000_000_000, - base_token_price: None, + base_token_price: base_token_ratio, }; let base_input = compute_batch_fee_model_input_v2(base_params, 1.0, 1.0); @@ -485,4 +553,78 @@ mod tests { "Max pubdata increase lowers pubdata price" ); } + + #[test] + fn test_compute_batch_fee_model_input_v2_base_token_overflow() { + let config = FeeModelConfigV2 { + minimal_l2_gas_price: 100_000_000_000, + compute_overhead_part: 1.0, + pubdata_overhead_part: 0.0, + batch_overhead_l1_gas: 700_000, + max_gas_per_batch: 500_000_000, + max_pubdata_per_batch: 100_000, + }; + + let params = FeeParamsV2 { + config, + l1_gas_price: GIANT_L1_GAS_PRICE, + l1_pubdata_price: GIANT_L1_GAS_PRICE, + base_token_price: Some(f64::MAX), + }; + + let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); + // in case of overflow, gas fees should be clamped at u64::MAX + assert_eq!(input.l1_gas_price, u64::MAX); + assert_eq!(input.fair_l2_gas_price, u64::MAX); + assert_eq!(input.fair_pubdata_price, u64::MAX); + } + + #[test] + fn test_compute_batch_fee_model_input_v2_base_token_disregard_non_positive_quote() { + let config = FeeModelConfigV2 { + minimal_l2_gas_price: 100_000_000_000, + compute_overhead_part: 1.0, + pubdata_overhead_part: 0.0, + batch_overhead_l1_gas: 700_000, + max_gas_per_batch: 500_000_000, + max_pubdata_per_batch: 100_000, + }; + + // these assertions ensure that invalid base token ratios such as negative numbers, nan, + // infinity, zero is disregarded + let test_fn = |params: FeeParamsV2| { + let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); + assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE); + assert_eq!(input.fair_l2_gas_price, 240_000_000_000); + assert_eq!(input.fair_pubdata_price, GIANT_L1_GAS_PRICE); + }; + + let params = FeeParamsV2 { + config, + l1_gas_price: GIANT_L1_GAS_PRICE, + l1_pubdata_price: GIANT_L1_GAS_PRICE, + base_token_price: Some(f64::NAN), + }; + + test_fn(FeeParamsV2 { + base_token_price: Some(f64::NAN), + ..params + }); + test_fn(FeeParamsV2 { + base_token_price: Some(f64::NEG_INFINITY), + ..params + }); + test_fn(FeeParamsV2 { + base_token_price: Some(f64::INFINITY), + ..params + }); + test_fn(FeeParamsV2 { + base_token_price: Some(0.0), + ..params + }); + test_fn(FeeParamsV2 { + base_token_price: Some(1.0), + ..params + }); + } } From 62d54ddb362d85139cc5ab3661e27aa620ff76eb Mon Sep 17 00:00:00 2001 From: Ivan Schasny Date: Fri, 14 Jun 2024 16:26:21 +0100 Subject: [PATCH 14/48] rename base_token_ratio --- core/lib/types/src/fee_model.rs | 2 +- .../src/base_token_adjuster.rs | 2 +- core/node/fee_model/src/lib.rs | 34 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 1b39bd4e597b..68a8fc14ed8b 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -239,7 +239,7 @@ pub struct FeeParamsV2 { pub config: FeeModelConfigV2, pub l1_gas_price: u64, pub l1_pubdata_price: u64, - pub base_token_price: Option, + pub base_token_ratio: Option, } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index b6c160ea8f31..6f6b2ee4ad2b 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -7,7 +7,7 @@ use chrono::{DateTime, Utc}; use tokio::sync::watch; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; -use zksync_types::base_token_price::{BaseTokenAPIPrice, BaseTokenPrice}; +use zksync_types::base_token_price::BaseTokenAPIPrice; pub trait BaseTokenAdjuster: Debug + Send + Sync { fn get_last_ratio(&self) -> Option; diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 327644b77cd7..0ab1a53d589e 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -1,7 +1,7 @@ -use std::{fmt, ops::Div, sync::Arc}; +use std::{fmt, sync::Arc}; use anyhow::Context as _; -use bigdecimal::{ToPrimitive, Zero}; +use bigdecimal::Zero; use zksync_base_token_adjuster::BaseTokenAdjuster; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ @@ -76,7 +76,7 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { config, l1_gas_price: self.provider.estimate_effective_gas_price(), l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), - base_token_price: self.base_token_adjuster.get_last_ratio(), + base_token_ratio: self.base_token_adjuster.get_last_ratio(), }), } } @@ -172,7 +172,7 @@ fn compute_batch_fee_model_input_v2( config, l1_gas_price, l1_pubdata_price, - base_token_price, + base_token_ratio, } = params; let FeeModelConfigV2 { @@ -224,7 +224,7 @@ fn compute_batch_fee_model_input_v2( // if there is base token quote available - multiplying the final results onto it // if not - ETH is used as a base token - match base_token_price { + match base_token_ratio { Some(x) => { let adjust_base_token = |n: u64| -> u64 { // disregard quote as invalid if it's not a finite positive number @@ -315,7 +315,7 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token_price: base_token_ratio, + base_token_ratio, }; // We'll use scale factor of 3.0 @@ -352,7 +352,7 @@ mod tests { config, l1_gas_price: SMALL_L1_GAS_PRICE, l1_pubdata_price: SMALL_L1_GAS_PRICE, - base_token_price: base_token_ratio, + base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -388,7 +388,7 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token_price: base_token_ratio, + base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -425,7 +425,7 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token_price: base_token_ratio, + base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -462,7 +462,7 @@ mod tests { config: base_config, l1_gas_price: 1_000_000_000, l1_pubdata_price: 1_000_000_000, - base_token_price: base_token_ratio, + base_token_ratio, }; let base_input = compute_batch_fee_model_input_v2(base_params, 1.0, 1.0); @@ -569,7 +569,7 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token_price: Some(f64::MAX), + base_token_ratio: Some(f64::MAX), }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -603,27 +603,27 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token_price: Some(f64::NAN), + base_token_ratio: Some(f64::NAN), }; test_fn(FeeParamsV2 { - base_token_price: Some(f64::NAN), + base_token_ratio: Some(f64::NAN), ..params }); test_fn(FeeParamsV2 { - base_token_price: Some(f64::NEG_INFINITY), + base_token_ratio: Some(f64::NEG_INFINITY), ..params }); test_fn(FeeParamsV2 { - base_token_price: Some(f64::INFINITY), + base_token_ratio: Some(f64::INFINITY), ..params }); test_fn(FeeParamsV2 { - base_token_price: Some(0.0), + base_token_ratio: Some(0.0), ..params }); test_fn(FeeParamsV2 { - base_token_price: Some(1.0), + base_token_ratio: Some(1.0), ..params }); } From 09aa9271999608204c949a50a7a807147a52bbce Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Thu, 20 Jun 2024 00:14:16 +0300 Subject: [PATCH 15/48] flow seems complete --- .../config/src/configs/base_token_adjuster.rs | 4 + core/lib/dal/sqlx-data.json | 3 + core/lib/dal/src/base_token_dal.rs | 11 +- .../src/base_token_adjuster.rs | 6 +- .../proto/config/base_token_adjuster.proto | 1 + core/lib/types/src/fee_model.rs | 10 +- core/lib/zksync_core_leftovers/src/lib.rs | 4 +- .../src/base_token_adjuster.rs | 89 ++++++++----- core/node/base_token_adjuster/src/lib.rs | 2 +- core/node/fee_model/src/lib.rs | 125 +++++++++++------- .../layers/base_token_adjuster.rs | 2 +- .../src/implementations/layers/l1_gas.rs | 4 +- core/node/state_keeper/src/io/tests/tester.rs | 2 +- prover/prover_dal/sqlx-data.json | 3 + 14 files changed, 167 insertions(+), 99 deletions(-) create mode 100644 core/lib/dal/sqlx-data.json create mode 100644 prover/prover_dal/sqlx-data.json diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index d94a26409d72..3e7c5b6c8ead 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -9,12 +9,16 @@ pub const DEFAULT_INTERVAL_MS: u64 = 30_000; pub struct BaseTokenAdjusterConfig { /// How often to poll external APIs for a new ETH<->Base-Token price. pub price_polling_interval_ms: Option, + + /// Base token symbol. + pub base_token: Option, } impl BaseTokenAdjusterConfig { pub fn for_tests() -> Self { Self { price_polling_interval_ms: Some(DEFAULT_INTERVAL_MS), + base_token: Option::from("ETH".to_string()), } } diff --git a/core/lib/dal/sqlx-data.json b/core/lib/dal/sqlx-data.json new file mode 100644 index 000000000000..95c8c858baaf --- /dev/null +++ b/core/lib/dal/sqlx-data.json @@ -0,0 +1,3 @@ +{ + "db": "PostgreSQL" +} \ No newline at end of file diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index 9e4f3f4374aa..3bd132ca2b2e 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -14,22 +14,25 @@ impl BaseTokenDal<'_, '_> { numerator: &BigDecimal, denominator: &BigDecimal, ratio_timestamp: &chrono::NaiveDateTime, - ) -> DalResult<()> { - sqlx::query!( + ) -> DalResult { + let row = sqlx::query!( r#" INSERT INTO base_token_prices (numerator, denominator, ratio_timestamp) VALUES ($1, $2, $3) + RETURNING + id "#, numerator, denominator, ratio_timestamp, ) .instrument("insert_base_token_price") - .execute(self.storage) + .fetch_one(self.storage) .await?; - Ok(()) + + Ok(row.id as usize) } // TODO (PE-128): pub async fn mark_l1_update() diff --git a/core/lib/protobuf_config/src/base_token_adjuster.rs b/core/lib/protobuf_config/src/base_token_adjuster.rs index f19f36a84a4d..6c7b8de207b9 100644 --- a/core/lib/protobuf_config/src/base_token_adjuster.rs +++ b/core/lib/protobuf_config/src/base_token_adjuster.rs @@ -8,13 +8,15 @@ impl ProtoRepr for proto::BaseTokenAdjuster { fn read(&self) -> anyhow::Result { Ok(configs::base_token_adjuster::BaseTokenAdjusterConfig { - price_polling_interval_ms: self.price_polling_interval_ms, + price_polling_interval_ms: self.price_polling_interval_ms.clone(), + base_token: self.base_token.clone(), }) } fn build(this: &Self::Type) -> Self { Self { - price_polling_interval_ms: this.price_polling_interval_ms, + price_polling_interval_ms: this.price_polling_interval_ms.clone(), + base_token: this.base_token.clone(), } } } diff --git a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto index 67e97dd14cda..f3f75c4b7b26 100644 --- a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto +++ b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto @@ -4,4 +4,5 @@ package zksync.config.base_token_adjuster; message BaseTokenAdjuster { optional uint64 price_polling_interval_ms = 1; + optional string base_token = 2; } diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 68a8fc14ed8b..b7f514bcadd8 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -1,3 +1,4 @@ +use bigdecimal::BigDecimal; use serde::{Deserialize, Serialize}; use zksync_config::configs::chain::{FeeModelVersion, StateKeeperConfig}; use zksync_system_constants::L1_GAS_PER_PUBDATA_BYTE; @@ -198,6 +199,8 @@ pub struct FeeModelConfigV2 { pub max_gas_per_batch: u64, /// The maximum amount of pubdata that can be used by the batch. Note that if the calldata is used as pubdata, this variable should not exceed 128kb. pub max_pubdata_per_batch: u64, + // /// A constant that indicates what base token is used. + // pub base_token: String, } impl Default for FeeModelConfig { @@ -234,15 +237,16 @@ pub struct FeeParamsV1 { pub l1_gas_price: u64, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, Clone)] pub struct FeeParamsV2 { pub config: FeeModelConfigV2, pub l1_gas_price: u64, pub l1_pubdata_price: u64, - pub base_token_ratio: Option, + pub base_token: &'static str, + pub base_token_ratio: Option, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, Clone)] pub enum FeeParams { V1(FeeParamsV1), V2(FeeParamsV2), diff --git a/core/lib/zksync_core_leftovers/src/lib.rs b/core/lib/zksync_core_leftovers/src/lib.rs index b228a3d3d0d5..485c4665bbcc 100644 --- a/core/lib/zksync_core_leftovers/src/lib.rs +++ b/core/lib/zksync_core_leftovers/src/lib.rs @@ -14,7 +14,7 @@ use tokio::{ sync::{oneshot, watch}, task::JoinHandle, }; -use zksync_base_token_adjuster::NodeBaseTokenAdjuster; +use zksync_base_token_adjuster::MainNodeBaseTokenAdjuster; use zksync_circuit_breaker::{ l1_txs::FailedL1TransactionChecker, replication_lag::ReplicationLagChecker, CircuitBreakerChecker, CircuitBreakers, @@ -296,7 +296,7 @@ pub async fn initialize_components( genesis_config.l1_batch_commit_data_generator_mode, ); - let arc_base_token_adjuster = Arc::new(NodeBaseTokenAdjuster::new( + let arc_base_token_adjuster = Arc::new(MainNodeBaseTokenAdjuster::new( connection_pool.clone(), configs .base_token_adjuster diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 6f6b2ee4ad2b..2054cba72df4 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -2,6 +2,8 @@ use std::{ fmt::Debug, time::{Duration, Instant}, }; +use std::ops::Div; +use anyhow::Context as _; use chrono::{DateTime, Utc}; use tokio::sync::watch; @@ -10,22 +12,28 @@ use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; use zksync_types::base_token_price::BaseTokenAPIPrice; pub trait BaseTokenAdjuster: Debug + Send + Sync { - fn get_last_ratio(&self) -> Option; + /// Returns the last ratio cached by the adjuster and ensure it's still usable. + fn get_last_ratio_and_check_usability(&self) -> anyhow::Result; + + /// Return configured symbol of the base token. + fn get_base_token(&self) -> String; } #[derive(Debug)] -pub struct NodeBaseTokenAdjuster { +/// BaseTokenAdjuster implementation for the main node (not the External Node). +pub struct MainNodeBaseTokenAdjuster { pool: ConnectionPool, config: BaseTokenAdjusterConfig, - // PriceAPIClient } -impl NodeBaseTokenAdjuster { +impl MainNodeBaseTokenAdjuster { pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { - Self { pool, config } + Self { pool, config} } - pub async fn run(&self, stop_receiver: watch::Receiver) -> anyhow::Result<()> { + /// Main loop for the base token adjuster. + /// Orchestrates fetching new ratio, persisting it, and updating L1. + pub async fn run(&mut self, stop_receiver: watch::Receiver) -> anyhow::Result<()> { let pool = self.pool.clone(); loop { if *stop_receiver.borrow() { @@ -37,19 +45,13 @@ impl NodeBaseTokenAdjuster { match self.fetch_new_ratio().await { Ok(new_ratio) => { - if let Err(err) = self.persist_ratio(&new_ratio, &pool).await { - tracing::error!("Error persisting ratio: {:?}", err); - } - - if let Err(err) = self - .maybe_update_l1( - &new_ratio.numerator, - &new_ratio.denominator, - &new_ratio.ratio_timestamp, - ) - .await - { - tracing::error!("Error updating L1 ratio: {:?}", err); + match self.persist_ratio(&new_ratio, &pool).await { + Ok(id) => { + if let Err(err) = self.maybe_update_l1(&new_ratio, id).await { + tracing::error!("Error updating L1 ratio: {:?}", err); + } + } + Err(err) => tracing::error!("Error persisting ratio: {:?}", err), } } Err(err) => tracing::error!("Error fetching new ratio: {:?}", err), @@ -60,6 +62,8 @@ impl NodeBaseTokenAdjuster { Ok(()) } + // TODO (PE-135): Use real API client to fetch new ratio through self.PriceAPIClient & mock for tests. + // For now, these hard coded values are also hard coded in the integration tests. async fn fetch_new_ratio(&self) -> anyhow::Result { let new_numerator = BigDecimal::from(1); let new_denominator = BigDecimal::from(100); @@ -76,9 +80,12 @@ impl NodeBaseTokenAdjuster { &self, api_price: &BaseTokenAPIPrice, pool: &ConnectionPool, - ) -> anyhow::Result<()> { - let mut conn = pool.connection_tagged("base_token_adjuster").await?; - conn.base_token_dal() + ) -> anyhow::Result<(usize)> { + let mut conn = pool.connection_tagged("base_token_adjuster") + .await + .context("Failed to obtain connection to the database")?; + + let id = conn.base_token_dal() .insert_token_price( &api_price.numerator, &api_price.denominator, @@ -87,20 +94,14 @@ impl NodeBaseTokenAdjuster { .await?; drop(conn); - Ok(()) + Ok(id) } - async fn maybe_update_l1( - &self, - _numerator: &BigDecimal, - _denominator: &BigDecimal, - _ratio_timestamp: &DateTime, - ) -> anyhow::Result<()> { + // TODO (PE-128): Complete L1 update flow. + async fn maybe_update_l1(&self, _new_ratio: &BaseTokenAPIPrice, _id: usize) -> anyhow::Result<()> { Ok(()) } - // TODO: async fn maybe_update_l1() - // Sleep for the remaining duration of the polling period async fn sleep_until_next_fetch(&self, start_time: Instant) { let elapsed_time = start_time.elapsed(); @@ -114,8 +115,28 @@ impl NodeBaseTokenAdjuster { } } -impl BaseTokenAdjuster for NodeBaseTokenAdjuster { - fn get_last_ratio(&self) -> Option { - None +impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { + // TODO (PE-129): Implement latest ratio usability logic. + async fn get_last_ratio_and_check_usability(&self) -> anyhow::Result { + let mut conn = self.pool.connection_tagged("base_token_adjuster") + .await + .context("Failed to obtain connection to the database")?; + + let last_storage_ratio = conn.base_token_dal() + .get_latest_price() + .await?; + drop(conn); + + let last_ratio = last_storage_ratio.numerator.div(&last_storage_ratio.denominator); + + Ok(last_ratio) + } + + /// Return configured symbol of the base token. If not configured, return "ETH". + fn get_base_token(&self) -> String { + match self.config.base_token.clone() { + Some(token) => token, + None => "ETH".to_string(), + } } } diff --git a/core/node/base_token_adjuster/src/lib.rs b/core/node/base_token_adjuster/src/lib.rs index d08199752626..1b025f81c26e 100644 --- a/core/node/base_token_adjuster/src/lib.rs +++ b/core/node/base_token_adjuster/src/lib.rs @@ -1,5 +1,5 @@ extern crate core; -pub use self::base_token_adjuster::{BaseTokenAdjuster, NodeBaseTokenAdjuster}; +pub use self::base_token_adjuster::{BaseTokenAdjuster, MainNodeBaseTokenAdjuster}; mod base_token_adjuster; diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 0ab1a53d589e..ca84c871b3ec 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -76,7 +76,8 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { config, l1_gas_price: self.provider.estimate_effective_gas_price(), l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), - base_token_ratio: self.base_token_adjuster.get_last_ratio(), + base_token: self.base_token_adjuster.get_base_token(), + base_token_ratio: self.base_token_adjuster.get_last_ratio_and_check_usability(), }), } } @@ -172,6 +173,7 @@ fn compute_batch_fee_model_input_v2( config, l1_gas_price, l1_pubdata_price, + base_token, base_token_ratio, } = params; @@ -222,37 +224,61 @@ fn compute_batch_fee_model_input_v2( l1_pubdata_price + pubdata_overhead_wei }; - // if there is base token quote available - multiplying the final results onto it - // if not - ETH is used as a base token - match base_token_ratio { - Some(x) => { - let adjust_base_token = |n: u64| -> u64 { - // disregard quote as invalid if it's not a finite positive number - if !(x.is_finite() && x.is_sign_positive() && !x.is_zero()) { - return n; - } - let v = x * n as f64; - if v.is_finite() { - v as u64 + // If the base token is ETH, simply return prices calculated in WEI. Otherwise, convert them to the base token. + match base_token { + "ETH" => PubdataIndependentBatchFeeModelInput { + l1_gas_price, + fair_l2_gas_price, + fair_pubdata_price, + }, + _ => { + let convert_to_base_token_price = |price_in_wei: u64| -> u64 { + let converted_price = base_token_ratio * price_in_wei as f64; + if converted_price.is_finite() { + converted_price as u64 } else { u64::MAX } }; PubdataIndependentBatchFeeModelInput { - l1_gas_price: adjust_base_token(l1_gas_price), - fair_l2_gas_price: adjust_base_token(fair_l2_gas_price), - fair_pubdata_price: adjust_base_token(fair_pubdata_price), + l1_gas_price: convert_to_base_token_price(l1_gas_price), + fair_l2_gas_price: convert_to_base_token_price(fair_l2_gas_price), + fair_pubdata_price: convert_to_base_token_price(fair_pubdata_price), } } - None => PubdataIndependentBatchFeeModelInput { - l1_gas_price, - fair_l2_gas_price, - fair_pubdata_price, - }, } } +fn convert_to_base_token_price(base_token_ratio: BigDecimal, price_in_wei: u64) -> u64 { + // Convert to BigDecimal so the conversion is more precise. + let price_in_wei_bd = BigDecimal::from(price_in_wei); + + let converted_price_bd = base_token_ratio * price_in_wei_bd; + + // If the conversion is not possible, return the maximum value. + match converted_price_bd.to_u64() { + Some(converted_price) => converted_price, + None => { + // If we can't convert it's either because the value is too large or something unexpected. + // In either case we clamp down to u64::MAX. + if converted_price_bd > BigDecimal::from(u64::MAX) { + tracing::warn!( + "Conversion to base token price failed: converted price is too large: {}", + converted_price_bd + ); + } else { + tracing::error!( + "Conversion to base token price failed: converted price is not a valid u64: {}", + converted_price_bd + ); + } + u64::MAX + } + } +} + + /// Mock [`BatchFeeModelInputProvider`] implementation that returns a constant value. /// Intended to be used in tests only. #[derive(Debug)] @@ -272,6 +298,7 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { #[cfg(test)] mod tests { + use bigdecimal::BigDecimal; use test_casing::test_casing; use super::*; @@ -284,19 +311,12 @@ mod tests { // As a small small L2 gas price we'll use the value of 1 wei. const SMALL_L1_GAS_PRICE: u64 = 1; - // base token ration to use in the tests - const BASE_TOKEN_RATIO: f64 = 1.5; + // Base token ratio to use in the tests. + const BASE_TOKEN_RATIO: BigDecimal = BigDecimal::from(0.5); - fn apply_base_token_ratio(base_token_ratio: Option, value: u64) -> u64 { - match base_token_ratio { - Some(x) => (x * value as f64) as u64, - None => value, - } - } - - #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] + #[test_casing(3, ["ETH", "ZK"], [None, Some(BASE_TOKEN_RATIO)])] #[test] - fn test_compute_batch_fee_model_input_v2_giant_numbers(base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_giant_numbers(base_token: &'static str, base_token_ratio: Option) { let config = FeeModelConfigV2 { minimal_l2_gas_price: GIANT_L1_GAS_PRICE, // We generally don't expect those values to be larger than 1. Still, in theory the operator @@ -315,6 +335,7 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, + base_token, base_token_ratio, }; @@ -323,21 +344,21 @@ mod tests { assert_eq!( input.l1_gas_price, - apply_base_token_ratio(base_token_ratio, GIANT_L1_GAS_PRICE * 3) + convert_to_base_token_price(base_token_ratio, GIANT_L1_GAS_PRICE * 3) ); assert_eq!( input.fair_l2_gas_price, - apply_base_token_ratio(base_token_ratio, 130_000_000_000_000) + convert_to_base_token_price(base_token_ratio, 130_000_000_000_000) ); assert_eq!( input.fair_pubdata_price, - apply_base_token_ratio(base_token_ratio, 15_300_000_000_000_000) + convert_to_base_token_price(base_token_ratio, 15_300_000_000_000_000) ); } #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_small_numbers(base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_small_numbers(base_token: &'static str, base_token_ratio: Option) { // Here we assume that the operator wants to make the lives of users as cheap as possible. let config = FeeModelConfigV2 { minimal_l2_gas_price: SMALL_L1_GAS_PRICE, @@ -352,6 +373,7 @@ mod tests { config, l1_gas_price: SMALL_L1_GAS_PRICE, l1_pubdata_price: SMALL_L1_GAS_PRICE, + base_token, base_token_ratio, }; @@ -359,21 +381,21 @@ mod tests { assert_eq!( input.l1_gas_price, - apply_base_token_ratio(base_token_ratio, SMALL_L1_GAS_PRICE) + convert_to_base_token_price(base_token_ratio, SMALL_L1_GAS_PRICE) ); assert_eq!( input.fair_l2_gas_price, - apply_base_token_ratio(base_token_ratio, SMALL_L1_GAS_PRICE) + convert_to_base_token_price(base_token_ratio, SMALL_L1_GAS_PRICE) ); assert_eq!( input.fair_pubdata_price, - apply_base_token_ratio(base_token_ratio, SMALL_L1_GAS_PRICE) + convert_to_base_token_price(base_token_ratio, SMALL_L1_GAS_PRICE) ); } #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_only_pubdata_overhead(base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_only_pubdata_overhead(base_token: &'static str, base_token_ratio: Option) { // Here we use sensible config, but when only pubdata is used to close the batch let config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -388,29 +410,30 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, + base_token, base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); assert_eq!( input.l1_gas_price, - apply_base_token_ratio(base_token_ratio, GIANT_L1_GAS_PRICE) + convert_to_base_token_price(base_token_ratio, GIANT_L1_GAS_PRICE) ); // The fair L2 gas price is identical to the minimal one. assert_eq!( input.fair_l2_gas_price, - apply_base_token_ratio(base_token_ratio, 100_000_000_000) + convert_to_base_token_price(base_token_ratio, 100_000_000_000) ); // The fair pubdata price is the minimal one plus the overhead. assert_eq!( input.fair_pubdata_price, - apply_base_token_ratio(base_token_ratio, 800_000_000_000_000) + convert_to_base_token_price(base_token_ratio, 800_000_000_000_000) ); } #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_only_compute_overhead(base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_only_compute_overhead(base_token: &'static str, base_token_ratio: Option) { // Here we use sensible config, but when only compute is used to close the batch let config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -425,29 +448,30 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, + base_token, base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); assert_eq!( input.l1_gas_price, - apply_base_token_ratio(base_token_ratio, GIANT_L1_GAS_PRICE) + convert_to_base_token_price(base_token_ratio, GIANT_L1_GAS_PRICE) ); // The fair L2 gas price is identical to the minimal one, plus the overhead assert_eq!( input.fair_l2_gas_price, - apply_base_token_ratio(base_token_ratio, 240_000_000_000) + convert_to_base_token_price(base_token_ratio, 240_000_000_000) ); // The fair pubdata price is equal to the original one. assert_eq!( input.fair_pubdata_price, - apply_base_token_ratio(base_token_ratio, GIANT_L1_GAS_PRICE) + convert_to_base_token_price(base_token_ratio, GIANT_L1_GAS_PRICE) ); } #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_param_tweaking(base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_param_tweaking(base_token: &'static str, base_token_ratio: Option) { // In this test we generally checking that each param behaves as expected let base_config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -462,6 +486,7 @@ mod tests { config: base_config, l1_gas_price: 1_000_000_000, l1_pubdata_price: 1_000_000_000, + base_token, base_token_ratio, }; @@ -569,7 +594,8 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token_ratio: Some(f64::MAX), + base_token: "ZK", + base_token_ratio: Some(BigDecimal::MAX), }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -603,6 +629,7 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, + base_token: "ZK", base_token_ratio: Some(f64::NAN), }; diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index e2b37e64a6c8..004e686d16e2 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -54,7 +54,7 @@ impl Task for BaseTokenAdjusterTask { async fn run(self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { let adjuster = - zksync_base_token_adjuster::NodeBaseTokenAdjuster::new(self.main_pool, self.config); + zksync_base_token_adjuster::MainNodeBaseTokenAdjuster::new(self.main_pool, self.config); adjuster.run(stop_receiver.0).await } diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index 9efcddf11f8c..06e1e5996437 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use anyhow::Context; -use zksync_base_token_adjuster::NodeBaseTokenAdjuster; +use zksync_base_token_adjuster::MainNodeBaseTokenAdjuster; use zksync_config::{ configs::{chain::StateKeeperConfig, eth_sender::PubdataSendingMode}, BaseTokenAdjusterConfig, GasAdjusterConfig, GenesisConfig, @@ -70,7 +70,7 @@ impl WiringLayer for SequencerL1GasLayer { let replica_pool = pool_resource.get().await?; let base_token_adjuster = - NodeBaseTokenAdjuster::new(replica_pool.clone(), self.base_token_adjuster_config); + MainNodeBaseTokenAdjuster::new(replica_pool.clone(), self.base_token_adjuster_config); let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( gas_adjuster.clone(), diff --git a/core/node/state_keeper/src/io/tests/tester.rs b/core/node/state_keeper/src/io/tests/tester.rs index f47152e85694..7aa22423eff2 100644 --- a/core/node/state_keeper/src/io/tests/tester.rs +++ b/core/node/state_keeper/src/io/tests/tester.rs @@ -236,7 +236,7 @@ impl Tester { #[derive(Debug)] struct MockBaseTokenAdjuster {} impl BaseTokenAdjuster for MockBaseTokenAdjuster { - fn get_last_ratio(&self) -> Option { + fn get_last_ratio_and_check_usability(&self) -> Option { None } } diff --git a/prover/prover_dal/sqlx-data.json b/prover/prover_dal/sqlx-data.json new file mode 100644 index 000000000000..95c8c858baaf --- /dev/null +++ b/prover/prover_dal/sqlx-data.json @@ -0,0 +1,3 @@ +{ + "db": "PostgreSQL" +} \ No newline at end of file From bbd9f7c32d4174e0a66c6640d93f02ba142efb2e Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Fri, 21 Jun 2024 02:01:48 +0300 Subject: [PATCH 16/48] working sqlx + converting params themselves --- Cargo.lock | 1 + ...270e25815ca2ab720a59567da3b3b5bcedd63.json | 15 -- ...7d28571cad5438cbe24fb411494dc082c5acb.json | 16 -- core/lib/types/src/fee_model.rs | 13 +- core/node/base_token_adjuster/Cargo.toml | 1 + .../src/base_token_adjuster.rs | 72 +++--- core/node/fee_model/src/lib.rs | 228 ++---------------- .../layers/base_token_adjuster.rs | 2 +- 8 files changed, 70 insertions(+), 278 deletions(-) delete mode 100644 core/lib/dal/.sqlx/query-6b7f66422078e9880b002da3175270e25815ca2ab720a59567da3b3b5bcedd63.json delete mode 100644 core/lib/dal/.sqlx/query-8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb.json diff --git a/Cargo.lock b/Cargo.lock index fd21953c3960..e89523289a08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7983,6 +7983,7 @@ name = "zksync_base_token_adjuster" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "chrono", "rand 0.8.5", "tokio", diff --git a/core/lib/dal/.sqlx/query-6b7f66422078e9880b002da3175270e25815ca2ab720a59567da3b3b5bcedd63.json b/core/lib/dal/.sqlx/query-6b7f66422078e9880b002da3175270e25815ca2ab720a59567da3b3b5bcedd63.json deleted file mode 100644 index b7b84c323b2e..000000000000 --- a/core/lib/dal/.sqlx/query-6b7f66422078e9880b002da3175270e25815ca2ab720a59567da3b3b5bcedd63.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE tee_proof_generation_details\n SET\n status = $1,\n updated_at = NOW()\n WHERE\n l1_batch_number = $2\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "6b7f66422078e9880b002da3175270e25815ca2ab720a59567da3b3b5bcedd63" -} diff --git a/core/lib/dal/.sqlx/query-8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb.json b/core/lib/dal/.sqlx/query-8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb.json deleted file mode 100644 index a2d549b85dd4..000000000000 --- a/core/lib/dal/.sqlx/query-8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO\n base_token_prices (numerator, denominator, ratio_timestamp)\n VALUES\n ($1, $2, $3)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Numeric", - "Numeric", - "Timestamp" - ] - }, - "nullable": [] - }, - "hash": "8189b83c61418079419764d9fdf7d28571cad5438cbe24fb411494dc082c5acb" -} diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index b7f514bcadd8..9c2cc4d2aaf8 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -1,4 +1,3 @@ -use bigdecimal::BigDecimal; use serde::{Deserialize, Serialize}; use zksync_config::configs::chain::{FeeModelVersion, StateKeeperConfig}; use zksync_system_constants::L1_GAS_PER_PUBDATA_BYTE; @@ -158,10 +157,10 @@ pub struct PubdataIndependentBatchFeeModelInput { } /// The enum which represents the version of the fee model. It is used to determine which fee model should be used for the batch. -/// - `V1`, the first model that was used in zkSync Era. In this fee model, the pubdata price must be pegged to the L1 gas price. +/// - `V1`, the first model that was used in ZKsync Era. In this fee model, the pubdata price must be pegged to the L1 gas price. /// Also, the fair L2 gas price is expected to only include the proving/computation price for the operator and not the costs that come from /// processing the batch on L1. -/// - `V2`, the second model that was used in zkSync Era. There the pubdata price might be independent from the L1 gas price. Also, +/// - `V2`, the second model that was used in ZKsync Era. There the pubdata price might be independent from the L1 gas price. Also, /// The fair L2 gas price is expected to both the proving/computation price for the operator and the costs that come from /// processing the batch on L1. #[derive(Debug, Clone, Copy, Serialize, Deserialize)] @@ -199,8 +198,6 @@ pub struct FeeModelConfigV2 { pub max_gas_per_batch: u64, /// The maximum amount of pubdata that can be used by the batch. Note that if the calldata is used as pubdata, this variable should not exceed 128kb. pub max_pubdata_per_batch: u64, - // /// A constant that indicates what base token is used. - // pub base_token: String, } impl Default for FeeModelConfig { @@ -237,16 +234,14 @@ pub struct FeeParamsV1 { pub l1_gas_price: u64, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct FeeParamsV2 { pub config: FeeModelConfigV2, pub l1_gas_price: u64, pub l1_pubdata_price: u64, - pub base_token: &'static str, - pub base_token_ratio: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum FeeParams { V1(FeeParamsV1), V2(FeeParamsV2), diff --git a/core/node/base_token_adjuster/Cargo.toml b/core/node/base_token_adjuster/Cargo.toml index 2dd60cdee776..2b91a0dac2dc 100644 --- a/core/node/base_token_adjuster/Cargo.toml +++ b/core/node/base_token_adjuster/Cargo.toml @@ -22,3 +22,4 @@ anyhow.workspace = true tracing.workspace = true chrono.workspace = true rand.workspace = true +async-trait.workspace = true diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 2054cba72df4..193221f183ec 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -1,22 +1,23 @@ +use anyhow::Context as _; +use async_trait::async_trait; +use chrono::Utc; +use std::ops::Div; use std::{ fmt::Debug, time::{Duration, Instant}, }; -use std::ops::Div; -use anyhow::Context as _; - -use chrono::{DateTime, Utc}; use tokio::sync::watch; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; use zksync_types::base_token_price::BaseTokenAPIPrice; +#[async_trait] pub trait BaseTokenAdjuster: Debug + Send + Sync { /// Returns the last ratio cached by the adjuster and ensure it's still usable. - fn get_last_ratio_and_check_usability(&self) -> anyhow::Result; + async fn get_last_ratio_and_check_usability<'a>(&'a self) -> BigDecimal; /// Return configured symbol of the base token. - fn get_base_token(&self) -> String; + fn get_base_token(&self) -> &str; } #[derive(Debug)] @@ -28,7 +29,7 @@ pub struct MainNodeBaseTokenAdjuster { impl MainNodeBaseTokenAdjuster { pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { - Self { pool, config} + Self { pool, config } } /// Main loop for the base token adjuster. @@ -44,16 +45,14 @@ impl MainNodeBaseTokenAdjuster { let start_time = Instant::now(); match self.fetch_new_ratio().await { - Ok(new_ratio) => { - match self.persist_ratio(&new_ratio, &pool).await { - Ok(id) => { - if let Err(err) = self.maybe_update_l1(&new_ratio, id).await { - tracing::error!("Error updating L1 ratio: {:?}", err); - } + Ok(new_ratio) => match self.persist_ratio(&new_ratio, &pool).await { + Ok(id) => { + if let Err(err) = self.maybe_update_l1(&new_ratio, id).await { + tracing::error!("Error updating L1 ratio: {:?}", err); } - Err(err) => tracing::error!("Error persisting ratio: {:?}", err), } - } + Err(err) => tracing::error!("Error persisting ratio: {:?}", err), + }, Err(err) => tracing::error!("Error fetching new ratio: {:?}", err), } @@ -80,12 +79,14 @@ impl MainNodeBaseTokenAdjuster { &self, api_price: &BaseTokenAPIPrice, pool: &ConnectionPool, - ) -> anyhow::Result<(usize)> { - let mut conn = pool.connection_tagged("base_token_adjuster") + ) -> anyhow::Result { + let mut conn = pool + .connection_tagged("base_token_adjuster") .await .context("Failed to obtain connection to the database")?; - let id = conn.base_token_dal() + let id = conn + .base_token_dal() .insert_token_price( &api_price.numerator, &api_price.denominator, @@ -98,7 +99,11 @@ impl MainNodeBaseTokenAdjuster { } // TODO (PE-128): Complete L1 update flow. - async fn maybe_update_l1(&self, _new_ratio: &BaseTokenAPIPrice, _id: usize) -> anyhow::Result<()> { + async fn maybe_update_l1( + &self, + _new_ratio: &BaseTokenAPIPrice, + _id: usize, + ) -> anyhow::Result<()> { Ok(()) } @@ -115,28 +120,35 @@ impl MainNodeBaseTokenAdjuster { } } +#[async_trait] impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { // TODO (PE-129): Implement latest ratio usability logic. - async fn get_last_ratio_and_check_usability(&self) -> anyhow::Result { - let mut conn = self.pool.connection_tagged("base_token_adjuster") + async fn get_last_ratio_and_check_usability<'a>(&'a self) -> BigDecimal { + let mut conn = self + .pool + .connection_tagged("base_token_adjuster") .await - .context("Failed to obtain connection to the database")?; + .expect("Failed to obtain connection to the database"); - let last_storage_ratio = conn.base_token_dal() + let last_storage_ratio = conn + .base_token_dal() .get_latest_price() - .await?; + .await + .expect("Failed to get latest base token price"); drop(conn); - let last_ratio = last_storage_ratio.numerator.div(&last_storage_ratio.denominator); + let last_ratio = last_storage_ratio + .numerator + .div(&last_storage_ratio.denominator); - Ok(last_ratio) + last_ratio } /// Return configured symbol of the base token. If not configured, return "ETH". - fn get_base_token(&self) -> String { - match self.config.base_token.clone() { - Some(token) => token, - None => "ETH".to_string(), + fn get_base_token(&self) -> &str { + match &self.config.base_token { + Some(base_token) => base_token.as_str(), + None => "ETH", } } } diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index ca84c871b3ec..8519a0d4a0fe 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -1,7 +1,6 @@ use std::{fmt, sync::Arc}; use anyhow::Context as _; -use bigdecimal::Zero; use zksync_base_token_adjuster::BaseTokenAdjuster; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ @@ -76,8 +75,6 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { config, l1_gas_price: self.provider.estimate_effective_gas_price(), l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), - base_token: self.base_token_adjuster.get_base_token(), - base_token_ratio: self.base_token_adjuster.get_last_ratio_and_check_usability(), }), } } @@ -173,8 +170,6 @@ fn compute_batch_fee_model_input_v2( config, l1_gas_price, l1_pubdata_price, - base_token, - base_token_ratio, } = params; let FeeModelConfigV2 { @@ -224,61 +219,13 @@ fn compute_batch_fee_model_input_v2( l1_pubdata_price + pubdata_overhead_wei }; - // If the base token is ETH, simply return prices calculated in WEI. Otherwise, convert them to the base token. - match base_token { - "ETH" => PubdataIndependentBatchFeeModelInput { - l1_gas_price, - fair_l2_gas_price, - fair_pubdata_price, - }, - _ => { - let convert_to_base_token_price = |price_in_wei: u64| -> u64 { - let converted_price = base_token_ratio * price_in_wei as f64; - if converted_price.is_finite() { - converted_price as u64 - } else { - u64::MAX - } - }; - - PubdataIndependentBatchFeeModelInput { - l1_gas_price: convert_to_base_token_price(l1_gas_price), - fair_l2_gas_price: convert_to_base_token_price(fair_l2_gas_price), - fair_pubdata_price: convert_to_base_token_price(fair_pubdata_price), - } - } - } -} - -fn convert_to_base_token_price(base_token_ratio: BigDecimal, price_in_wei: u64) -> u64 { - // Convert to BigDecimal so the conversion is more precise. - let price_in_wei_bd = BigDecimal::from(price_in_wei); - - let converted_price_bd = base_token_ratio * price_in_wei_bd; - - // If the conversion is not possible, return the maximum value. - match converted_price_bd.to_u64() { - Some(converted_price) => converted_price, - None => { - // If we can't convert it's either because the value is too large or something unexpected. - // In either case we clamp down to u64::MAX. - if converted_price_bd > BigDecimal::from(u64::MAX) { - tracing::warn!( - "Conversion to base token price failed: converted price is too large: {}", - converted_price_bd - ); - } else { - tracing::error!( - "Conversion to base token price failed: converted price is not a valid u64: {}", - converted_price_bd - ); - } - u64::MAX - } + PubdataIndependentBatchFeeModelInput { + l1_gas_price, + fair_l2_gas_price, + fair_pubdata_price, } } - /// Mock [`BatchFeeModelInputProvider`] implementation that returns a constant value. /// Intended to be used in tests only. #[derive(Debug)] @@ -298,9 +245,6 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { #[cfg(test)] mod tests { - use bigdecimal::BigDecimal; - use test_casing::test_casing; - use super::*; // To test that overflow never happens, we'll use giant L1 gas price, i.e. @@ -311,12 +255,8 @@ mod tests { // As a small small L2 gas price we'll use the value of 1 wei. const SMALL_L1_GAS_PRICE: u64 = 1; - // Base token ratio to use in the tests. - const BASE_TOKEN_RATIO: BigDecimal = BigDecimal::from(0.5); - - #[test_casing(3, ["ETH", "ZK"], [None, Some(BASE_TOKEN_RATIO)])] #[test] - fn test_compute_batch_fee_model_input_v2_giant_numbers(base_token: &'static str, base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_giant_numbers() { let config = FeeModelConfigV2 { minimal_l2_gas_price: GIANT_L1_GAS_PRICE, // We generally don't expect those values to be larger than 1. Still, in theory the operator @@ -335,30 +275,18 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token, - base_token_ratio, }; // We'll use scale factor of 3.0 let input = compute_batch_fee_model_input_v2(params, 3.0, 3.0); - assert_eq!( - input.l1_gas_price, - convert_to_base_token_price(base_token_ratio, GIANT_L1_GAS_PRICE * 3) - ); - assert_eq!( - input.fair_l2_gas_price, - convert_to_base_token_price(base_token_ratio, 130_000_000_000_000) - ); - assert_eq!( - input.fair_pubdata_price, - convert_to_base_token_price(base_token_ratio, 15_300_000_000_000_000) - ); + assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE * 3); + assert_eq!(input.fair_l2_gas_price, 130_000_000_000_000); + assert_eq!(input.fair_pubdata_price, 15_300_000_000_000_000); } - #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_small_numbers(base_token: &'static str, base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_small_numbers() { // Here we assume that the operator wants to make the lives of users as cheap as possible. let config = FeeModelConfigV2 { minimal_l2_gas_price: SMALL_L1_GAS_PRICE, @@ -373,29 +301,17 @@ mod tests { config, l1_gas_price: SMALL_L1_GAS_PRICE, l1_pubdata_price: SMALL_L1_GAS_PRICE, - base_token, - base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); - assert_eq!( - input.l1_gas_price, - convert_to_base_token_price(base_token_ratio, SMALL_L1_GAS_PRICE) - ); - assert_eq!( - input.fair_l2_gas_price, - convert_to_base_token_price(base_token_ratio, SMALL_L1_GAS_PRICE) - ); - assert_eq!( - input.fair_pubdata_price, - convert_to_base_token_price(base_token_ratio, SMALL_L1_GAS_PRICE) - ); + assert_eq!(input.l1_gas_price, SMALL_L1_GAS_PRICE); + assert_eq!(input.fair_l2_gas_price, SMALL_L1_GAS_PRICE); + assert_eq!(input.fair_pubdata_price, SMALL_L1_GAS_PRICE); } - #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_only_pubdata_overhead(base_token: &'static str, base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_only_pubdata_overhead() { // Here we use sensible config, but when only pubdata is used to close the batch let config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -410,30 +326,18 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token, - base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); - assert_eq!( - input.l1_gas_price, - convert_to_base_token_price(base_token_ratio, GIANT_L1_GAS_PRICE) - ); + assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE); // The fair L2 gas price is identical to the minimal one. - assert_eq!( - input.fair_l2_gas_price, - convert_to_base_token_price(base_token_ratio, 100_000_000_000) - ); + assert_eq!(input.fair_l2_gas_price, 100_000_000_000); // The fair pubdata price is the minimal one plus the overhead. - assert_eq!( - input.fair_pubdata_price, - convert_to_base_token_price(base_token_ratio, 800_000_000_000_000) - ); + assert_eq!(input.fair_pubdata_price, 800_000_000_000_000); } - #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_only_compute_overhead(base_token: &'static str, base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_only_compute_overhead() { // Here we use sensible config, but when only compute is used to close the batch let config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -448,30 +352,18 @@ mod tests { config, l1_gas_price: GIANT_L1_GAS_PRICE, l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token, - base_token_ratio, }; let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); - assert_eq!( - input.l1_gas_price, - convert_to_base_token_price(base_token_ratio, GIANT_L1_GAS_PRICE) - ); + assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE); // The fair L2 gas price is identical to the minimal one, plus the overhead - assert_eq!( - input.fair_l2_gas_price, - convert_to_base_token_price(base_token_ratio, 240_000_000_000) - ); + assert_eq!(input.fair_l2_gas_price, 240_000_000_000); // The fair pubdata price is equal to the original one. - assert_eq!( - input.fair_pubdata_price, - convert_to_base_token_price(base_token_ratio, GIANT_L1_GAS_PRICE) - ); + assert_eq!(input.fair_pubdata_price, GIANT_L1_GAS_PRICE); } - #[test_casing(2, [Some(BASE_TOKEN_RATIO), None])] #[test] - fn test_compute_batch_fee_model_input_v2_param_tweaking(base_token: &'static str, base_token_ratio: Option) { + fn test_compute_batch_fee_model_input_v2_param_tweaking() { // In this test we generally checking that each param behaves as expected let base_config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -486,8 +378,6 @@ mod tests { config: base_config, l1_gas_price: 1_000_000_000, l1_pubdata_price: 1_000_000_000, - base_token, - base_token_ratio, }; let base_input = compute_batch_fee_model_input_v2(base_params, 1.0, 1.0); @@ -578,80 +468,4 @@ mod tests { "Max pubdata increase lowers pubdata price" ); } - - #[test] - fn test_compute_batch_fee_model_input_v2_base_token_overflow() { - let config = FeeModelConfigV2 { - minimal_l2_gas_price: 100_000_000_000, - compute_overhead_part: 1.0, - pubdata_overhead_part: 0.0, - batch_overhead_l1_gas: 700_000, - max_gas_per_batch: 500_000_000, - max_pubdata_per_batch: 100_000, - }; - - let params = FeeParamsV2 { - config, - l1_gas_price: GIANT_L1_GAS_PRICE, - l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token: "ZK", - base_token_ratio: Some(BigDecimal::MAX), - }; - - let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); - // in case of overflow, gas fees should be clamped at u64::MAX - assert_eq!(input.l1_gas_price, u64::MAX); - assert_eq!(input.fair_l2_gas_price, u64::MAX); - assert_eq!(input.fair_pubdata_price, u64::MAX); - } - - #[test] - fn test_compute_batch_fee_model_input_v2_base_token_disregard_non_positive_quote() { - let config = FeeModelConfigV2 { - minimal_l2_gas_price: 100_000_000_000, - compute_overhead_part: 1.0, - pubdata_overhead_part: 0.0, - batch_overhead_l1_gas: 700_000, - max_gas_per_batch: 500_000_000, - max_pubdata_per_batch: 100_000, - }; - - // these assertions ensure that invalid base token ratios such as negative numbers, nan, - // infinity, zero is disregarded - let test_fn = |params: FeeParamsV2| { - let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); - assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE); - assert_eq!(input.fair_l2_gas_price, 240_000_000_000); - assert_eq!(input.fair_pubdata_price, GIANT_L1_GAS_PRICE); - }; - - let params = FeeParamsV2 { - config, - l1_gas_price: GIANT_L1_GAS_PRICE, - l1_pubdata_price: GIANT_L1_GAS_PRICE, - base_token: "ZK", - base_token_ratio: Some(f64::NAN), - }; - - test_fn(FeeParamsV2 { - base_token_ratio: Some(f64::NAN), - ..params - }); - test_fn(FeeParamsV2 { - base_token_ratio: Some(f64::NEG_INFINITY), - ..params - }); - test_fn(FeeParamsV2 { - base_token_ratio: Some(f64::INFINITY), - ..params - }); - test_fn(FeeParamsV2 { - base_token_ratio: Some(0.0), - ..params - }); - test_fn(FeeParamsV2 { - base_token_ratio: Some(1.0), - ..params - }); - } } diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index 004e686d16e2..bf1fc18a7acd 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -53,7 +53,7 @@ impl Task for BaseTokenAdjusterTask { } async fn run(self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { - let adjuster = + let mut adjuster = zksync_base_token_adjuster::MainNodeBaseTokenAdjuster::new(self.main_pool, self.config); adjuster.run(stop_receiver.0).await From 313444fb0cd57f2ddb96cd50fc46fdd9cd4bc3cf Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Fri, 21 Jun 2024 02:02:24 +0300 Subject: [PATCH 17/48] real working sqlx --- ...b49c9306c7af534dcccbf1976553cfa84fe6e.json | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 core/lib/dal/.sqlx/query-4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e.json diff --git a/core/lib/dal/.sqlx/query-4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e.json b/core/lib/dal/.sqlx/query-4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e.json new file mode 100644 index 000000000000..37e823643043 --- /dev/null +++ b/core/lib/dal/.sqlx/query-4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO\n base_token_prices (numerator, denominator, ratio_timestamp)\n VALUES\n ($1, $2, $3)\n RETURNING\n id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Numeric", + "Numeric", + "Timestamp" + ] + }, + "nullable": [ + false + ] + }, + "hash": "4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e" +} From 9160131be54f65c27f06287658a387c6e5e5376a Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Fri, 21 Jun 2024 14:47:34 +0300 Subject: [PATCH 18/48] flow through updating params --- .../web3/backend_jsonrpsee/namespaces/zks.rs | 4 +- .../api_server/src/web3/namespaces/zks.rs | 6 +- .../src/base_token_adjuster.rs | 9 +- .../src/l1_gas_price/main_node_fetcher.rs | 5 +- core/node/fee_model/src/lib.rs | 96 ++++++++++++++++--- 5 files changed, 96 insertions(+), 24 deletions(-) diff --git a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs index 5a4f7eb1f5fa..e94a810ff811 100644 --- a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs +++ b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs @@ -151,7 +151,9 @@ impl ZksNamespaceServer for ZksNamespace { } async fn get_fee_params(&self) -> RpcResult { - Ok(self.get_fee_params_impl()) + self.get_fee_params_impl() + .await + .map_err(|err| self.current_method().map_err(err)) } async fn get_batch_fee_input(&self) -> RpcResult { diff --git a/core/node/api_server/src/web3/namespaces/zks.rs b/core/node/api_server/src/web3/namespaces/zks.rs index 6b872bcf637e..5719055d2bea 100644 --- a/core/node/api_server/src/web3/namespaces/zks.rs +++ b/core/node/api_server/src/web3/namespaces/zks.rs @@ -450,12 +450,14 @@ impl ZksNamespace { } #[tracing::instrument(skip(self))] - pub fn get_fee_params_impl(&self) -> FeeParams { - self.state + pub async fn get_fee_params_impl(&self) -> Result { + Ok(self + .state .tx_sender .0 .batch_fee_input_provider .get_fee_model_params() + .await?) } pub async fn get_protocol_version_impl( diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 193221f183ec..73ca9c33742d 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -1,11 +1,12 @@ -use anyhow::Context as _; -use async_trait::async_trait; -use chrono::Utc; -use std::ops::Div; use std::{ fmt::Debug, + ops::Div, time::{Duration, Instant}, }; + +use anyhow::Context as _; +use async_trait::async_trait; +use chrono::Utc; use tokio::sync::watch; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; diff --git a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs index 259a5e3e3fed..dd659e984d41 100644 --- a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs +++ b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs @@ -71,8 +71,9 @@ impl MainNodeFeeParamsFetcher { } } +#[async_trait::async_trait] impl BatchFeeModelInputProvider for MainNodeFeeParamsFetcher { - fn get_fee_model_params(&self) -> FeeParams { - *self.main_node_fee_params.read().unwrap() + async fn get_fee_model_params(&self) -> anyhow::Result { + Ok(*self.main_node_fee_params.read().unwrap()) } } diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 8519a0d4a0fe..66063097cfac 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -1,6 +1,7 @@ use std::{fmt, sync::Arc}; use anyhow::Context as _; +use bigdecimal::{BigDecimal, ToPrimitive}; use zksync_base_token_adjuster::BaseTokenAdjuster; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ @@ -26,7 +27,7 @@ pub trait BatchFeeModelInputProvider: fmt::Debug + 'static + Send + Sync { l1_gas_price_scale_factor: f64, l1_pubdata_price_scale_factor: f64, ) -> anyhow::Result { - let params = self.get_fee_model_params(); + let params = self.get_fee_model_params().await?; Ok(match params { FeeParams::V1(params) => BatchFeeInput::L1Pegged(compute_batch_fee_model_input_v1( @@ -43,8 +44,8 @@ pub trait BatchFeeModelInputProvider: fmt::Debug + 'static + Send + Sync { }) } - /// Returns the fee model parameters. - fn get_fee_model_params(&self) -> FeeParams; + /// Returns the fee model parameters using the denomination of the base token used (WEI for ETH). + async fn get_fee_model_params(&self) -> anyhow::Result; } impl dyn BatchFeeModelInputProvider { @@ -64,18 +65,36 @@ pub struct MainNodeFeeInputProvider { config: FeeModelConfig, } +#[async_trait::async_trait] impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { - fn get_fee_model_params(&self) -> FeeParams { + async fn get_fee_model_params(&self) -> anyhow::Result { match self.config { - FeeModelConfig::V1(config) => FeeParams::V1(FeeParamsV1 { + FeeModelConfig::V1(config) => Ok(FeeParams::V1(FeeParamsV1 { config, l1_gas_price: self.provider.estimate_effective_gas_price(), - }), - FeeModelConfig::V2(config) => FeeParams::V2(FeeParamsV2 { - config, - l1_gas_price: self.provider.estimate_effective_gas_price(), - l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), - }), + })), + FeeModelConfig::V2(config) => { + let params = FeeParamsV2 { + config, + l1_gas_price: self.provider.estimate_effective_gas_price(), + l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), + }; + + let base_token = self.base_token_adjuster.get_base_token(); + match base_token { + "ETH" => Ok(FeeParams::V2(params)), + _ => { + let base_token_conversion_ratio = self + .base_token_adjuster + .get_last_ratio_and_check_usability() + .await; + Ok(FeeParams::V2(convert_to_base_token( + params, + base_token_conversion_ratio, + ))) + } + } + } } } } @@ -94,6 +113,52 @@ impl MainNodeFeeInputProvider { } } +fn convert_to_base_token( + params: FeeParamsV2, + base_token_conversion_ratio: BigDecimal, +) -> FeeParamsV2 { + let FeeParamsV2 { + config, + l1_gas_price, + l1_pubdata_price, + } = params; + + let convert_price = |price_in_wei: u64, eth_to_base_token_ratio: &BigDecimal| -> u64 { + let converted_price_bd = BigDecimal::from(price_in_wei) * eth_to_base_token_ratio; + match converted_price_bd.to_u64() { + Some(converted_price) => converted_price, + None => { + if converted_price_bd > BigDecimal::from(u64::MAX) { + tracing::warn!( + "Conversion to base token price failed: converted price is too large: {}", + converted_price_bd + ); + } else { + tracing::error!( + "Conversion to base token price failed: converted price is not a valid u64: {}", + converted_price_bd + ); + } + u64::MAX + } + } + }; + + let l1_gas_price_converted = convert_price(l1_gas_price, &base_token_conversion_ratio); + let l1_pubdata_price_converted = convert_price(l1_pubdata_price, &base_token_conversion_ratio); + let minimal_l2_gas_price_converted = + convert_price(config.minimal_l2_gas_price, &base_token_conversion_ratio); + + FeeParamsV2 { + config: FeeModelConfigV2 { + minimal_l2_gas_price: minimal_l2_gas_price_converted, + ..config + }, + l1_gas_price: l1_gas_price_converted, + l1_pubdata_price: l1_pubdata_price_converted, + } +} + /// The fee model provider to be used in the API. It returns the maximum batch fee input between the projected main node one and /// the one from the last sealed L2 block. #[derive(Debug)] @@ -140,8 +205,8 @@ impl BatchFeeModelInputProvider for ApiFeeInputProvider { } /// Returns the fee model parameters. - fn get_fee_model_params(&self) -> FeeParams { - self.inner.get_fee_model_params() + async fn get_fee_model_params(&self) -> anyhow::Result { + self.inner.get_fee_model_params().await } } @@ -237,9 +302,10 @@ impl Default for MockBatchFeeParamsProvider { } } +#[async_trait::async_trait] impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { - fn get_fee_model_params(&self) -> FeeParams { - self.0 + async fn get_fee_model_params<'a>(&'a self) -> anyhow::Result { + Ok(self.0) } } From cce90e1a01d46aa12bdb86ea939f7bda8a5771ab Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Fri, 21 Jun 2024 21:47:39 +0300 Subject: [PATCH 19/48] one happy test --- .../src/base_token_adjuster.rs | 31 ++++- core/node/base_token_adjuster/src/lib.rs | 4 +- .../src/l1_gas_price/gas_adjuster/mod.rs | 128 +++++++++++------- core/node/fee_model/src/l1_gas_price/mod.rs | 3 +- core/node/fee_model/src/lib.rs | 77 ++++++++++- 5 files changed, 188 insertions(+), 55 deletions(-) diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 73ca9c33742d..b1963cdd5069 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -65,8 +65,8 @@ impl MainNodeBaseTokenAdjuster { // TODO (PE-135): Use real API client to fetch new ratio through self.PriceAPIClient & mock for tests. // For now, these hard coded values are also hard coded in the integration tests. async fn fetch_new_ratio(&self) -> anyhow::Result { - let new_numerator = BigDecimal::from(1); - let new_denominator = BigDecimal::from(100); + let new_numerator = BigDecimal::from(100000); + let new_denominator = BigDecimal::from(1); let ratio_timestamp = Utc::now(); Ok(BaseTokenAPIPrice { @@ -153,3 +153,30 @@ impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { } } } + +#[derive(Debug)] +#[allow(dead_code)] +pub struct MockBaseTokenAdjuster { + last_ratio: BigDecimal, + base_token: String, +} + +impl MockBaseTokenAdjuster { + pub fn new(last_ratio: BigDecimal, base_token: String) -> Self { + Self { + last_ratio, + base_token, + } + } +} + +#[async_trait] +impl BaseTokenAdjuster for MockBaseTokenAdjuster { + async fn get_last_ratio_and_check_usability(&self) -> BigDecimal { + self.last_ratio.clone() + } + + fn get_base_token(&self) -> &str { + &self.base_token + } +} diff --git a/core/node/base_token_adjuster/src/lib.rs b/core/node/base_token_adjuster/src/lib.rs index 1b025f81c26e..45c0615dfc01 100644 --- a/core/node/base_token_adjuster/src/lib.rs +++ b/core/node/base_token_adjuster/src/lib.rs @@ -1,5 +1,7 @@ extern crate core; -pub use self::base_token_adjuster::{BaseTokenAdjuster, MainNodeBaseTokenAdjuster}; +pub use self::base_token_adjuster::{ + BaseTokenAdjuster, MainNodeBaseTokenAdjuster, MockBaseTokenAdjuster, +}; mod base_token_adjuster; diff --git a/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs b/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs index 9e553ba47bf2..35f8f090a128 100644 --- a/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs +++ b/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs @@ -2,6 +2,7 @@ use std::{ collections::VecDeque, + fmt::Debug, ops::RangeInclusive, sync::{Arc, RwLock}, }; @@ -19,6 +20,11 @@ mod metrics; #[cfg(test)] mod tests; +pub trait L1GasAdjuster: Debug + Send + Sync { + fn estimate_effective_gas_price(&self) -> u64; + fn estimate_effective_pubdata_price(&self) -> u64; +} + /// This component keeps track of the median `base_fee` from the last `max_base_fee_samples` blocks /// and of the median `blob_base_fee` from the last `max_blob_base_fee_sample` blocks. /// It is used to adjust the base_fee of transactions sent to L1. @@ -35,6 +41,54 @@ pub struct GasAdjuster { commitment_mode: L1BatchCommitmentMode, } +impl L1GasAdjuster for GasAdjuster { + fn estimate_effective_gas_price(&self) -> u64 { + if let Some(price) = self.config.internal_enforced_l1_gas_price { + return price; + } + + let effective_gas_price = self.get_base_fee(0) + self.get_priority_fee(); + + let calculated_price = + (self.config.internal_l1_pricing_multiplier * effective_gas_price as f64) as u64; + + // Bound the price if it's too high. + self.bound_gas_price(calculated_price) + } + + fn estimate_effective_pubdata_price(&self) -> u64 { + if let Some(price) = self.config.internal_enforced_pubdata_price { + return price; + } + + match self.pubdata_sending_mode { + PubdataSendingMode::Blobs => { + const BLOB_GAS_PER_BYTE: u64 = 1; // `BYTES_PER_BLOB` = `GAS_PER_BLOB` = 2 ^ 17. + + let blob_base_fee_median = self.blob_base_fee_statistics.median(); + + // Check if blob base fee overflows `u64` before converting. Can happen only in very extreme cases. + if blob_base_fee_median > U256::from(u64::MAX) { + let max_allowed = self.config.max_blob_base_fee(); + tracing::error!("Blob base fee is too high: {blob_base_fee_median}, using max allowed: {max_allowed}"); + return max_allowed; + } + METRICS + .median_blob_base_fee + .set(blob_base_fee_median.as_u64()); + let calculated_price = blob_base_fee_median.as_u64() as f64 + * BLOB_GAS_PER_BYTE as f64 + * self.config.internal_pubdata_pricing_multiplier; + + self.bound_blob_base_fee(calculated_price) + } + PubdataSendingMode::Calldata => { + self.estimate_effective_gas_price() * self.pubdata_byte_gas() + } + } + } +} + impl GasAdjuster { pub async fn new( eth_client: Box>, @@ -155,54 +209,6 @@ impl GasAdjuster { Ok(()) } - /// Returns the sum of base and priority fee, in wei, not considering time in mempool. - /// Can be used to get an estimate of current gas price. - pub(crate) fn estimate_effective_gas_price(&self) -> u64 { - if let Some(price) = self.config.internal_enforced_l1_gas_price { - return price; - } - - let effective_gas_price = self.get_base_fee(0) + self.get_priority_fee(); - - let calculated_price = - (self.config.internal_l1_pricing_multiplier * effective_gas_price as f64) as u64; - - // Bound the price if it's too high. - self.bound_gas_price(calculated_price) - } - - pub(crate) fn estimate_effective_pubdata_price(&self) -> u64 { - if let Some(price) = self.config.internal_enforced_pubdata_price { - return price; - } - - match self.pubdata_sending_mode { - PubdataSendingMode::Blobs => { - const BLOB_GAS_PER_BYTE: u64 = 1; // `BYTES_PER_BLOB` = `GAS_PER_BLOB` = 2 ^ 17. - - let blob_base_fee_median = self.blob_base_fee_statistics.median(); - - // Check if blob base fee overflows `u64` before converting. Can happen only in very extreme cases. - if blob_base_fee_median > U256::from(u64::MAX) { - let max_allowed = self.config.max_blob_base_fee(); - tracing::error!("Blob base fee is too high: {blob_base_fee_median}, using max allowed: {max_allowed}"); - return max_allowed; - } - METRICS - .median_blob_base_fee - .set(blob_base_fee_median.as_u64()); - let calculated_price = blob_base_fee_median.as_u64() as f64 - * BLOB_GAS_PER_BYTE as f64 - * self.config.internal_pubdata_pricing_multiplier; - - self.bound_blob_base_fee(calculated_price) - } - PubdataSendingMode::Calldata => { - self.estimate_effective_gas_price() * self.pubdata_byte_gas() - } - } - } - fn pubdata_byte_gas(&self) -> u64 { match self.commitment_mode { L1BatchCommitmentMode::Validium => 0, @@ -415,3 +421,29 @@ impl GasStatistics { self.0.read().unwrap().last_processed_block } } + +#[derive(Debug)] +#[allow(dead_code)] +pub struct MockGasAdjuster { + pub effective_l1_gas_price: u64, + pub effective_l1_pubdata_price: u64, +} + +impl L1GasAdjuster for MockGasAdjuster { + fn estimate_effective_gas_price(&self) -> u64 { + self.effective_l1_gas_price + } + + fn estimate_effective_pubdata_price(&self) -> u64 { + self.effective_l1_pubdata_price + } +} + +impl MockGasAdjuster { + pub fn new(effective_l1_gas_price: u64, effective_l1_pubdata_price: u64) -> Self { + Self { + effective_l1_gas_price, + effective_l1_pubdata_price, + } + } +} diff --git a/core/node/fee_model/src/l1_gas_price/mod.rs b/core/node/fee_model/src/l1_gas_price/mod.rs index 219dc2f9c38d..0f0a6918328b 100644 --- a/core/node/fee_model/src/l1_gas_price/mod.rs +++ b/core/node/fee_model/src/l1_gas_price/mod.rs @@ -3,7 +3,8 @@ use std::fmt; pub use self::{ - gas_adjuster::GasAdjuster, main_node_fetcher::MainNodeFeeParamsFetcher, + gas_adjuster::{GasAdjuster, L1GasAdjuster, MockGasAdjuster}, + main_node_fetcher::MainNodeFeeParamsFetcher, singleton::GasAdjusterSingleton, }; diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 66063097cfac..0634c630a719 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -13,7 +13,7 @@ use zksync_types::{ }; use zksync_utils::ceil_div_u256; -use crate::l1_gas_price::GasAdjuster; +use crate::l1_gas_price::L1GasAdjuster; pub mod l1_gas_price; @@ -60,7 +60,7 @@ impl dyn BatchFeeModelInputProvider { /// it from other node. #[derive(Debug)] pub struct MainNodeFeeInputProvider { - provider: Arc, + provider: Arc, base_token_adjuster: Arc, config: FeeModelConfig, } @@ -101,7 +101,7 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { impl MainNodeFeeInputProvider { pub fn new( - provider: Arc, + provider: Arc, base_token_adjuster: Arc, config: FeeModelConfig, ) -> Self { @@ -311,7 +311,12 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { #[cfg(test)] mod tests { + use bigdecimal::FromPrimitive; + use test_casing::test_casing; + use zksync_base_token_adjuster::MockBaseTokenAdjuster; + use super::*; + use crate::l1_gas_price::MockGasAdjuster; // To test that overflow never happens, we'll use giant L1 gas price, i.e. // almost realistic very large value of 100k gwei. Since it is so large, we'll also @@ -321,6 +326,12 @@ mod tests { // As a small small L2 gas price we'll use the value of 1 wei. const SMALL_L1_GAS_PRICE: u64 = 1; + // Conversion ratio for ETH to base token (1ETH = 200K BaseToken) + const ETH_TO_BASE_TOKEN: u64 = 200000; + + // Conversion ratio for ETH to ETH (1ETH = 1ETH) + const ETH_TO_ETH: u64 = 1; + #[test] fn test_compute_batch_fee_model_input_v2_giant_numbers() { let config = FeeModelConfigV2 { @@ -534,4 +545,64 @@ mod tests { "Max pubdata increase lowers pubdata price" ); } + + #[test_casing(2, [("ETH", ETH_TO_ETH),("ZK", ETH_TO_BASE_TOKEN)])] + #[tokio::test] + async fn test_get_fee_model_params(base_token: &str, conversion_ratio: u64) { + let conversion_ratio_bd = BigDecimal::from_u64(conversion_ratio).unwrap(); + let in_effective_l1_gas_price = 10_000_000_000; // 10 gwei + let in_effective_l1_pubdata_price = 20_000_000; // 0.002 gwei + let in_minimal_l2_gas_price = 25_000_000; // 0.025 gwei + + let gas_adjuster = Arc::new(MockGasAdjuster::new( + in_effective_l1_gas_price, + in_effective_l1_pubdata_price, + )); + + let base_token_adjuster = Arc::new(MockBaseTokenAdjuster::new( + conversion_ratio_bd.clone(), + base_token.to_string(), + )); + + let config = FeeModelConfig::V2(FeeModelConfigV2 { + minimal_l2_gas_price: in_minimal_l2_gas_price, + compute_overhead_part: 1.0, + pubdata_overhead_part: 1.0, + batch_overhead_l1_gas: 1_000_000, + max_gas_per_batch: 50_000_000, + max_pubdata_per_batch: 100_000, + }); + + let fee_provider = MainNodeFeeInputProvider::new( + gas_adjuster.clone(), + base_token_adjuster.clone(), + config, + ); + + let fee_params = fee_provider.get_fee_model_params().await.unwrap(); + + let expected_l1_gas_price = (BigDecimal::from(in_effective_l1_gas_price) + * conversion_ratio_bd.clone()) + .to_u64() + .unwrap(); + let expected_l1_pubdata_price = (BigDecimal::from(in_effective_l1_pubdata_price) + * conversion_ratio_bd.clone()) + .to_u64() + .unwrap(); + let expected_minimal_l2_gas_price = (BigDecimal::from(in_minimal_l2_gas_price) + * conversion_ratio_bd) + .to_u64() + .unwrap(); + + if let FeeParams::V2(params) = fee_params { + assert_eq!(params.l1_gas_price, expected_l1_gas_price); + assert_eq!(params.l1_pubdata_price, expected_l1_pubdata_price); + assert_eq!( + params.config.minimal_l2_gas_price, + expected_minimal_l2_gas_price + ); + } else { + panic!("Expected FeeParams::V2"); + } + } } From 92ce099271e9438e97922dbe25bcfc3b154f0340 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Sun, 23 Jun 2024 15:10:28 +0300 Subject: [PATCH 20/48] self CR and unit test. i tests out --- Cargo.lock | 1 + ...263bca6b63f6793b4e92e179a48491282d4c.json} | 4 +- ...ba6b5dc624747adb73c9f94afe84e61f7513d.json | 4 +- ...11121747_add_base_token_price_table.up.sql | 4 +- core/lib/dal/src/base_token_dal.rs | 12 +- .../src/models/storage_base_token_price.rs | 8 +- core/lib/types/src/base_token_price.rs | 8 +- core/node/base_token_adjuster/Cargo.toml | 1 + .../src/base_token_adjuster.rs | 253 +++++++++++++-- .../src/l1_gas_price/main_node_fetcher.rs | 7 + core/node/fee_model/src/lib.rs | 287 +++++++++--------- core/node/state_keeper/src/io/tests/tester.rs | 2 +- prover/Cargo.lock | 2 + 13 files changed, 409 insertions(+), 184 deletions(-) rename core/lib/dal/.sqlx/{query-4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e.json => query-4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c.json} (57%) diff --git a/Cargo.lock b/Cargo.lock index e89523289a08..b101714c7599 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7984,6 +7984,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "bigdecimal", "chrono", "rand 0.8.5", "tokio", diff --git a/core/lib/dal/.sqlx/query-4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e.json b/core/lib/dal/.sqlx/query-4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c.json similarity index 57% rename from core/lib/dal/.sqlx/query-4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e.json rename to core/lib/dal/.sqlx/query-4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c.json index 37e823643043..894812a14df8 100644 --- a/core/lib/dal/.sqlx/query-4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e.json +++ b/core/lib/dal/.sqlx/query-4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO\n base_token_prices (numerator, denominator, ratio_timestamp)\n VALUES\n ($1, $2, $3)\n RETURNING\n id\n ", + "query": "\n INSERT INTO\n base_token_prices (base_token_price, eth_price, ratio_timestamp, created_at, updated_at)\n VALUES\n ($1, $2, $3, NOW(), NOW())\n RETURNING\n id\n ", "describe": { "columns": [ { @@ -20,5 +20,5 @@ false ] }, - "hash": "4b87a7531bee38f8cbfb111c116b49c9306c7af534dcccbf1976553cfa84fe6e" + "hash": "4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c" } diff --git a/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json b/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json index 00224846ffca..f235bbf16093 100644 --- a/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json +++ b/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json @@ -25,12 +25,12 @@ }, { "ordinal": 4, - "name": "numerator", + "name": "base_token_price", "type_info": "Numeric" }, { "ordinal": 5, - "name": "denominator", + "name": "eth_price", "type_info": "Numeric" }, { diff --git a/core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql b/core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql index 4d3aa4c46cd3..ba9a934794d6 100644 --- a/core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql +++ b/core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql @@ -4,8 +4,8 @@ CREATE TABLE base_token_prices ( updated_at TIMESTAMP NOT NULL, ratio_timestamp TIMESTAMP NOT NULL, - numerator NUMERIC NOT NULL, - denominator NUMERIC NOT NULL, + base_token_price NUMERIC NOT NULL, + eth_price NUMERIC NOT NULL, used_in_l1 BOOLEAN NOT NULL DEFAULT FALSE ); diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index 3bd132ca2b2e..3b812a54e666 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -11,21 +11,21 @@ pub struct BaseTokenDal<'a, 'c> { impl BaseTokenDal<'_, '_> { pub async fn insert_token_price( &mut self, - numerator: &BigDecimal, - denominator: &BigDecimal, + base_token_price: &BigDecimal, + eth_price: &BigDecimal, ratio_timestamp: &chrono::NaiveDateTime, ) -> DalResult { let row = sqlx::query!( r#" INSERT INTO - base_token_prices (numerator, denominator, ratio_timestamp) + base_token_prices (base_token_price, eth_price, ratio_timestamp, created_at, updated_at) VALUES - ($1, $2, $3) + ($1, $2, $3, NOW(), NOW()) RETURNING id "#, - numerator, - denominator, + base_token_price, + eth_price, ratio_timestamp, ) .instrument("insert_base_token_price") diff --git a/core/lib/dal/src/models/storage_base_token_price.rs b/core/lib/dal/src/models/storage_base_token_price.rs index ed4b015b0ddf..f7d8634f90e4 100644 --- a/core/lib/dal/src/models/storage_base_token_price.rs +++ b/core/lib/dal/src/models/storage_base_token_price.rs @@ -9,8 +9,8 @@ pub struct StorageBaseTokenPrice { pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub ratio_timestamp: NaiveDateTime, - pub numerator: BigDecimal, - pub denominator: BigDecimal, + pub base_token_price: BigDecimal, + pub eth_price: BigDecimal, pub used_in_l1: bool, } @@ -19,8 +19,8 @@ impl From for BaseTokenPrice { BaseTokenPrice { id: row.id, ratio_timestamp: row.ratio_timestamp.and_utc(), - numerator: row.numerator, - denominator: row.denominator, + base_token_price: row.base_token_price, + eth_price: row.eth_price, used_in_l1: row.used_in_l1, } } diff --git a/core/lib/types/src/base_token_price.rs b/core/lib/types/src/base_token_price.rs index f1024cbdd3aa..9c093d8a6fda 100644 --- a/core/lib/types/src/base_token_price.rs +++ b/core/lib/types/src/base_token_price.rs @@ -6,16 +6,16 @@ use chrono::{DateTime, Utc}; pub struct BaseTokenPrice { pub id: i64, pub ratio_timestamp: DateTime, - pub numerator: BigDecimal, - pub denominator: BigDecimal, + pub base_token_price: BigDecimal, + pub eth_price: BigDecimal, pub used_in_l1: bool, } /// Struct to represent API response containing denominator, numerator and optional timestamp. #[derive(Debug, Default)] pub struct BaseTokenAPIPrice { - pub numerator: BigDecimal, - pub denominator: BigDecimal, + pub base_token_price: BigDecimal, + pub eth_price: BigDecimal, // Either the timestamp of the quote or the timestamp of the request. pub ratio_timestamp: DateTime, } diff --git a/core/node/base_token_adjuster/Cargo.toml b/core/node/base_token_adjuster/Cargo.toml index 2b91a0dac2dc..06c67225cf1b 100644 --- a/core/node/base_token_adjuster/Cargo.toml +++ b/core/node/base_token_adjuster/Cargo.toml @@ -16,6 +16,7 @@ zksync_dal.workspace = true zksync_utils.workspace = true zksync_config.workspace = true zksync_types.workspace = true +bigdecimal.workspace = true tokio = { workspace = true, features = ["time"] } anyhow.workspace = true diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index b1963cdd5069..a64d959a648f 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -6,23 +6,27 @@ use std::{ use anyhow::Context as _; use async_trait::async_trait; +use bigdecimal::{BigDecimal, ToPrimitive}; use chrono::Utc; use tokio::sync::watch; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; -use zksync_dal::{BigDecimal, ConnectionPool, Core, CoreDal}; -use zksync_types::base_token_price::BaseTokenAPIPrice; +use zksync_dal::{ConnectionPool, Core, CoreDal}; +use zksync_types::{ + base_token_price::BaseTokenAPIPrice, + fee_model::{FeeModelConfigV2, FeeParams, FeeParamsV2}, +}; #[async_trait] pub trait BaseTokenAdjuster: Debug + Send + Sync { /// Returns the last ratio cached by the adjuster and ensure it's still usable. - async fn get_last_ratio_and_check_usability<'a>(&'a self) -> BigDecimal; + async fn maybe_convert_to_base_token(&self, params: FeeParams) -> anyhow::Result; /// Return configured symbol of the base token. fn get_base_token(&self) -> &str; } #[derive(Debug)] -/// BaseTokenAdjuster implementation for the main node (not the External Node). +/// BaseTokenAdjuster implementation for the main node (not the External Node). TODO (PE-137): impl APIBaseTokenAdjuster pub struct MainNodeBaseTokenAdjuster { pool: ConnectionPool, config: BaseTokenAdjusterConfig, @@ -65,13 +69,13 @@ impl MainNodeBaseTokenAdjuster { // TODO (PE-135): Use real API client to fetch new ratio through self.PriceAPIClient & mock for tests. // For now, these hard coded values are also hard coded in the integration tests. async fn fetch_new_ratio(&self) -> anyhow::Result { - let new_numerator = BigDecimal::from(100000); - let new_denominator = BigDecimal::from(1); + let new_base_token_price = BigDecimal::from(1); + let new_eth_price = BigDecimal::from(100000); let ratio_timestamp = Utc::now(); Ok(BaseTokenAPIPrice { - numerator: new_numerator, - denominator: new_denominator, + base_token_price: new_base_token_price, + eth_price: new_eth_price, ratio_timestamp, }) } @@ -89,8 +93,8 @@ impl MainNodeBaseTokenAdjuster { let id = conn .base_token_dal() .insert_token_price( - &api_price.numerator, - &api_price.denominator, + &api_price.base_token_price, + &api_price.eth_price, &api_price.ratio_timestamp.naive_utc(), ) .await?; @@ -124,25 +128,35 @@ impl MainNodeBaseTokenAdjuster { #[async_trait] impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { // TODO (PE-129): Implement latest ratio usability logic. - async fn get_last_ratio_and_check_usability<'a>(&'a self) -> BigDecimal { + async fn maybe_convert_to_base_token(&self, params: FeeParams) -> anyhow::Result { let mut conn = self .pool .connection_tagged("base_token_adjuster") .await .expect("Failed to obtain connection to the database"); - let last_storage_ratio = conn + let last_storage_price = conn .base_token_dal() .get_latest_price() .await .expect("Failed to get latest base token price"); drop(conn); - let last_ratio = last_storage_ratio - .numerator - .div(&last_storage_ratio.denominator); + let last_ratio = last_storage_price + .base_token_price + .div(&last_storage_price.eth_price); - last_ratio + let base_token = self.get_base_token(); + match base_token { + "ETH" => Ok(params), + _ => { + if let FeeParams::V2(params_v2) = params { + Ok(FeeParams::V2(convert_to_base_token(params_v2, last_ratio))) + } else { + panic!("Custom base token is not supported for V1 fee model") + } + } + } } /// Return configured symbol of the base token. If not configured, return "ETH". @@ -154,6 +168,49 @@ impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { } } +/// Converts the fee parameters to the base token using the latest ratio fetched from the DB. +fn convert_to_base_token(params: FeeParamsV2, base_token_to_eth: BigDecimal) -> FeeParamsV2 { + let FeeParamsV2 { + config, + l1_gas_price, + l1_pubdata_price, + } = params; + + let convert_price = |price_in_wei: u64| -> u64 { + let converted_price_bd = BigDecimal::from(price_in_wei) * base_token_to_eth.clone(); + match converted_price_bd.to_u64() { + Some(converted_price) => converted_price, + None => { + if converted_price_bd > BigDecimal::from(u64::MAX) { + tracing::warn!( + "Conversion to base token price failed: converted price is too large: {}", + converted_price_bd + ); + } else { + tracing::error!( + "Conversion to base token price failed: converted price is not a valid u64: {}", + converted_price_bd + ); + } + u64::MAX + } + } + }; + + let l1_gas_price_converted = convert_price(l1_gas_price); + let l1_pubdata_price_converted = convert_price(l1_pubdata_price); + let minimal_l2_gas_price_converted = convert_price(config.minimal_l2_gas_price); + + FeeParamsV2 { + config: FeeModelConfigV2 { + minimal_l2_gas_price: minimal_l2_gas_price_converted, + ..config + }, + l1_gas_price: l1_gas_price_converted, + l1_pubdata_price: l1_pubdata_price_converted, + } +} + #[derive(Debug)] #[allow(dead_code)] pub struct MockBaseTokenAdjuster { @@ -172,11 +229,171 @@ impl MockBaseTokenAdjuster { #[async_trait] impl BaseTokenAdjuster for MockBaseTokenAdjuster { - async fn get_last_ratio_and_check_usability(&self) -> BigDecimal { - self.last_ratio.clone() + async fn maybe_convert_to_base_token(&self, params: FeeParams) -> anyhow::Result { + // LOG THE PARAMS + tracing::info!("Params: {:?}", params); + match self.get_base_token() { + "ETH" => Ok(params), + _ => { + if let FeeParams::V2(params_v2) = params { + Ok(FeeParams::V2(convert_to_base_token( + params_v2, + self.last_ratio.clone(), + ))) + } else { + panic!("Custom base token is not supported for V1 fee model") + } + } + } } fn get_base_token(&self) -> &str { &self.base_token } } + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use bigdecimal::BigDecimal; + use zksync_types::fee_model::{FeeModelConfigV2, FeeParamsV2}; + + use super::*; + + #[test] + fn test_convert_to_base_token() { + let base_fee_model_config = FeeModelConfigV2 { + minimal_l2_gas_price: 0, + // All the below are unaffected by this flow. + compute_overhead_part: 1.0, + pubdata_overhead_part: 1.0, + batch_overhead_l1_gas: 1, + max_gas_per_batch: 1, + max_pubdata_per_batch: 1, + }; + + struct TestCase { + name: &'static str, + base_token_to_eth: BigDecimal, + input_minimal_l2_gas_price: u64, + input_l1_gas_price: u64, + input_l1_pubdata_price: u64, + expected_minimal_l2_gas_price: u64, + expected_l1_gas_price: u64, + expected_l1_pubdata_price: u64, + } + + let test_cases = vec![ + TestCase { + name: "1 ETH = 2 BaseToken", + base_token_to_eth: BigDecimal::from(2), + input_minimal_l2_gas_price: 1000, + input_l1_gas_price: 2000, + input_l1_pubdata_price: 3000, + expected_minimal_l2_gas_price: 2000, + expected_l1_gas_price: 4000, + expected_l1_pubdata_price: 6000, + }, + TestCase { + name: "1 ETH = 0.5 BaseToken", + base_token_to_eth: BigDecimal::from_str("0.5").unwrap(), + input_minimal_l2_gas_price: 1000, + input_l1_gas_price: 2000, + input_l1_pubdata_price: 3000, + expected_minimal_l2_gas_price: 500, + expected_l1_gas_price: 1000, + expected_l1_pubdata_price: 1500, + }, + TestCase { + name: "1 ETH = 1 BaseToken", + base_token_to_eth: BigDecimal::from(1), + input_minimal_l2_gas_price: 1000, + input_l1_gas_price: 2000, + input_l1_pubdata_price: 3000, + expected_minimal_l2_gas_price: 1000, + expected_l1_gas_price: 2000, + expected_l1_pubdata_price: 3000, + }, + TestCase { + name: "Small conversion - 1 ETH - 1_000_000 BaseToken", + base_token_to_eth: BigDecimal::from_str("0.000001").unwrap(), + input_minimal_l2_gas_price: 1_000_000, + input_l1_gas_price: 2_000_000, + input_l1_pubdata_price: 3_000_000, + expected_minimal_l2_gas_price: 1, + expected_l1_gas_price: 2, + expected_l1_pubdata_price: 3, + }, + TestCase { + name: "Large conversion - 1 ETH = 0.000001 BaseToken", + base_token_to_eth: BigDecimal::from_str("1000000").unwrap(), + input_minimal_l2_gas_price: 1, + input_l1_gas_price: 2, + input_l1_pubdata_price: 3, + expected_minimal_l2_gas_price: 1_000_000, + expected_l1_gas_price: 2_000_000, + expected_l1_pubdata_price: 3_000_000, + }, + TestCase { + name: "Fractional conversion ratio", + base_token_to_eth: BigDecimal::from_str("1.123456789").unwrap(), + input_minimal_l2_gas_price: 1000, + input_l1_gas_price: 2000, + input_l1_pubdata_price: 3000, + expected_minimal_l2_gas_price: 1123, + expected_l1_gas_price: 2246, + expected_l1_pubdata_price: 3370, + }, + TestCase { + name: "Zero conversion ratio", + base_token_to_eth: BigDecimal::from(0), + input_minimal_l2_gas_price: 1000, + input_l1_gas_price: 2000, + input_l1_pubdata_price: 3000, + expected_minimal_l2_gas_price: 0, + expected_l1_gas_price: 0, + expected_l1_pubdata_price: 0, + }, + TestCase { + name: "Conversion ratio too large so clamp down to u64::MAX", + base_token_to_eth: BigDecimal::from(u64::MAX), + input_minimal_l2_gas_price: 2, + input_l1_gas_price: 2, + input_l1_pubdata_price: 2, + expected_minimal_l2_gas_price: u64::MAX, + expected_l1_gas_price: u64::MAX, + expected_l1_pubdata_price: u64::MAX, + }, + ]; + + for case in test_cases { + let input_params = FeeParamsV2 { + config: FeeModelConfigV2 { + minimal_l2_gas_price: case.input_minimal_l2_gas_price, + ..base_fee_model_config + }, + l1_gas_price: case.input_l1_gas_price, + l1_pubdata_price: case.input_l1_pubdata_price, + }; + + let result = convert_to_base_token(input_params, case.base_token_to_eth.clone()); + + assert_eq!( + result.config.minimal_l2_gas_price, case.expected_minimal_l2_gas_price, + "Test case '{}' failed: minimal_l2_gas_price mismatch", + case.name + ); + assert_eq!( + result.l1_gas_price, case.expected_l1_gas_price, + "Test case '{}' failed: l1_gas_price mismatch", + case.name + ); + assert_eq!( + result.l1_pubdata_price, case.expected_l1_pubdata_price, + "Test case '{}' failed: l1_pubdata_price mismatch", + case.name + ); + } + } +} diff --git a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs index dd659e984d41..0e93dfeae573 100644 --- a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs +++ b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs @@ -76,4 +76,11 @@ impl BatchFeeModelInputProvider for MainNodeFeeParamsFetcher { async fn get_fee_model_params(&self) -> anyhow::Result { Ok(*self.main_node_fee_params.read().unwrap()) } + + async fn maybe_convert_params_to_base_token( + &self, + _params: FeeParams, + ) -> anyhow::Result { + Ok(_params) + } } diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 0634c630a719..f351f7caa30d 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -1,7 +1,7 @@ use std::{fmt, sync::Arc}; use anyhow::Context as _; -use bigdecimal::{BigDecimal, ToPrimitive}; +use async_trait::async_trait; use zksync_base_token_adjuster::BaseTokenAdjuster; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ @@ -18,7 +18,7 @@ use crate::l1_gas_price::L1GasAdjuster; pub mod l1_gas_price; /// Trait responsible for providing fee info for a batch -#[async_trait::async_trait] +#[async_trait] pub trait BatchFeeModelInputProvider: fmt::Debug + 'static + Send + Sync { /// Returns the batch fee with scaling applied. This may be used to account for the fact that the L1 gas and pubdata prices may fluctuate, esp. /// in API methods that should return values that are valid for some period of time after the estimation was done. @@ -45,7 +45,17 @@ pub trait BatchFeeModelInputProvider: fmt::Debug + 'static + Send + Sync { } /// Returns the fee model parameters using the denomination of the base token used (WEI for ETH). - async fn get_fee_model_params(&self) -> anyhow::Result; + async fn get_fee_model_params(&self) -> anyhow::Result { + let unconverted_params = self.get_fee_model_params().await?; + self.maybe_convert_params_to_base_token(unconverted_params) + .await + } + + /// Converts the fee model parameters to the base token denomination. + async fn maybe_convert_params_to_base_token( + &self, + params: FeeParams, + ) -> anyhow::Result; } impl dyn BatchFeeModelInputProvider { @@ -55,9 +65,9 @@ impl dyn BatchFeeModelInputProvider { } } -/// The struct that represents the batch fee input provider to be used in the main node of the server, i.e. -/// it explicitly gets the L1 gas price from the provider and uses it to calculate the batch fee input instead of getting -/// it from other node. +/// The struct that represents the batch fee input provider to be used in the main node of the server. +/// This struct gets the L1 gas price directly from the provider rather than from another node, as is the +/// case with the external node. #[derive(Debug)] pub struct MainNodeFeeInputProvider { provider: Arc, @@ -65,37 +75,31 @@ pub struct MainNodeFeeInputProvider { config: FeeModelConfig, } -#[async_trait::async_trait] +#[async_trait] impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { async fn get_fee_model_params(&self) -> anyhow::Result { - match self.config { - FeeModelConfig::V1(config) => Ok(FeeParams::V1(FeeParamsV1 { + let params = match self.config { + FeeModelConfig::V1(config) => FeeParams::V1(FeeParamsV1 { config, l1_gas_price: self.provider.estimate_effective_gas_price(), - })), - FeeModelConfig::V2(config) => { - let params = FeeParamsV2 { - config, - l1_gas_price: self.provider.estimate_effective_gas_price(), - l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), - }; - - let base_token = self.base_token_adjuster.get_base_token(); - match base_token { - "ETH" => Ok(FeeParams::V2(params)), - _ => { - let base_token_conversion_ratio = self - .base_token_adjuster - .get_last_ratio_and_check_usability() - .await; - Ok(FeeParams::V2(convert_to_base_token( - params, - base_token_conversion_ratio, - ))) - } - } - } - } + }), + FeeModelConfig::V2(config) => FeeParams::V2(FeeParamsV2 { + config, + l1_gas_price: self.provider.estimate_effective_gas_price(), + l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), + }), + }; + + self.maybe_convert_params_to_base_token(params).await + } + + async fn maybe_convert_params_to_base_token( + &self, + params: FeeParams, + ) -> anyhow::Result { + self.base_token_adjuster + .maybe_convert_to_base_token(params) + .await } } @@ -113,52 +117,6 @@ impl MainNodeFeeInputProvider { } } -fn convert_to_base_token( - params: FeeParamsV2, - base_token_conversion_ratio: BigDecimal, -) -> FeeParamsV2 { - let FeeParamsV2 { - config, - l1_gas_price, - l1_pubdata_price, - } = params; - - let convert_price = |price_in_wei: u64, eth_to_base_token_ratio: &BigDecimal| -> u64 { - let converted_price_bd = BigDecimal::from(price_in_wei) * eth_to_base_token_ratio; - match converted_price_bd.to_u64() { - Some(converted_price) => converted_price, - None => { - if converted_price_bd > BigDecimal::from(u64::MAX) { - tracing::warn!( - "Conversion to base token price failed: converted price is too large: {}", - converted_price_bd - ); - } else { - tracing::error!( - "Conversion to base token price failed: converted price is not a valid u64: {}", - converted_price_bd - ); - } - u64::MAX - } - } - }; - - let l1_gas_price_converted = convert_price(l1_gas_price, &base_token_conversion_ratio); - let l1_pubdata_price_converted = convert_price(l1_pubdata_price, &base_token_conversion_ratio); - let minimal_l2_gas_price_converted = - convert_price(config.minimal_l2_gas_price, &base_token_conversion_ratio); - - FeeParamsV2 { - config: FeeModelConfigV2 { - minimal_l2_gas_price: minimal_l2_gas_price_converted, - ..config - }, - l1_gas_price: l1_gas_price_converted, - l1_pubdata_price: l1_pubdata_price_converted, - } -} - /// The fee model provider to be used in the API. It returns the maximum batch fee input between the projected main node one and /// the one from the last sealed L2 block. #[derive(Debug)] @@ -179,7 +137,7 @@ impl ApiFeeInputProvider { } } -#[async_trait::async_trait] +#[async_trait] impl BatchFeeModelInputProvider for ApiFeeInputProvider { async fn get_batch_fee_input_scaled( &self, @@ -208,6 +166,13 @@ impl BatchFeeModelInputProvider for ApiFeeInputProvider { async fn get_fee_model_params(&self) -> anyhow::Result { self.inner.get_fee_model_params().await } + + async fn maybe_convert_params_to_base_token( + &self, + params: FeeParams, + ) -> anyhow::Result { + self.inner.maybe_convert_params_to_base_token(params).await + } } /// Calculates the batch fee input based on the main node parameters. @@ -302,17 +267,25 @@ impl Default for MockBatchFeeParamsProvider { } } -#[async_trait::async_trait] +#[async_trait] impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { - async fn get_fee_model_params<'a>(&'a self) -> anyhow::Result { + async fn get_fee_model_params(&self) -> anyhow::Result { Ok(self.0) } + + async fn maybe_convert_params_to_base_token( + &self, + params: FeeParams, + ) -> anyhow::Result { + Ok(params) + } } #[cfg(test)] mod tests { - use bigdecimal::FromPrimitive; - use test_casing::test_casing; + // use bigdecimal::{BigDecimal, ToPrimitive}; + use bigdecimal::BigDecimal; + // use test_casing::test_casing; use zksync_base_token_adjuster::MockBaseTokenAdjuster; use super::*; @@ -326,12 +299,6 @@ mod tests { // As a small small L2 gas price we'll use the value of 1 wei. const SMALL_L1_GAS_PRICE: u64 = 1; - // Conversion ratio for ETH to base token (1ETH = 200K BaseToken) - const ETH_TO_BASE_TOKEN: u64 = 200000; - - // Conversion ratio for ETH to ETH (1ETH = 1ETH) - const ETH_TO_ETH: u64 = 1; - #[test] fn test_compute_batch_fee_model_input_v2_giant_numbers() { let config = FeeModelConfigV2 { @@ -414,7 +381,7 @@ mod tests { } #[test] - fn test_compute_batch_fee_model_input_v2_only_compute_overhead() { + fn test_compute_baxtch_fee_model_input_v2_only_compute_overhead() { // Here we use sensible config, but when only compute is used to close the batch let config = FeeModelConfigV2 { minimal_l2_gas_price: 100_000_000_000, @@ -546,63 +513,93 @@ mod tests { ); } - #[test_casing(2, [("ETH", ETH_TO_ETH),("ZK", ETH_TO_BASE_TOKEN)])] #[tokio::test] - async fn test_get_fee_model_params(base_token: &str, conversion_ratio: u64) { - let conversion_ratio_bd = BigDecimal::from_u64(conversion_ratio).unwrap(); - let in_effective_l1_gas_price = 10_000_000_000; // 10 gwei - let in_effective_l1_pubdata_price = 20_000_000; // 0.002 gwei - let in_minimal_l2_gas_price = 25_000_000; // 0.025 gwei - - let gas_adjuster = Arc::new(MockGasAdjuster::new( - in_effective_l1_gas_price, - in_effective_l1_pubdata_price, - )); - - let base_token_adjuster = Arc::new(MockBaseTokenAdjuster::new( - conversion_ratio_bd.clone(), - base_token.to_string(), - )); - - let config = FeeModelConfig::V2(FeeModelConfigV2 { - minimal_l2_gas_price: in_minimal_l2_gas_price, - compute_overhead_part: 1.0, - pubdata_overhead_part: 1.0, - batch_overhead_l1_gas: 1_000_000, - max_gas_per_batch: 50_000_000, - max_pubdata_per_batch: 100_000, - }); - - let fee_provider = MainNodeFeeInputProvider::new( - gas_adjuster.clone(), - base_token_adjuster.clone(), - config, - ); + async fn test_get_fee_model_params() { + struct TestCase { + name: &'static str, + base_token: String, + base_token_to_eth: BigDecimal, + effective_l1_gas_price: u64, + effective_l1_pubdata_price: u64, + minimal_l2_gas_price: u64, + expected_l1_gas_price: u64, + expected_l1_pubdata_price: u64, + expected_minimal_l2_gas_price: u64, + } - let fee_params = fee_provider.get_fee_model_params().await.unwrap(); - - let expected_l1_gas_price = (BigDecimal::from(in_effective_l1_gas_price) - * conversion_ratio_bd.clone()) - .to_u64() - .unwrap(); - let expected_l1_pubdata_price = (BigDecimal::from(in_effective_l1_pubdata_price) - * conversion_ratio_bd.clone()) - .to_u64() - .unwrap(); - let expected_minimal_l2_gas_price = (BigDecimal::from(in_minimal_l2_gas_price) - * conversion_ratio_bd) - .to_u64() - .unwrap(); - - if let FeeParams::V2(params) = fee_params { - assert_eq!(params.l1_gas_price, expected_l1_gas_price); - assert_eq!(params.l1_pubdata_price, expected_l1_pubdata_price); - assert_eq!( - params.config.minimal_l2_gas_price, - expected_minimal_l2_gas_price + let test_cases = vec![ + TestCase { + name: "Convert to a custom base token", + base_token: "ZK".to_string(), + base_token_to_eth: BigDecimal::from(200000), + effective_l1_gas_price: 10_000_000_000, // 10 gwei + effective_l1_pubdata_price: 20_000_000, // 0.02 gwei + minimal_l2_gas_price: 25_000_000, // 0.025 gwei + expected_l1_gas_price: 2_000_000_000_000_000, + expected_l1_pubdata_price: 4_000_000_000_000, + expected_minimal_l2_gas_price: 5_000_000_000_000, + }, + TestCase { + name: "ETH as base token (no conversion)", + base_token: "ETH".to_string(), + base_token_to_eth: BigDecimal::from(1), + effective_l1_gas_price: 15_000_000_000, // 15 gwei + effective_l1_pubdata_price: 30_000_000, // 0.03 gwei + minimal_l2_gas_price: 40_000_000, // 0.04 gwei + expected_l1_gas_price: 15_000_000_000, + expected_l1_pubdata_price: 30_000_000, + expected_minimal_l2_gas_price: 40_000_000, + }, + ]; + + for case in test_cases { + let gas_adjuster = Arc::new(MockGasAdjuster::new( + case.effective_l1_gas_price, + case.effective_l1_pubdata_price, + )); + + let base_token_adjuster = Arc::new(MockBaseTokenAdjuster::new( + case.base_token_to_eth.clone(), + case.base_token.clone(), + )); + + let config = FeeModelConfig::V2(FeeModelConfigV2 { + minimal_l2_gas_price: case.minimal_l2_gas_price, + // The below values don't matter for this test. + compute_overhead_part: 1.0, + pubdata_overhead_part: 1.0, + batch_overhead_l1_gas: 1, + max_gas_per_batch: 1, + max_pubdata_per_batch: 1, + }); + + let fee_provider = MainNodeFeeInputProvider::new( + gas_adjuster.clone(), + base_token_adjuster.clone(), + config, ); - } else { - panic!("Expected FeeParams::V2"); + + let fee_params = fee_provider.get_fee_model_params().await.unwrap(); + + if let FeeParams::V2(params) = fee_params { + assert_eq!( + params.l1_gas_price, case.expected_l1_gas_price, + "Test case '{}' failed: l1_gas_price mismatch", + case.name + ); + assert_eq!( + params.l1_pubdata_price, case.expected_l1_pubdata_price, + "Test case '{}' failed: l1_pubdata_price mismatch", + case.name + ); + assert_eq!( + params.config.minimal_l2_gas_price, case.expected_minimal_l2_gas_price, + "Test case '{}' failed: minimal_l2_gas_price mismatch", + case.name + ); + } else { + panic!("Expected FeeParams::V2 for test case '{}'", case.name); + } } } } diff --git a/core/node/state_keeper/src/io/tests/tester.rs b/core/node/state_keeper/src/io/tests/tester.rs index 7aa22423eff2..d1d24d7e5fcc 100644 --- a/core/node/state_keeper/src/io/tests/tester.rs +++ b/core/node/state_keeper/src/io/tests/tester.rs @@ -236,7 +236,7 @@ impl Tester { #[derive(Debug)] struct MockBaseTokenAdjuster {} impl BaseTokenAdjuster for MockBaseTokenAdjuster { - fn get_last_ratio_and_check_usability(&self) -> Option { + fn maybe_convert_to_base_token(&self) -> Option { None } } diff --git a/prover/Cargo.lock b/prover/Cargo.lock index a6c0aa45ecb5..27965dfbb507 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8392,6 +8392,8 @@ name = "zksync_base_token_adjuster" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", + "bigdecimal", "chrono", "rand 0.8.5", "tokio", From adba6825b483fd98e3119857da77c86e6e628772 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Sun, 23 Jun 2024 18:46:58 +0300 Subject: [PATCH 21/48] cargo lock --- prover/Cargo.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prover/Cargo.lock b/prover/Cargo.lock index a1c9d0231770..3f953c88fc06 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8485,6 +8485,7 @@ name = "zksync_types" version = "0.1.0" dependencies = [ "anyhow", + "bigdecimal", "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono", "derive_more", @@ -8634,4 +8635,4 @@ checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", -] \ No newline at end of file +] From 681735a4d1c7c4cd9beed3175e6520840940ca72 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Mon, 24 Jun 2024 00:57:26 +0300 Subject: [PATCH 22/48] retry fetching price and some self CR to make CI happy --- Cargo.lock | 3 - .../config/src/configs/base_token_adjuster.rs | 6 +- core/lib/dal/sqlx-data.json | 3 - core/lib/external_price_api/Cargo.toml | 1 - core/lib/external_price_api/src/lib.rs | 2 +- .../src/base_token_adjuster.rs | 4 +- core/lib/zksync_core_leftovers/Cargo.toml | 2 +- core/lib/zksync_core_leftovers/src/lib.rs | 2 +- core/node/base_token_adjuster/Cargo.toml | 2 - core/node/base_token_adjuster/README.md | 12 ++- .../src/base_token_adjuster.rs | 92 +++++++++++++------ core/node/state_keeper/src/io/tests/tester.rs | 16 +--- 12 files changed, 85 insertions(+), 60 deletions(-) delete mode 100644 core/lib/dal/sqlx-data.json diff --git a/Cargo.lock b/Cargo.lock index ebc970a51f9b..46c8736d5b54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7897,11 +7897,9 @@ dependencies = [ "rand 0.8.5", "tokio", "tracing", - "vise", "zksync_config", "zksync_dal", "zksync_types", - "zksync_utils", ] [[package]] @@ -8512,7 +8510,6 @@ dependencies = [ "anyhow", "async-trait", "serde", - "tracing", "zksync_config", "zksync_types", ] diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 3e7c5b6c8ead..2a6b3851b373 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -2,15 +2,15 @@ use std::time::Duration; use serde::Deserialize; -// Fetch new prices every 30 seconds by default. +// By default external APIs shall be polled every 30 seconds for a new price. pub const DEFAULT_INTERVAL_MS: u64 = 30_000; #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct BaseTokenAdjusterConfig { - /// How often to poll external APIs for a new ETH<->Base-Token price. + /// How often to fetch external APIs for a new ETH<->Base-Token price. pub price_polling_interval_ms: Option, - /// Base token symbol. + /// Base token symbol. If none, an assumption of ETH is made. pub base_token: Option, } diff --git a/core/lib/dal/sqlx-data.json b/core/lib/dal/sqlx-data.json deleted file mode 100644 index 95c8c858baaf..000000000000 --- a/core/lib/dal/sqlx-data.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "db": "PostgreSQL" -} \ No newline at end of file diff --git a/core/lib/external_price_api/Cargo.toml b/core/lib/external_price_api/Cargo.toml index 646a82a4aebe..e1c4c99676c7 100644 --- a/core/lib/external_price_api/Cargo.toml +++ b/core/lib/external_price_api/Cargo.toml @@ -11,7 +11,6 @@ categories.workspace = true [dependencies] serde = { workspace = true, features = ["derive"] } -tracing = "0.1.40" async-trait = "0.1.74" anyhow.workspace = true diff --git a/core/lib/external_price_api/src/lib.rs b/core/lib/external_price_api/src/lib.rs index ced897cfff50..0f1675a6b7a6 100644 --- a/core/lib/external_price_api/src/lib.rs +++ b/core/lib/external_price_api/src/lib.rs @@ -6,6 +6,6 @@ use zksync_types::{base_token_price::BaseTokenAPIPrice, Address}; /// Trait that defines the interface for a client connecting with an external API to get prices. #[async_trait] pub trait PriceAPIClient: Sync + Send + fmt::Debug { - /// Returns the price for the input token address in dollars. + /// Returns the price for the input token address in $USD. async fn fetch_price(&self, token_address: Address) -> anyhow::Result; } diff --git a/core/lib/protobuf_config/src/base_token_adjuster.rs b/core/lib/protobuf_config/src/base_token_adjuster.rs index 6c7b8de207b9..c3cd81089498 100644 --- a/core/lib/protobuf_config/src/base_token_adjuster.rs +++ b/core/lib/protobuf_config/src/base_token_adjuster.rs @@ -8,14 +8,14 @@ impl ProtoRepr for proto::BaseTokenAdjuster { fn read(&self) -> anyhow::Result { Ok(configs::base_token_adjuster::BaseTokenAdjusterConfig { - price_polling_interval_ms: self.price_polling_interval_ms.clone(), + price_polling_interval_ms: self.price_polling_interval_ms, base_token: self.base_token.clone(), }) } fn build(this: &Self::Type) -> Self { Self { - price_polling_interval_ms: this.price_polling_interval_ms.clone(), + price_polling_interval_ms: this.price_polling_interval_ms, base_token: this.base_token.clone(), } } diff --git a/core/lib/zksync_core_leftovers/Cargo.toml b/core/lib/zksync_core_leftovers/Cargo.toml index 1c83fdc2acd7..83e22fc6a5eb 100644 --- a/core/lib/zksync_core_leftovers/Cargo.toml +++ b/core/lib/zksync_core_leftovers/Cargo.toml @@ -18,4 +18,4 @@ zksync_node_genesis.workspace = true anyhow.workspace = true tokio = { workspace = true, features = ["time"] } serde_yaml.workspace = true -ctrlc.workspace = true \ No newline at end of file +ctrlc.workspace = true diff --git a/core/lib/zksync_core_leftovers/src/lib.rs b/core/lib/zksync_core_leftovers/src/lib.rs index 8d2b816ad39c..b086a45a9e1f 100644 --- a/core/lib/zksync_core_leftovers/src/lib.rs +++ b/core/lib/zksync_core_leftovers/src/lib.rs @@ -88,7 +88,7 @@ pub enum Component { CommitmentGenerator, /// VM runner-based component that saves protective reads to Postgres. VmRunnerProtectiveReads, - /// A component to adjust anything related to a chain's custom base token. + /// A component to handle anything related to a chain's custom base token. BaseTokenAdjuster, } diff --git a/core/node/base_token_adjuster/Cargo.toml b/core/node/base_token_adjuster/Cargo.toml index 06c67225cf1b..90616b7313a7 100644 --- a/core/node/base_token_adjuster/Cargo.toml +++ b/core/node/base_token_adjuster/Cargo.toml @@ -11,9 +11,7 @@ categories.workspace = true [dependencies] -vise.workspace = true zksync_dal.workspace = true -zksync_utils.workspace = true zksync_config.workspace = true zksync_types.workspace = true bigdecimal.workspace = true diff --git a/core/node/base_token_adjuster/README.md b/core/node/base_token_adjuster/README.md index 18baeab97e7c..3444f06906c5 100644 --- a/core/node/base_token_adjuster/README.md +++ b/core/node/base_token_adjuster/README.md @@ -1,11 +1,13 @@ # Base Token Adjuster -This crate contains an implementation of +This crate contains all the logic to handle ZK Chain with custom base tokens. It is used by other node layers to adjust +the fees to be denominated in the chain's base token. ## Overview -The Base Token Adjuster has 3 roles: +The Base Token Adjuster: -1. bla -2. bli -3. blue +- Connect with external APIs to get the current price of the base token. +- Persist the price of the base token in the database. +- Upon request, adjust the fees to be denominated in the base token. +- Upon certain configured threshold, update the L1 ETH<->BaseToken conversion ratio. diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index a64d959a648f..4ecfcaff0b1a 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -8,7 +8,8 @@ use anyhow::Context as _; use async_trait::async_trait; use bigdecimal::{BigDecimal, ToPrimitive}; use chrono::Utc; -use tokio::sync::watch; +use rand::Rng; +use tokio::{sync::watch, time::sleep}; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ @@ -25,7 +26,7 @@ pub trait BaseTokenAdjuster: Debug + Send + Sync { fn get_base_token(&self) -> &str; } -#[derive(Debug)] +#[derive(Debug, Clone)] /// BaseTokenAdjuster implementation for the main node (not the External Node). TODO (PE-137): impl APIBaseTokenAdjuster pub struct MainNodeBaseTokenAdjuster { pool: ConnectionPool, @@ -112,6 +113,47 @@ impl MainNodeBaseTokenAdjuster { Ok(()) } + async fn retry_get_latest_price(&self) -> anyhow::Result { + let mut retry_delay = 1; // seconds + let max_retries = 5; + let mut attempts = 1; + + loop { + let mut conn = self + .pool + .connection_tagged("base_token_adjuster") + .await + .expect("Failed to obtain connection to the database"); + + let result = conn.base_token_dal().get_latest_price().await; + + drop(conn); + + if let Ok(last_storage_price) = result { + return Ok(last_storage_price + .base_token_price + .div(&last_storage_price.eth_price)); + } else { + if attempts >= max_retries { + break; + } + let sleep_duration = Duration::from_secs(retry_delay) + .mul_f32(rand::thread_rng().gen_range(0.8..1.2)); + tracing::warn!( + "Attempt {}/{} failed to get latest base token price, retrying in {} seconds...", + attempts, max_retries, sleep_duration.as_secs() + ); + sleep(sleep_duration).await; + retry_delay *= 2; + attempts += 1; + } + } + anyhow::bail!( + "Failed to get latest base token price after {} attempts", + max_retries + ); + } + // Sleep for the remaining duration of the polling period async fn sleep_until_next_fetch(&self, start_time: Instant) { let elapsed_time = start_time.elapsed(); @@ -129,33 +171,22 @@ impl MainNodeBaseTokenAdjuster { impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { // TODO (PE-129): Implement latest ratio usability logic. async fn maybe_convert_to_base_token(&self, params: FeeParams) -> anyhow::Result { - let mut conn = self - .pool - .connection_tagged("base_token_adjuster") - .await - .expect("Failed to obtain connection to the database"); + let base_token = self.get_base_token(); - let last_storage_price = conn - .base_token_dal() - .get_latest_price() - .await - .expect("Failed to get latest base token price"); - drop(conn); + if base_token == "ETH" { + return Ok(params); + } - let last_ratio = last_storage_price - .base_token_price - .div(&last_storage_price.eth_price); + // Retries are necessary for the initial setup, where prices may not yet be persisted. + let latest_ratio = self.retry_get_latest_price().await?; - let base_token = self.get_base_token(); - match base_token { - "ETH" => Ok(params), - _ => { - if let FeeParams::V2(params_v2) = params { - Ok(FeeParams::V2(convert_to_base_token(params_v2, last_ratio))) - } else { - panic!("Custom base token is not supported for V1 fee model") - } - } + if let FeeParams::V2(params_v2) = params { + Ok(FeeParams::V2(convert_to_base_token( + params_v2, + latest_ratio, + ))) + } else { + panic!("Custom base token is not supported for V1 fee model") } } @@ -227,6 +258,15 @@ impl MockBaseTokenAdjuster { } } +impl Default for MockBaseTokenAdjuster { + fn default() -> Self { + Self { + last_ratio: BigDecimal::from(1), + base_token: "ETH".to_string(), + } + } +} + #[async_trait] impl BaseTokenAdjuster for MockBaseTokenAdjuster { async fn maybe_convert_to_base_token(&self, params: FeeParams) -> anyhow::Result { diff --git a/core/node/state_keeper/src/io/tests/tester.rs b/core/node/state_keeper/src/io/tests/tester.rs index d1d24d7e5fcc..da4b5ccba748 100644 --- a/core/node/state_keeper/src/io/tests/tester.rs +++ b/core/node/state_keeper/src/io/tests/tester.rs @@ -3,7 +3,7 @@ use std::{slice, sync::Arc, time::Duration}; use multivm::vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT; -use zksync_base_token_adjuster::BaseTokenAdjuster; +use zksync_base_token_adjuster::MockBaseTokenAdjuster; use zksync_config::{ configs::{chain::StateKeeperConfig, eth_sender::PubdataSendingMode, wallets::Wallets}, GasAdjusterConfig, @@ -17,7 +17,6 @@ use zksync_node_test_utils::{ create_l1_batch, create_l2_block, create_l2_transaction, execute_l2_transaction, }; use zksync_types::{ - base_token_price::BaseTokenPrice, block::L2BlockHeader, commitment::L1BatchCommitmentMode, fee::TransactionExecutionMetrics, @@ -80,9 +79,10 @@ impl Tester { pub(super) async fn create_batch_fee_input_provider(&self) -> MainNodeFeeInputProvider { let gas_adjuster = Arc::new(self.create_gas_adjuster().await); + MainNodeFeeInputProvider::new( gas_adjuster, - Arc::new(MockBaseTokenAdjuster {}), + Arc::new(MockBaseTokenAdjuster::default()), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), @@ -101,7 +101,7 @@ impl Tester { let gas_adjuster = Arc::new(self.create_gas_adjuster().await); let batch_fee_input_provider = MainNodeFeeInputProvider::new( gas_adjuster, - Arc::new(MockBaseTokenAdjuster {}), + Arc::new(MockBaseTokenAdjuster::default()), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), @@ -232,11 +232,3 @@ impl Tester { tx } } - -#[derive(Debug)] -struct MockBaseTokenAdjuster {} -impl BaseTokenAdjuster for MockBaseTokenAdjuster { - fn maybe_convert_to_base_token(&self) -> Option { - None - } -} From 34f72528b7c1af27e91cb0a4d73e1c451cd81602 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 25 Jun 2024 01:56:12 +0300 Subject: [PATCH 23/48] cleanup: trait, config, running locally --- core/bin/zksync_server/src/main.rs | 2 +- core/bin/zksync_server/src/node_builder.rs | 6 +- .../config/src/configs/base_token_adjuster.rs | 4 -- core/lib/config/src/configs/contracts.rs | 1 + .../src/base_token_adjuster.rs | 2 - .../proto/config/base_token_adjuster.proto | 1 - .../src/base_token_adjuster.rs | 72 ++++++++----------- .../src/l1_gas_price/main_node_fetcher.rs | 7 -- core/node/fee_model/src/lib.rs | 38 +--------- .../layers/base_token_adjuster.rs | 18 +++-- .../src/implementations/layers/l1_gas.rs | 11 ++- etc/env/base/chain.toml | 4 +- etc/env/base/rust.toml | 1 + prover/prover_dal/sqlx-data.json | 3 - 14 files changed, 67 insertions(+), 103 deletions(-) delete mode 100644 prover/prover_dal/sqlx-data.json diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index 3914d34c362d..b9b58e0a6017 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -48,7 +48,7 @@ struct Cli { /// Comma-separated list of components to launch. #[arg( long, - default_value = "api,tree,eth,state_keeper,housekeeper,tee_verifier_input_producer,commitment_generator" + default_value = "api,tree,eth,state_keeper,housekeeper,tee_verifier_input_producer,commitment_generator,base_token_adjuster" )] components: ComponentsToRun, /// Path to the yaml config. If set, it will be used instead of env vars. diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 5f6a1051b26f..db4ec531e113 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -153,6 +153,7 @@ impl MainNodeBuilder { state_keeper_config, try_load_config!(eth_sender_config.sender).pubdata_sending_mode, base_token_adjuster_config, + self.contracts_config.base_token_addr, ); self.node.add_layer(sequencer_l1_gas_layer); Ok(self) @@ -449,7 +450,10 @@ impl MainNodeBuilder { fn add_base_token_adjuster_layer(mut self) -> anyhow::Result { let config = try_load_config!(self.configs.base_token_adjuster); - self.node.add_layer(BaseTokenAdjusterLayer::new(config)); + self.node.add_layer(BaseTokenAdjusterLayer::new( + self.contracts_config.base_token_addr, + config, + )); Ok(self) } diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 2a6b3851b373..8507d0e52a6d 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -9,16 +9,12 @@ pub const DEFAULT_INTERVAL_MS: u64 = 30_000; pub struct BaseTokenAdjusterConfig { /// How often to fetch external APIs for a new ETH<->Base-Token price. pub price_polling_interval_ms: Option, - - /// Base token symbol. If none, an assumption of ETH is made. - pub base_token: Option, } impl BaseTokenAdjusterConfig { pub fn for_tests() -> Self { Self { price_polling_interval_ms: Some(DEFAULT_INTERVAL_MS), - base_token: Option::from("ETH".to_string()), } } diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index f9bfcc7696b1..7ccd702bca27 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -37,6 +37,7 @@ pub struct ContractsConfig { pub l2_testnet_paymaster_addr: Option
, pub l1_multicall3_addr: Address, pub ecosystem_contracts: Option, + // Used for the RPC API and by the BaseTokenAdjuster. If not set, ETH is the chain's base token. pub base_token_addr: Option
, } diff --git a/core/lib/protobuf_config/src/base_token_adjuster.rs b/core/lib/protobuf_config/src/base_token_adjuster.rs index c3cd81089498..f19f36a84a4d 100644 --- a/core/lib/protobuf_config/src/base_token_adjuster.rs +++ b/core/lib/protobuf_config/src/base_token_adjuster.rs @@ -9,14 +9,12 @@ impl ProtoRepr for proto::BaseTokenAdjuster { fn read(&self) -> anyhow::Result { Ok(configs::base_token_adjuster::BaseTokenAdjusterConfig { price_polling_interval_ms: self.price_polling_interval_ms, - base_token: self.base_token.clone(), }) } fn build(this: &Self::Type) -> Self { Self { price_polling_interval_ms: this.price_polling_interval_ms, - base_token: this.base_token.clone(), } } } diff --git a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto index f3f75c4b7b26..67e97dd14cda 100644 --- a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto +++ b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto @@ -4,5 +4,4 @@ package zksync.config.base_token_adjuster; message BaseTokenAdjuster { optional uint64 price_polling_interval_ms = 1; - optional string base_token = 2; } diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 4ecfcaff0b1a..26a9b317cac0 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -15,27 +15,34 @@ use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ base_token_price::BaseTokenAPIPrice, fee_model::{FeeModelConfigV2, FeeParams, FeeParamsV2}, + Address, }; #[async_trait] pub trait BaseTokenAdjuster: Debug + Send + Sync { /// Returns the last ratio cached by the adjuster and ensure it's still usable. - async fn maybe_convert_to_base_token(&self, params: FeeParams) -> anyhow::Result; - - /// Return configured symbol of the base token. - fn get_base_token(&self) -> &str; + async fn convert(&self, params: FeeParams) -> anyhow::Result; } #[derive(Debug, Clone)] /// BaseTokenAdjuster implementation for the main node (not the External Node). TODO (PE-137): impl APIBaseTokenAdjuster pub struct MainNodeBaseTokenAdjuster { pool: ConnectionPool, + base_token_l1_address: Option
, config: BaseTokenAdjusterConfig, } impl MainNodeBaseTokenAdjuster { - pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { - Self { pool, config } + pub fn new( + pool: ConnectionPool, + config: BaseTokenAdjusterConfig, + base_token_l1_address: Option
, + ) -> Self { + Self { + pool, + config, + base_token_l1_address, + } } /// Main loop for the base token adjuster. @@ -51,14 +58,14 @@ impl MainNodeBaseTokenAdjuster { let start_time = Instant::now(); match self.fetch_new_ratio().await { - Ok(new_ratio) => match self.persist_ratio(&new_ratio, &pool).await { - Ok(id) => { - if let Err(err) = self.maybe_update_l1(&new_ratio, id).await { - tracing::error!("Error updating L1 ratio: {:?}", err); + Ok(new_ratio) => { + match self.persist_ratio(&new_ratio, &pool).await { + Ok(_) => { + // TODO(PE-128): Update L1 ratio } + Err(err) => tracing::error!("Error persisting ratio: {:?}", err), } - Err(err) => tracing::error!("Error persisting ratio: {:?}", err), - }, + } Err(err) => tracing::error!("Error fetching new ratio: {:?}", err), } @@ -104,15 +111,6 @@ impl MainNodeBaseTokenAdjuster { Ok(id) } - // TODO (PE-128): Complete L1 update flow. - async fn maybe_update_l1( - &self, - _new_ratio: &BaseTokenAPIPrice, - _id: usize, - ) -> anyhow::Result<()> { - Ok(()) - } - async fn retry_get_latest_price(&self) -> anyhow::Result { let mut retry_delay = 1; // seconds let max_retries = 5; @@ -165,21 +163,27 @@ impl MainNodeBaseTokenAdjuster { tokio::time::sleep(sleep_duration).await; } + + /// If no base token L1 address is set then the base token is ETH. + fn is_base_token_eth(&self) -> bool { + self.base_token_l1_address.is_none() + } } #[async_trait] impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { - // TODO (PE-129): Implement latest ratio usability logic. - async fn maybe_convert_to_base_token(&self, params: FeeParams) -> anyhow::Result { - let base_token = self.get_base_token(); + async fn convert(&self, params: FeeParams) -> anyhow::Result { + let is_eth = self.is_base_token_eth(); - if base_token == "ETH" { + if is_eth { return Ok(params); } // Retries are necessary for the initial setup, where prices may not yet be persisted. let latest_ratio = self.retry_get_latest_price().await?; + // TODO(PE-129): Implement latest ratio usability logic. + if let FeeParams::V2(params_v2) = params { Ok(FeeParams::V2(convert_to_base_token( params_v2, @@ -189,14 +193,6 @@ impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { panic!("Custom base token is not supported for V1 fee model") } } - - /// Return configured symbol of the base token. If not configured, return "ETH". - fn get_base_token(&self) -> &str { - match &self.config.base_token { - Some(base_token) => base_token.as_str(), - None => "ETH", - } - } } /// Converts the fee parameters to the base token using the latest ratio fetched from the DB. @@ -269,10 +265,8 @@ impl Default for MockBaseTokenAdjuster { #[async_trait] impl BaseTokenAdjuster for MockBaseTokenAdjuster { - async fn maybe_convert_to_base_token(&self, params: FeeParams) -> anyhow::Result { - // LOG THE PARAMS - tracing::info!("Params: {:?}", params); - match self.get_base_token() { + async fn convert(&self, params: FeeParams) -> anyhow::Result { + match self.base_token.as_str() { "ETH" => Ok(params), _ => { if let FeeParams::V2(params_v2) = params { @@ -286,10 +280,6 @@ impl BaseTokenAdjuster for MockBaseTokenAdjuster { } } } - - fn get_base_token(&self) -> &str { - &self.base_token - } } #[cfg(test)] diff --git a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs index 0e93dfeae573..dd659e984d41 100644 --- a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs +++ b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs @@ -76,11 +76,4 @@ impl BatchFeeModelInputProvider for MainNodeFeeParamsFetcher { async fn get_fee_model_params(&self) -> anyhow::Result { Ok(*self.main_node_fee_params.read().unwrap()) } - - async fn maybe_convert_params_to_base_token( - &self, - _params: FeeParams, - ) -> anyhow::Result { - Ok(_params) - } } diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index f351f7caa30d..7a0fa2f1adbb 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -45,17 +45,7 @@ pub trait BatchFeeModelInputProvider: fmt::Debug + 'static + Send + Sync { } /// Returns the fee model parameters using the denomination of the base token used (WEI for ETH). - async fn get_fee_model_params(&self) -> anyhow::Result { - let unconverted_params = self.get_fee_model_params().await?; - self.maybe_convert_params_to_base_token(unconverted_params) - .await - } - - /// Converts the fee model parameters to the base token denomination. - async fn maybe_convert_params_to_base_token( - &self, - params: FeeParams, - ) -> anyhow::Result; + async fn get_fee_model_params(&self) -> anyhow::Result; } impl dyn BatchFeeModelInputProvider { @@ -90,16 +80,7 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { }), }; - self.maybe_convert_params_to_base_token(params).await - } - - async fn maybe_convert_params_to_base_token( - &self, - params: FeeParams, - ) -> anyhow::Result { - self.base_token_adjuster - .maybe_convert_to_base_token(params) - .await + self.base_token_adjuster.convert(params).await } } @@ -165,13 +146,7 @@ impl BatchFeeModelInputProvider for ApiFeeInputProvider { /// Returns the fee model parameters. async fn get_fee_model_params(&self) -> anyhow::Result { self.inner.get_fee_model_params().await - } - - async fn maybe_convert_params_to_base_token( - &self, - params: FeeParams, - ) -> anyhow::Result { - self.inner.maybe_convert_params_to_base_token(params).await + // TODO: QQQQ: convert for API as well } } @@ -272,13 +247,6 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { async fn get_fee_model_params(&self) -> anyhow::Result { Ok(self.0) } - - async fn maybe_convert_params_to_base_token( - &self, - params: FeeParams, - ) -> anyhow::Result { - Ok(params) - } } #[cfg(test)] diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index bf1fc18a7acd..ca1c61a04dd1 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -1,6 +1,7 @@ use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use zksync_dal::Core; use zksync_db_connection::connection_pool::ConnectionPool; +use zksync_types::Address; use crate::{ implementations::resources::pools::{MasterPool, PoolResource}, @@ -12,12 +13,16 @@ use crate::{ /// A layer that wires the Base Token Adjuster task. #[derive(Debug)] pub struct BaseTokenAdjusterLayer { + base_token_l1_address: Option
, config: BaseTokenAdjusterConfig, } impl BaseTokenAdjusterLayer { - pub fn new(config: BaseTokenAdjusterConfig) -> Self { - Self { config } + pub fn new(base_token_l1_address: Option
, config: BaseTokenAdjusterConfig) -> Self { + Self { + base_token_l1_address, + config, + } } } @@ -33,6 +38,7 @@ impl WiringLayer for BaseTokenAdjusterLayer { context.add_task(Box::new(BaseTokenAdjusterTask { main_pool: master_pool, + base_token_l1_address: self.base_token_l1_address, config: self.config, })); @@ -43,6 +49,7 @@ impl WiringLayer for BaseTokenAdjusterLayer { #[derive(Debug)] struct BaseTokenAdjusterTask { main_pool: ConnectionPool, + base_token_l1_address: Option
, config: BaseTokenAdjusterConfig, } @@ -53,8 +60,11 @@ impl Task for BaseTokenAdjusterTask { } async fn run(self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { - let mut adjuster = - zksync_base_token_adjuster::MainNodeBaseTokenAdjuster::new(self.main_pool, self.config); + let mut adjuster = zksync_base_token_adjuster::MainNodeBaseTokenAdjuster::new( + self.main_pool, + self.config, + self.base_token_l1_address, + ); adjuster.run(stop_receiver.0).await } diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index 2dd28475c1eb..ddd446b3121a 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -8,6 +8,7 @@ use zksync_config::{ }; use zksync_node_fee_model::{l1_gas_price::GasAdjuster, MainNodeFeeInputProvider}; use zksync_types::fee_model::FeeModelConfig; +use zksync_types::Address; use crate::{ implementations::resources::{ @@ -28,6 +29,7 @@ pub struct SequencerL1GasLayer { pubdata_sending_mode: PubdataSendingMode, state_keeper_config: StateKeeperConfig, base_token_adjuster_config: BaseTokenAdjusterConfig, + base_token_l1_address: Option
, } impl SequencerL1GasLayer { @@ -37,6 +39,7 @@ impl SequencerL1GasLayer { state_keeper_config: StateKeeperConfig, pubdata_sending_mode: PubdataSendingMode, base_token_adjuster_config: BaseTokenAdjusterConfig, + base_token_l1_address: Option
, ) -> Self { Self { gas_adjuster_config, @@ -44,6 +47,7 @@ impl SequencerL1GasLayer { pubdata_sending_mode, state_keeper_config, base_token_adjuster_config, + base_token_l1_address, } } } @@ -69,8 +73,11 @@ impl WiringLayer for SequencerL1GasLayer { let pool_resource = context.get_resource::>().await?; let replica_pool = pool_resource.get().await?; - let base_token_adjuster = - MainNodeBaseTokenAdjuster::new(replica_pool.clone(), self.base_token_adjuster_config); + let base_token_adjuster = MainNodeBaseTokenAdjuster::new( + replica_pool.clone(), + self.base_token_adjuster_config, + self.base_token_l1_address, + ); let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( gas_adjuster.clone(), diff --git a/etc/env/base/chain.toml b/etc/env/base/chain.toml index 88a4c71bbb9b..0cb8213119b3 100644 --- a/etc/env/base/chain.toml +++ b/etc/env/base/chain.toml @@ -82,9 +82,9 @@ max_pubdata_per_batch = 100000 # Also, the fair L2 gas price is expected to only include the proving/computation price for the operator and not the costs that come from # processing the batch on L1. # - `V2`, the second model that was used in ZKsync Era. There the pubdata price might be independent from the L1 gas price. Also, -# The fair L2 gas price is expected to both the proving/computation price for the operator and the costs that come from +# The fair L2 gas price is expected to be both the proving/computation price for the operator and the costs that come from # processing the batch on L1. -fee_model_version = "V1" +fee_model_version = "V2" # Max number of computational gas that validation step is allowed to take. validation_computational_gas_limit = 300000 diff --git a/etc/env/base/rust.toml b/etc/env/base/rust.toml index ee4a69721cd3..950e78a155a0 100644 --- a/etc/env/base/rust.toml +++ b/etc/env/base/rust.toml @@ -57,6 +57,7 @@ zksync_health_check=debug,\ zksync_proof_fri_compressor=info,\ vise_exporter=debug,\ snapshots_creator=debug,\ +zksync_base_token_adjuster=debug,\ """ # `RUST_BACKTRACE` variable diff --git a/prover/prover_dal/sqlx-data.json b/prover/prover_dal/sqlx-data.json deleted file mode 100644 index 95c8c858baaf..000000000000 --- a/prover/prover_dal/sqlx-data.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "db": "PostgreSQL" -} \ No newline at end of file From 686ace0571ab8f2689d1aeba924959005735731f Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 25 Jun 2024 02:16:13 +0300 Subject: [PATCH 24/48] fmt and todo --- core/node/fee_model/src/lib.rs | 2 +- core/node/node_framework/src/implementations/layers/l1_gas.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 7a0fa2f1adbb..22dfcaf8c9c6 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -146,7 +146,7 @@ impl BatchFeeModelInputProvider for ApiFeeInputProvider { /// Returns the fee model parameters. async fn get_fee_model_params(&self) -> anyhow::Result { self.inner.get_fee_model_params().await - // TODO: QQQQ: convert for API as well + // TODO(PE-137): impl APIBaseTokenAdjuster and use it in the ApiFeeInputProvider. } } diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index ddd446b3121a..3f4ddc7e1d56 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -7,8 +7,7 @@ use zksync_config::{ BaseTokenAdjusterConfig, GasAdjusterConfig, GenesisConfig, }; use zksync_node_fee_model::{l1_gas_price::GasAdjuster, MainNodeFeeInputProvider}; -use zksync_types::fee_model::FeeModelConfig; -use zksync_types::Address; +use zksync_types::{fee_model::FeeModelConfig, Address}; use crate::{ implementations::resources::{ From 554c9012b0d94c360b392bac12cc7ff2d462761d Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 26 Jun 2024 01:43:53 +0300 Subject: [PATCH 25/48] base token cfg non optional. nume+deno again. conversion in params. unit tests --- Cargo.lock | 2 +- core/bin/external_node/src/config/mod.rs | 2 +- core/lib/config/src/configs/contracts.rs | 7 +- ...6263bca6b63f6793b4e92e179a48491282d4c.json | 24 -- ...ba6b5dc624747adb73c9f94afe84e61f7513d.json | 56 ---- ...121747_add_base_token_price_table.down.sql | 1 - ...121747_add_base_token_ratio_table.down.sql | 1 + ...1121747_add_base_token_ratio_table.up.sql} | 6 +- core/lib/dal/src/base_token_dal.rs | 29 +- core/lib/dal/src/models/mod.rs | 2 +- .../src/models/storage_base_token_price.rs | 27 -- .../src/models/storage_base_token_ratio.rs | 27 ++ core/lib/env_config/src/contracts.rs | 2 +- core/lib/external_price_api/src/lib.rs | 4 +- core/lib/protobuf_config/src/contracts.rs | 9 +- core/lib/types/Cargo.toml | 1 + core/lib/types/src/base_token_price.rs | 19 +- core/lib/types/src/fee_model.rs | 74 ++++- .../api_server/src/web3/namespaces/zks.rs | 5 +- core/node/api_server/src/web3/state.rs | 2 +- core/node/base_token_adjuster/Cargo.toml | 1 - .../src/base_token_adjuster.rs | 285 +++--------------- core/node/fee_model/src/lib.rs | 266 ++++++++++------ .../node/node_framework/examples/main_node.rs | 2 + .../layers/base_token_adjuster.rs | 6 +- .../src/implementations/layers/l1_gas.rs | 4 +- prover/Cargo.lock | 1 + 27 files changed, 360 insertions(+), 505 deletions(-) delete mode 100644 core/lib/dal/.sqlx/query-4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c.json delete mode 100644 core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json delete mode 100644 core/lib/dal/migrations/20240611121747_add_base_token_price_table.down.sql create mode 100644 core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.down.sql rename core/lib/dal/migrations/{20240611121747_add_base_token_price_table.up.sql => 20240611121747_add_base_token_ratio_table.up.sql} (64%) delete mode 100644 core/lib/dal/src/models/storage_base_token_price.rs create mode 100644 core/lib/dal/src/models/storage_base_token_ratio.rs diff --git a/Cargo.lock b/Cargo.lock index a260aae0edab..006805b271a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7792,7 +7792,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "bigdecimal", "chrono", "rand 0.8.5", "tokio", @@ -9235,6 +9234,7 @@ dependencies = [ "strum", "thiserror", "tokio", + "tracing", "zksync_basic_types", "zksync_config", "zksync_contracts", diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index b47ae3f8886e..d4da45cc00a6 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -940,7 +940,7 @@ impl From<&ExternalNodeConfig> for InternalApiConfig { l2_testnet_paymaster_addr: config.remote.l2_testnet_paymaster_addr, req_entities_limit: config.optional.req_entities_limit, fee_history_limit: config.optional.fee_history_limit, - base_token_address: Some(config.remote.base_token_addr), + base_token_address: config.remote.base_token_addr, filters_disabled: config.optional.filters_disabled, dummy_verifier: config.remote.dummy_verifier, l1_batch_commit_data_generator_mode: config.remote.l1_batch_commit_data_generator_mode, diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index 7ccd702bca27..2497eed947e0 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -37,8 +37,9 @@ pub struct ContractsConfig { pub l2_testnet_paymaster_addr: Option
, pub l1_multicall3_addr: Address, pub ecosystem_contracts: Option, - // Used for the RPC API and by the BaseTokenAdjuster. If not set, ETH is the chain's base token. - pub base_token_addr: Option
, + // Used for the RPC API and by the BaseTokenAdjuster. If not set, ETH is the chain's base token + // and the "ETH address" is SHARED_BRIDGE_ETHER_TOKEN_ADDRESS. + pub base_token_addr: Address, } impl ContractsConfig { @@ -57,7 +58,7 @@ impl ContractsConfig { l2_testnet_paymaster_addr: Some(Address::repeat_byte(0x11)), l1_multicall3_addr: Address::repeat_byte(0x12), governance_addr: Address::repeat_byte(0x13), - base_token_addr: Some(Address::repeat_byte(0x14)), + base_token_addr: Address::repeat_byte(0x14), ecosystem_contracts: Some(EcosystemContracts::for_tests()), } } diff --git a/core/lib/dal/.sqlx/query-4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c.json b/core/lib/dal/.sqlx/query-4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c.json deleted file mode 100644 index 894812a14df8..000000000000 --- a/core/lib/dal/.sqlx/query-4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO\n base_token_prices (base_token_price, eth_price, ratio_timestamp, created_at, updated_at)\n VALUES\n ($1, $2, $3, NOW(), NOW())\n RETURNING\n id\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Numeric", - "Numeric", - "Timestamp" - ] - }, - "nullable": [ - false - ] - }, - "hash": "4f4a8489cb3ee9337fce8c5c9756263bca6b63f6793b4e92e179a48491282d4c" -} diff --git a/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json b/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json deleted file mode 100644 index f235bbf16093..000000000000 --- a/core/lib/dal/.sqlx/query-fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n *\n FROM\n base_token_prices\n ORDER BY\n created_at DESC\n LIMIT\n 1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4" - }, - { - "ordinal": 1, - "name": "created_at", - "type_info": "Timestamp" - }, - { - "ordinal": 2, - "name": "updated_at", - "type_info": "Timestamp" - }, - { - "ordinal": 3, - "name": "ratio_timestamp", - "type_info": "Timestamp" - }, - { - "ordinal": 4, - "name": "base_token_price", - "type_info": "Numeric" - }, - { - "ordinal": 5, - "name": "eth_price", - "type_info": "Numeric" - }, - { - "ordinal": 6, - "name": "used_in_l1", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "fc15573c6564779792e7ab69f66ba6b5dc624747adb73c9f94afe84e61f7513d" -} diff --git a/core/lib/dal/migrations/20240611121747_add_base_token_price_table.down.sql b/core/lib/dal/migrations/20240611121747_add_base_token_price_table.down.sql deleted file mode 100644 index db15b29ecaa3..000000000000 --- a/core/lib/dal/migrations/20240611121747_add_base_token_price_table.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS base_token_price; diff --git a/core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.down.sql b/core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.down.sql new file mode 100644 index 000000000000..e64cb3c7c408 --- /dev/null +++ b/core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS base_token_ratios; diff --git a/core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql b/core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.up.sql similarity index 64% rename from core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql rename to core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.up.sql index ba9a934794d6..ce52209e073f 100644 --- a/core/lib/dal/migrations/20240611121747_add_base_token_price_table.up.sql +++ b/core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.up.sql @@ -1,11 +1,11 @@ -CREATE TABLE base_token_prices ( +CREATE TABLE base_token_ratios ( id SERIAL PRIMARY KEY, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, ratio_timestamp TIMESTAMP NOT NULL, - base_token_price NUMERIC NOT NULL, - eth_price NUMERIC NOT NULL, + numerator NUMERIC NOT NULL, + denominator NUMERIC NOT NULL, used_in_l1 BOOLEAN NOT NULL DEFAULT FALSE ); diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index 3b812a54e666..a069afcaf429 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -1,7 +1,8 @@ -use bigdecimal::BigDecimal; +use bigdecimal::{BigDecimal, FromPrimitive}; use zksync_db_connection::{connection::Connection, error::DalResult, instrument::InstrumentExt}; +use zksync_types::base_token_price::BaseTokenRatio; -use crate::{models::storage_base_token_price::StorageBaseTokenPrice, Core}; +use crate::{models::storage_base_token_ratio::StorageBaseTokenRatio, Core}; #[derive(Debug)] pub struct BaseTokenDal<'a, 'c> { @@ -9,26 +10,26 @@ pub struct BaseTokenDal<'a, 'c> { } impl BaseTokenDal<'_, '_> { - pub async fn insert_token_price( + pub async fn insert_token_ratio( &mut self, - base_token_price: &BigDecimal, - eth_price: &BigDecimal, + numerator: u64, + denominator: u64, ratio_timestamp: &chrono::NaiveDateTime, ) -> DalResult { let row = sqlx::query!( r#" INSERT INTO - base_token_prices (base_token_price, eth_price, ratio_timestamp, created_at, updated_at) + base_token_ratios (numerator, denominator, ratio_timestamp, created_at, updated_at) VALUES ($1, $2, $3, NOW(), NOW()) RETURNING id "#, - base_token_price, - eth_price, + BigDecimal::from_u64(numerator), + BigDecimal::from_u64(denominator), ratio_timestamp, ) - .instrument("insert_base_token_price") + .instrument("insert_token_ratio") .fetch_one(self.storage) .await?; @@ -37,23 +38,23 @@ impl BaseTokenDal<'_, '_> { // TODO (PE-128): pub async fn mark_l1_update() - pub async fn get_latest_price(&mut self) -> DalResult { + pub async fn get_latest_ratio(&mut self) -> DalResult { let row = sqlx::query_as!( - StorageBaseTokenPrice, + StorageBaseTokenRatio, r#" SELECT * FROM - base_token_prices + base_token_ratios ORDER BY created_at DESC LIMIT 1 "#, ) - .instrument("get_latest_base_token_price") + .instrument("get_latest_ratio") .fetch_one(self.storage) .await?; - Ok(row) + Ok(row.into()) } } diff --git a/core/lib/dal/src/models/mod.rs b/core/lib/dal/src/models/mod.rs index 97661aa66ba9..b26cdc1d2e37 100644 --- a/core/lib/dal/src/models/mod.rs +++ b/core/lib/dal/src/models/mod.rs @@ -3,7 +3,7 @@ use anyhow::Context as _; use zksync_db_connection::error::SqlxContext; use zksync_types::{ProtocolVersionId, H160, H256}; -pub mod storage_base_token_price; +pub mod storage_base_token_ratio; pub mod storage_eth_tx; pub mod storage_event; pub mod storage_log; diff --git a/core/lib/dal/src/models/storage_base_token_price.rs b/core/lib/dal/src/models/storage_base_token_price.rs deleted file mode 100644 index f7d8634f90e4..000000000000 --- a/core/lib/dal/src/models/storage_base_token_price.rs +++ /dev/null @@ -1,27 +0,0 @@ -use bigdecimal::BigDecimal; -use chrono::NaiveDateTime; -use zksync_types::base_token_price::BaseTokenPrice; - -/// Represents a row in the `storage_base_token_price` table. -#[derive(Debug, Clone)] -pub struct StorageBaseTokenPrice { - pub id: i64, - pub created_at: NaiveDateTime, - pub updated_at: NaiveDateTime, - pub ratio_timestamp: NaiveDateTime, - pub base_token_price: BigDecimal, - pub eth_price: BigDecimal, - pub used_in_l1: bool, -} - -impl From for BaseTokenPrice { - fn from(row: StorageBaseTokenPrice) -> BaseTokenPrice { - BaseTokenPrice { - id: row.id, - ratio_timestamp: row.ratio_timestamp.and_utc(), - base_token_price: row.base_token_price, - eth_price: row.eth_price, - used_in_l1: row.used_in_l1, - } - } -} diff --git a/core/lib/dal/src/models/storage_base_token_ratio.rs b/core/lib/dal/src/models/storage_base_token_ratio.rs new file mode 100644 index 000000000000..b70e3136d8b6 --- /dev/null +++ b/core/lib/dal/src/models/storage_base_token_ratio.rs @@ -0,0 +1,27 @@ +use bigdecimal::{BigDecimal, ToPrimitive}; +use chrono::NaiveDateTime; +use zksync_types::base_token_price::BaseTokenRatio; + +/// Represents a row in the `storage_base_token_price` table. +#[derive(Debug, Clone)] +pub struct StorageBaseTokenRatio { + pub id: i64, + pub created_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub ratio_timestamp: NaiveDateTime, + pub numerator: BigDecimal, + pub denominator: BigDecimal, + pub used_in_l1: bool, +} + +impl From for BaseTokenRatio { + fn from(row: StorageBaseTokenRatio) -> BaseTokenRatio { + BaseTokenRatio { + id: row.id, + ratio_timestamp: row.ratio_timestamp.and_utc(), + numerator: row.numerator.to_u64().expect("numerator is not u64"), + denominator: row.denominator.to_u64().expect("denominator is not u64"), + used_in_l1: row.used_in_l1, + } + } +} diff --git a/core/lib/env_config/src/contracts.rs b/core/lib/env_config/src/contracts.rs index ae5eb6f30c92..c56f32017d22 100644 --- a/core/lib/env_config/src/contracts.rs +++ b/core/lib/env_config/src/contracts.rs @@ -70,7 +70,7 @@ mod tests { state_transition_proxy_addr: addr("0xd90f1c081c6117241624e97cb6147257c3cb2097"), transparent_proxy_admin_addr: addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5"), }), - base_token_addr: Some(SHARED_BRIDGE_ETHER_TOKEN_ADDRESS), + base_token_addr: SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, } } diff --git a/core/lib/external_price_api/src/lib.rs b/core/lib/external_price_api/src/lib.rs index 0f1675a6b7a6..6fa2574fdf86 100644 --- a/core/lib/external_price_api/src/lib.rs +++ b/core/lib/external_price_api/src/lib.rs @@ -1,11 +1,11 @@ use std::fmt; use async_trait::async_trait; -use zksync_types::{base_token_price::BaseTokenAPIPrice, Address}; +use zksync_types::{base_token_price::BaseTokenAPIRatio, Address}; /// Trait that defines the interface for a client connecting with an external API to get prices. #[async_trait] pub trait PriceAPIClient: Sync + Send + fmt::Debug { /// Returns the price for the input token address in $USD. - async fn fetch_price(&self, token_address: Address) -> anyhow::Result; + async fn fetch_price(&self, token_address: Address) -> anyhow::Result; } diff --git a/core/lib/protobuf_config/src/contracts.rs b/core/lib/protobuf_config/src/contracts.rs index ac1864b7a0bd..d891ba329004 100644 --- a/core/lib/protobuf_config/src/contracts.rs +++ b/core/lib/protobuf_config/src/contracts.rs @@ -95,11 +95,8 @@ impl ProtoRepr for proto::Contracts { l1_multicall3_addr: required(&l1.multicall3_addr) .and_then(|x| parse_h160(x)) .context("l1_multicall3_addr")?, - base_token_addr: l1 - .base_token_addr - .as_ref() - .map(|x| parse_h160(x)) - .transpose() + base_token_addr: required(&l1.base_token_addr) + .and_then(|x| parse_h160(x)) .context("base_token_addr")?, }) } @@ -131,7 +128,7 @@ impl ProtoRepr for proto::Contracts { validator_timelock_addr: Some(format!("{:?}", this.validator_timelock_addr)), default_upgrade_addr: Some(format!("{:?}", this.default_upgrade_addr)), multicall3_addr: Some(format!("{:?}", this.l1_multicall3_addr)), - base_token_addr: this.base_token_addr.map(|a| format!("{:?}", a)), + base_token_addr: Some(format!("{:?}", this.base_token_addr)), }), l2: Some(proto::L2 { testnet_paymaster_addr: this.l2_testnet_paymaster_addr.map(|a| format!("{:?}", a)), diff --git a/core/lib/types/Cargo.toml b/core/lib/types/Cargo.toml index 10e59639629c..673a0f35a26a 100644 --- a/core/lib/types/Cargo.toml +++ b/core/lib/types/Cargo.toml @@ -34,6 +34,7 @@ num_enum.workspace = true hex.workspace = true prost.workspace = true itertools.workspace = true +tracing.workspace = true # Crypto stuff secp256k1.workspace = true diff --git a/core/lib/types/src/base_token_price.rs b/core/lib/types/src/base_token_price.rs index 9c093d8a6fda..26c1a7f09c9e 100644 --- a/core/lib/types/src/base_token_price.rs +++ b/core/lib/types/src/base_token_price.rs @@ -1,21 +1,20 @@ -use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; -/// Represents the base token price at a given point in time. -#[derive(Debug, Clone, Default)] -pub struct BaseTokenPrice { +/// Represents the base token to ETH ratio at a given point in time. +#[derive(Debug, Clone)] +pub struct BaseTokenRatio { pub id: i64, pub ratio_timestamp: DateTime, - pub base_token_price: BigDecimal, - pub eth_price: BigDecimal, + pub numerator: u64, + pub denominator: u64, pub used_in_l1: bool, } /// Struct to represent API response containing denominator, numerator and optional timestamp. -#[derive(Debug, Default)] -pub struct BaseTokenAPIPrice { - pub base_token_price: BigDecimal, - pub eth_price: BigDecimal, +#[derive(Debug)] +pub struct BaseTokenAPIRatio { + pub numerator: u64, + pub denominator: u64, // Either the timestamp of the quote or the timestamp of the request. pub ratio_timestamp: DateTime, } diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 9c2cc4d2aaf8..69444c31dfc1 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -1,3 +1,4 @@ +use bigdecimal::{BigDecimal, ToPrimitive}; use serde::{Deserialize, Serialize}; use zksync_config::configs::chain::{FeeModelVersion, StateKeeperConfig}; use zksync_system_constants::L1_GAS_PER_PUBDATA_BYTE; @@ -236,9 +237,76 @@ pub struct FeeParamsV1 { #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct FeeParamsV2 { - pub config: FeeModelConfigV2, - pub l1_gas_price: u64, - pub l1_pubdata_price: u64, + config: FeeModelConfigV2, + l1_gas_price: u64, + l1_pubdata_price: u64, + conversion_ratio: BaseTokenConversionRatio, +} + +impl FeeParamsV2 { + pub fn new( + config: FeeModelConfigV2, + l1_gas_price: u64, + l1_pubdata_price: u64, + conversion_ratio: BaseTokenConversionRatio, + ) -> Self { + Self { + config, + l1_gas_price, + l1_pubdata_price, + conversion_ratio, + } + } + + /// Returns the fee model config with the minimal L2 gas price denominated in the chain's base token (WEI or equivalent). + pub fn config(&self) -> FeeModelConfigV2 { + FeeModelConfigV2 { + minimal_l2_gas_price: self.convert_to_base_token(self.config.minimal_l2_gas_price), + ..self.config + } + } + + /// Returns the l1 gas price denominated in the chain's base token (WEI or equivalent). + pub fn l1_gas_price(&self) -> u64 { + self.convert_to_base_token(self.l1_gas_price) + } + + /// Returns the l1 pubdata price denominated in the chain's base token (WEI or equivalent). + pub fn l1_pubdata_price(&self) -> u64 { + self.convert_to_base_token(self.l1_pubdata_price) + } + + /// Converts the fee param to the base token. + fn convert_to_base_token(&self, price_in_wei: u64) -> u64 { + let conversion_ratio = BigDecimal::from(self.conversion_ratio.numerator) + / BigDecimal::from(self.conversion_ratio.denominator); + let converted_price_bd = BigDecimal::from(price_in_wei) * conversion_ratio; + + // Match on the converted price to ensure it can be represented as a u64 + match converted_price_bd.to_u64() { + Some(converted_price) => converted_price, + None => { + if converted_price_bd > BigDecimal::from(u64::MAX) { + tracing::warn!( + "Conversion to base token price failed: converted price is too large: {}", + converted_price_bd + ); + } else { + tracing::error!( + "Conversion to base token price failed: converted price is not a valid u64: {}", + converted_price_bd + ); + } + u64::MAX + } + } + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct BaseTokenConversionRatio { + pub numerator: u64, + pub denominator: u64, } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] diff --git a/core/node/api_server/src/web3/namespaces/zks.rs b/core/node/api_server/src/web3/namespaces/zks.rs index b801241bdfc4..5db1f9320695 100644 --- a/core/node/api_server/src/web3/namespaces/zks.rs +++ b/core/node/api_server/src/web3/namespaces/zks.rs @@ -543,10 +543,7 @@ impl ZksNamespace { } pub fn get_base_token_l1_address_impl(&self) -> Result { - self.state - .api_config - .base_token_address - .ok_or(Web3Error::MethodNotImplemented) + Ok(self.state.api_config.base_token_address) } #[tracing::instrument(skip(self))] diff --git a/core/node/api_server/src/web3/state.rs b/core/node/api_server/src/web3/state.rs index 276e0b6755e7..f7a964f3cad1 100644 --- a/core/node/api_server/src/web3/state.rs +++ b/core/node/api_server/src/web3/state.rs @@ -108,7 +108,7 @@ pub struct InternalApiConfig { pub l2_testnet_paymaster_addr: Option
, pub req_entities_limit: usize, pub fee_history_limit: u64, - pub base_token_address: Option
, + pub base_token_address: Address, pub filters_disabled: bool, pub dummy_verifier: bool, pub l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, diff --git a/core/node/base_token_adjuster/Cargo.toml b/core/node/base_token_adjuster/Cargo.toml index 90616b7313a7..7e5c5bcaae43 100644 --- a/core/node/base_token_adjuster/Cargo.toml +++ b/core/node/base_token_adjuster/Cargo.toml @@ -14,7 +14,6 @@ categories.workspace = true zksync_dal.workspace = true zksync_config.workspace = true zksync_types.workspace = true -bigdecimal.workspace = true tokio = { workspace = true, features = ["time"] } anyhow.workspace = true diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 26a9b317cac0..58ae6e612a02 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -1,34 +1,31 @@ use std::{ fmt::Debug, - ops::Div, time::{Duration, Instant}, }; use anyhow::Context as _; use async_trait::async_trait; -use bigdecimal::{BigDecimal, ToPrimitive}; use chrono::Utc; use rand::Rng; use tokio::{sync::watch, time::sleep}; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ - base_token_price::BaseTokenAPIPrice, - fee_model::{FeeModelConfigV2, FeeParams, FeeParamsV2}, - Address, + base_token_price::BaseTokenAPIRatio, fee_model::BaseTokenConversionRatio, Address, + SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, }; #[async_trait] pub trait BaseTokenAdjuster: Debug + Send + Sync { /// Returns the last ratio cached by the adjuster and ensure it's still usable. - async fn convert(&self, params: FeeParams) -> anyhow::Result; + async fn get_conversion_ratio(&self) -> anyhow::Result; } #[derive(Debug, Clone)] /// BaseTokenAdjuster implementation for the main node (not the External Node). TODO (PE-137): impl APIBaseTokenAdjuster pub struct MainNodeBaseTokenAdjuster { pool: ConnectionPool, - base_token_l1_address: Option
, + base_token_l1_address: Address, config: BaseTokenAdjusterConfig, } @@ -36,7 +33,7 @@ impl MainNodeBaseTokenAdjuster { pub fn new( pool: ConnectionPool, config: BaseTokenAdjusterConfig, - base_token_l1_address: Option
, + base_token_l1_address: Address, ) -> Self { Self { pool, @@ -76,21 +73,19 @@ impl MainNodeBaseTokenAdjuster { // TODO (PE-135): Use real API client to fetch new ratio through self.PriceAPIClient & mock for tests. // For now, these hard coded values are also hard coded in the integration tests. - async fn fetch_new_ratio(&self) -> anyhow::Result { - let new_base_token_price = BigDecimal::from(1); - let new_eth_price = BigDecimal::from(100000); + async fn fetch_new_ratio(&self) -> anyhow::Result { let ratio_timestamp = Utc::now(); - Ok(BaseTokenAPIPrice { - base_token_price: new_base_token_price, - eth_price: new_eth_price, + Ok(BaseTokenAPIRatio { + numerator: 1, + denominator: 100000, ratio_timestamp, }) } async fn persist_ratio( &self, - api_price: &BaseTokenAPIPrice, + api_price: &BaseTokenAPIRatio, pool: &ConnectionPool, ) -> anyhow::Result { let mut conn = pool @@ -100,9 +95,9 @@ impl MainNodeBaseTokenAdjuster { let id = conn .base_token_dal() - .insert_token_price( - &api_price.base_token_price, - &api_price.eth_price, + .insert_token_ratio( + api_price.numerator, + api_price.denominator, &api_price.ratio_timestamp.naive_utc(), ) .await?; @@ -111,7 +106,7 @@ impl MainNodeBaseTokenAdjuster { Ok(id) } - async fn retry_get_latest_price(&self) -> anyhow::Result { + async fn retry_get_latest_price(&self) -> anyhow::Result { let mut retry_delay = 1; // seconds let max_retries = 5; let mut attempts = 1; @@ -123,14 +118,15 @@ impl MainNodeBaseTokenAdjuster { .await .expect("Failed to obtain connection to the database"); - let result = conn.base_token_dal().get_latest_price().await; + let result = conn.base_token_dal().get_latest_ratio().await; drop(conn); if let Ok(last_storage_price) = result { - return Ok(last_storage_price - .base_token_price - .div(&last_storage_price.eth_price)); + return Ok(BaseTokenConversionRatio { + numerator: last_storage_price.numerator, + denominator: last_storage_price.denominator, + }); } else { if attempts >= max_retries { break; @@ -164,266 +160,57 @@ impl MainNodeBaseTokenAdjuster { tokio::time::sleep(sleep_duration).await; } - /// If no base token L1 address is set then the base token is ETH. + /// Compare with SHARED_BRIDGE_ETHER_TOKEN_ADDRESS fn is_base_token_eth(&self) -> bool { - self.base_token_l1_address.is_none() + self.base_token_l1_address == SHARED_BRIDGE_ETHER_TOKEN_ADDRESS } } #[async_trait] impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { - async fn convert(&self, params: FeeParams) -> anyhow::Result { + async fn get_conversion_ratio(&self) -> anyhow::Result { let is_eth = self.is_base_token_eth(); if is_eth { - return Ok(params); + return Ok(BaseTokenConversionRatio { + numerator: 1, + denominator: 1, + }); } // Retries are necessary for the initial setup, where prices may not yet be persisted. - let latest_ratio = self.retry_get_latest_price().await?; + self.retry_get_latest_price().await // TODO(PE-129): Implement latest ratio usability logic. - - if let FeeParams::V2(params_v2) = params { - Ok(FeeParams::V2(convert_to_base_token( - params_v2, - latest_ratio, - ))) - } else { - panic!("Custom base token is not supported for V1 fee model") - } - } -} - -/// Converts the fee parameters to the base token using the latest ratio fetched from the DB. -fn convert_to_base_token(params: FeeParamsV2, base_token_to_eth: BigDecimal) -> FeeParamsV2 { - let FeeParamsV2 { - config, - l1_gas_price, - l1_pubdata_price, - } = params; - - let convert_price = |price_in_wei: u64| -> u64 { - let converted_price_bd = BigDecimal::from(price_in_wei) * base_token_to_eth.clone(); - match converted_price_bd.to_u64() { - Some(converted_price) => converted_price, - None => { - if converted_price_bd > BigDecimal::from(u64::MAX) { - tracing::warn!( - "Conversion to base token price failed: converted price is too large: {}", - converted_price_bd - ); - } else { - tracing::error!( - "Conversion to base token price failed: converted price is not a valid u64: {}", - converted_price_bd - ); - } - u64::MAX - } - } - }; - - let l1_gas_price_converted = convert_price(l1_gas_price); - let l1_pubdata_price_converted = convert_price(l1_pubdata_price); - let minimal_l2_gas_price_converted = convert_price(config.minimal_l2_gas_price); - - FeeParamsV2 { - config: FeeModelConfigV2 { - minimal_l2_gas_price: minimal_l2_gas_price_converted, - ..config - }, - l1_gas_price: l1_gas_price_converted, - l1_pubdata_price: l1_pubdata_price_converted, } } #[derive(Debug)] #[allow(dead_code)] pub struct MockBaseTokenAdjuster { - last_ratio: BigDecimal, - base_token: String, + last_ratio: BaseTokenConversionRatio, } impl MockBaseTokenAdjuster { - pub fn new(last_ratio: BigDecimal, base_token: String) -> Self { - Self { - last_ratio, - base_token, - } + pub fn new(last_ratio: BaseTokenConversionRatio) -> Self { + Self { last_ratio } } } impl Default for MockBaseTokenAdjuster { fn default() -> Self { Self { - last_ratio: BigDecimal::from(1), - base_token: "ETH".to_string(), + last_ratio: BaseTokenConversionRatio { + numerator: 1, + denominator: 1, + }, } } } #[async_trait] impl BaseTokenAdjuster for MockBaseTokenAdjuster { - async fn convert(&self, params: FeeParams) -> anyhow::Result { - match self.base_token.as_str() { - "ETH" => Ok(params), - _ => { - if let FeeParams::V2(params_v2) = params { - Ok(FeeParams::V2(convert_to_base_token( - params_v2, - self.last_ratio.clone(), - ))) - } else { - panic!("Custom base token is not supported for V1 fee model") - } - } - } - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use bigdecimal::BigDecimal; - use zksync_types::fee_model::{FeeModelConfigV2, FeeParamsV2}; - - use super::*; - - #[test] - fn test_convert_to_base_token() { - let base_fee_model_config = FeeModelConfigV2 { - minimal_l2_gas_price: 0, - // All the below are unaffected by this flow. - compute_overhead_part: 1.0, - pubdata_overhead_part: 1.0, - batch_overhead_l1_gas: 1, - max_gas_per_batch: 1, - max_pubdata_per_batch: 1, - }; - - struct TestCase { - name: &'static str, - base_token_to_eth: BigDecimal, - input_minimal_l2_gas_price: u64, - input_l1_gas_price: u64, - input_l1_pubdata_price: u64, - expected_minimal_l2_gas_price: u64, - expected_l1_gas_price: u64, - expected_l1_pubdata_price: u64, - } - - let test_cases = vec![ - TestCase { - name: "1 ETH = 2 BaseToken", - base_token_to_eth: BigDecimal::from(2), - input_minimal_l2_gas_price: 1000, - input_l1_gas_price: 2000, - input_l1_pubdata_price: 3000, - expected_minimal_l2_gas_price: 2000, - expected_l1_gas_price: 4000, - expected_l1_pubdata_price: 6000, - }, - TestCase { - name: "1 ETH = 0.5 BaseToken", - base_token_to_eth: BigDecimal::from_str("0.5").unwrap(), - input_minimal_l2_gas_price: 1000, - input_l1_gas_price: 2000, - input_l1_pubdata_price: 3000, - expected_minimal_l2_gas_price: 500, - expected_l1_gas_price: 1000, - expected_l1_pubdata_price: 1500, - }, - TestCase { - name: "1 ETH = 1 BaseToken", - base_token_to_eth: BigDecimal::from(1), - input_minimal_l2_gas_price: 1000, - input_l1_gas_price: 2000, - input_l1_pubdata_price: 3000, - expected_minimal_l2_gas_price: 1000, - expected_l1_gas_price: 2000, - expected_l1_pubdata_price: 3000, - }, - TestCase { - name: "Small conversion - 1 ETH - 1_000_000 BaseToken", - base_token_to_eth: BigDecimal::from_str("0.000001").unwrap(), - input_minimal_l2_gas_price: 1_000_000, - input_l1_gas_price: 2_000_000, - input_l1_pubdata_price: 3_000_000, - expected_minimal_l2_gas_price: 1, - expected_l1_gas_price: 2, - expected_l1_pubdata_price: 3, - }, - TestCase { - name: "Large conversion - 1 ETH = 0.000001 BaseToken", - base_token_to_eth: BigDecimal::from_str("1000000").unwrap(), - input_minimal_l2_gas_price: 1, - input_l1_gas_price: 2, - input_l1_pubdata_price: 3, - expected_minimal_l2_gas_price: 1_000_000, - expected_l1_gas_price: 2_000_000, - expected_l1_pubdata_price: 3_000_000, - }, - TestCase { - name: "Fractional conversion ratio", - base_token_to_eth: BigDecimal::from_str("1.123456789").unwrap(), - input_minimal_l2_gas_price: 1000, - input_l1_gas_price: 2000, - input_l1_pubdata_price: 3000, - expected_minimal_l2_gas_price: 1123, - expected_l1_gas_price: 2246, - expected_l1_pubdata_price: 3370, - }, - TestCase { - name: "Zero conversion ratio", - base_token_to_eth: BigDecimal::from(0), - input_minimal_l2_gas_price: 1000, - input_l1_gas_price: 2000, - input_l1_pubdata_price: 3000, - expected_minimal_l2_gas_price: 0, - expected_l1_gas_price: 0, - expected_l1_pubdata_price: 0, - }, - TestCase { - name: "Conversion ratio too large so clamp down to u64::MAX", - base_token_to_eth: BigDecimal::from(u64::MAX), - input_minimal_l2_gas_price: 2, - input_l1_gas_price: 2, - input_l1_pubdata_price: 2, - expected_minimal_l2_gas_price: u64::MAX, - expected_l1_gas_price: u64::MAX, - expected_l1_pubdata_price: u64::MAX, - }, - ]; - - for case in test_cases { - let input_params = FeeParamsV2 { - config: FeeModelConfigV2 { - minimal_l2_gas_price: case.input_minimal_l2_gas_price, - ..base_fee_model_config - }, - l1_gas_price: case.input_l1_gas_price, - l1_pubdata_price: case.input_l1_pubdata_price, - }; - - let result = convert_to_base_token(input_params, case.base_token_to_eth.clone()); - - assert_eq!( - result.config.minimal_l2_gas_price, case.expected_minimal_l2_gas_price, - "Test case '{}' failed: minimal_l2_gas_price mismatch", - case.name - ); - assert_eq!( - result.l1_gas_price, case.expected_l1_gas_price, - "Test case '{}' failed: l1_gas_price mismatch", - case.name - ); - assert_eq!( - result.l1_pubdata_price, case.expected_l1_pubdata_price, - "Test case '{}' failed: l1_pubdata_price mismatch", - case.name - ); - } + async fn get_conversion_ratio(&self) -> anyhow::Result { + Ok(self.last_ratio.clone()) } } diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 22dfcaf8c9c6..86ca4b94eabb 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -68,19 +68,18 @@ pub struct MainNodeFeeInputProvider { #[async_trait] impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { async fn get_fee_model_params(&self) -> anyhow::Result { - let params = match self.config { - FeeModelConfig::V1(config) => FeeParams::V1(FeeParamsV1 { + match self.config { + FeeModelConfig::V1(config) => Ok(FeeParams::V1(FeeParamsV1 { config, l1_gas_price: self.provider.estimate_effective_gas_price(), - }), - FeeModelConfig::V2(config) => FeeParams::V2(FeeParamsV2 { + })), + FeeModelConfig::V2(config) => Ok(FeeParams::V2(FeeParamsV2::new( config, - l1_gas_price: self.provider.estimate_effective_gas_price(), - l1_pubdata_price: self.provider.estimate_effective_pubdata_price(), - }), - }; - - self.base_token_adjuster.convert(params).await + self.provider.estimate_effective_gas_price(), + self.provider.estimate_effective_pubdata_price(), + self.base_token_adjuster.get_conversion_ratio().await?, + ))), + } } } @@ -171,11 +170,9 @@ fn compute_batch_fee_model_input_v2( l1_gas_price_scale_factor: f64, l1_pubdata_price_scale_factor: f64, ) -> PubdataIndependentBatchFeeModelInput { - let FeeParamsV2 { - config, - l1_gas_price, - l1_pubdata_price, - } = params; + let config = params.config(); + let l1_gas_price = params.l1_gas_price(); + let l1_pubdata_price = params.l1_pubdata_price(); let FeeModelConfigV2 { minimal_l2_gas_price, @@ -251,10 +248,8 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { #[cfg(test)] mod tests { - // use bigdecimal::{BigDecimal, ToPrimitive}; - use bigdecimal::BigDecimal; - // use test_casing::test_casing; use zksync_base_token_adjuster::MockBaseTokenAdjuster; + use zksync_types::fee_model::BaseTokenConversionRatio; use super::*; use crate::l1_gas_price::MockGasAdjuster; @@ -267,6 +262,11 @@ mod tests { // As a small small L2 gas price we'll use the value of 1 wei. const SMALL_L1_GAS_PRICE: u64 = 1; + const ONE_TO_ONE_CONVERSION: BaseTokenConversionRatio = BaseTokenConversionRatio { + numerator: 1, + denominator: 1, + }; + #[test] fn test_compute_batch_fee_model_input_v2_giant_numbers() { let config = FeeModelConfigV2 { @@ -283,11 +283,12 @@ mod tests { max_pubdata_per_batch: 100_000, }; - let params = FeeParamsV2 { + let params = FeeParamsV2::new( config, - l1_gas_price: GIANT_L1_GAS_PRICE, - l1_pubdata_price: GIANT_L1_GAS_PRICE, - }; + GIANT_L1_GAS_PRICE, + GIANT_L1_GAS_PRICE, + ONE_TO_ONE_CONVERSION, + ); // We'll use scale factor of 3.0 let input = compute_batch_fee_model_input_v2(params, 3.0, 3.0); @@ -309,11 +310,12 @@ mod tests { max_pubdata_per_batch: 100_000, }; - let params = FeeParamsV2 { + let params = FeeParamsV2::new( config, - l1_gas_price: SMALL_L1_GAS_PRICE, - l1_pubdata_price: SMALL_L1_GAS_PRICE, - }; + SMALL_L1_GAS_PRICE, + SMALL_L1_GAS_PRICE, + ONE_TO_ONE_CONVERSION, + ); let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -334,11 +336,12 @@ mod tests { max_pubdata_per_batch: 100_000, }; - let params = FeeParamsV2 { + let params = FeeParamsV2::new( config, - l1_gas_price: GIANT_L1_GAS_PRICE, - l1_pubdata_price: GIANT_L1_GAS_PRICE, - }; + GIANT_L1_GAS_PRICE, + GIANT_L1_GAS_PRICE, + ONE_TO_ONE_CONVERSION, + ); let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE); @@ -360,11 +363,12 @@ mod tests { max_pubdata_per_batch: 100_000, }; - let params = FeeParamsV2 { + let params = FeeParamsV2::new( config, - l1_gas_price: GIANT_L1_GAS_PRICE, - l1_pubdata_price: GIANT_L1_GAS_PRICE, - }; + GIANT_L1_GAS_PRICE, + GIANT_L1_GAS_PRICE, + ONE_TO_ONE_CONVERSION, + ); let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE); @@ -386,19 +390,23 @@ mod tests { max_pubdata_per_batch: 100_000, }; - let base_params = FeeParamsV2 { - config: base_config, - l1_gas_price: 1_000_000_000, - l1_pubdata_price: 1_000_000_000, - }; + let base_params = FeeParamsV2::new( + base_config, + 1_000_000_000, + 1_000_000_000, + ONE_TO_ONE_CONVERSION, + ); let base_input = compute_batch_fee_model_input_v2(base_params, 1.0, 1.0); let base_input_larger_l1_gas_price = compute_batch_fee_model_input_v2( - FeeParamsV2 { - l1_gas_price: base_params.l1_gas_price * 2, - ..base_params - }, + FeeParamsV2::new( + base_config, + // We double the L1 gas price + 2_000_000_000, + 1_000_000_000, + ONE_TO_ONE_CONVERSION, + ), 1.0, 1.0, ); @@ -418,10 +426,13 @@ mod tests { ); let base_input_larger_pubdata_price = compute_batch_fee_model_input_v2( - FeeParamsV2 { - l1_pubdata_price: base_params.l1_pubdata_price * 2, - ..base_params - }, + FeeParamsV2::new( + base_config, + 1_000_000_000, + // We double the L1 pubdata price + 2_000_000_000, + ONE_TO_ONE_CONVERSION, + ), 1.0, 1.0, ); @@ -441,13 +452,15 @@ mod tests { ); let base_input_larger_max_gas = compute_batch_fee_model_input_v2( - FeeParamsV2 { - config: FeeModelConfigV2 { + FeeParamsV2::new( + FeeModelConfigV2 { max_gas_per_batch: base_config.max_gas_per_batch * 2, ..base_config }, - ..base_params - }, + base_params.l1_gas_price(), + base_params.l1_pubdata_price(), + ONE_TO_ONE_CONVERSION, + ), 1.0, 1.0, ); @@ -461,13 +474,15 @@ mod tests { ); let base_input_larger_max_pubdata = compute_batch_fee_model_input_v2( - FeeParamsV2 { - config: FeeModelConfigV2 { + FeeParamsV2::new( + FeeModelConfigV2 { max_pubdata_per_batch: base_config.max_pubdata_per_batch * 2, ..base_config }, - ..base_params - }, + base_params.l1_gas_price(), + base_params.l1_pubdata_price(), + ONE_TO_ONE_CONVERSION, + ), 1.0, 1.0, ); @@ -485,55 +500,119 @@ mod tests { async fn test_get_fee_model_params() { struct TestCase { name: &'static str, - base_token: String, - base_token_to_eth: BigDecimal, - effective_l1_gas_price: u64, - effective_l1_pubdata_price: u64, - minimal_l2_gas_price: u64, - expected_l1_gas_price: u64, - expected_l1_pubdata_price: u64, - expected_minimal_l2_gas_price: u64, + conversion_ratio: BaseTokenConversionRatio, + input_minimal_l2_gas_price: u64, // Wei denomination + input_l1_gas_price: u64, // Wei + input_l1_pubdata_price: u64, // Wei + expected_minimal_l2_gas_price: u64, // BaseToken denomination + expected_l1_gas_price: u64, // BaseToken + expected_l1_pubdata_price: u64, // BaseToken } let test_cases = vec![ TestCase { - name: "Convert to a custom base token", - base_token: "ZK".to_string(), - base_token_to_eth: BigDecimal::from(200000), - effective_l1_gas_price: 10_000_000_000, // 10 gwei - effective_l1_pubdata_price: 20_000_000, // 0.02 gwei - minimal_l2_gas_price: 25_000_000, // 0.025 gwei - expected_l1_gas_price: 2_000_000_000_000_000, - expected_l1_pubdata_price: 4_000_000_000_000, - expected_minimal_l2_gas_price: 5_000_000_000_000, + name: "1 ETH = 2 BaseToken", + conversion_ratio: BaseTokenConversionRatio { + numerator: 2, + denominator: 1, + }, + input_minimal_l2_gas_price: 1000, + input_l1_gas_price: 2000, + input_l1_pubdata_price: 3000, + expected_minimal_l2_gas_price: 2000, + expected_l1_gas_price: 4000, + expected_l1_pubdata_price: 6000, + }, + TestCase { + name: "1 ETH = 0.5 BaseToken", + conversion_ratio: BaseTokenConversionRatio { + numerator: 1, + denominator: 2, + }, + input_minimal_l2_gas_price: 1000, + input_l1_gas_price: 2000, + input_l1_pubdata_price: 3000, + expected_minimal_l2_gas_price: 500, + expected_l1_gas_price: 1000, + expected_l1_pubdata_price: 1500, + }, + TestCase { + name: "1 ETH = 1 BaseToken", + conversion_ratio: BaseTokenConversionRatio { + numerator: 1, + denominator: 1, + }, + input_minimal_l2_gas_price: 1000, + input_l1_gas_price: 2000, + input_l1_pubdata_price: 3000, + expected_minimal_l2_gas_price: 1000, + expected_l1_gas_price: 2000, + expected_l1_pubdata_price: 3000, }, TestCase { - name: "ETH as base token (no conversion)", - base_token: "ETH".to_string(), - base_token_to_eth: BigDecimal::from(1), - effective_l1_gas_price: 15_000_000_000, // 15 gwei - effective_l1_pubdata_price: 30_000_000, // 0.03 gwei - minimal_l2_gas_price: 40_000_000, // 0.04 gwei - expected_l1_gas_price: 15_000_000_000, - expected_l1_pubdata_price: 30_000_000, - expected_minimal_l2_gas_price: 40_000_000, + name: "Large conversion - 1 ETH = 1_000 BaseToken", + conversion_ratio: BaseTokenConversionRatio { + numerator: 1_000_000, + denominator: 1, + }, + input_minimal_l2_gas_price: 1_000_000, + input_l1_gas_price: 2_000_000, + input_l1_pubdata_price: 3_000_000, + expected_minimal_l2_gas_price: 1_000_000_000_000, + expected_l1_gas_price: 2_000_000_000_000, + expected_l1_pubdata_price: 3_000_000_000_000, + }, + TestCase { + name: "Small conversion - 1 ETH = 0.001 BaseToken", + conversion_ratio: BaseTokenConversionRatio { + numerator: 1, + denominator: 1_000, + }, + input_minimal_l2_gas_price: 1_000_000, + input_l1_gas_price: 2_000_000, + input_l1_pubdata_price: 3_000_000, + expected_minimal_l2_gas_price: 1_000, + expected_l1_gas_price: 2_000, + expected_l1_pubdata_price: 3_000, + }, + TestCase { + name: "Fractional conversion ratio 123456789", + conversion_ratio: BaseTokenConversionRatio { + numerator: 1123456789, + denominator: 1_000_000_000, + }, + input_minimal_l2_gas_price: 1_000_000, + input_l1_gas_price: 2_000_000, + input_l1_pubdata_price: 3_000_000, + expected_minimal_l2_gas_price: 1123456, + expected_l1_gas_price: 2246913, + expected_l1_pubdata_price: 3370370, + }, + TestCase { + name: "Conversion ratio too large so clamp down to u64::MAX", + conversion_ratio: BaseTokenConversionRatio { + numerator: u64::MAX, + denominator: 1, + }, + input_minimal_l2_gas_price: 2, + input_l1_gas_price: 2, + input_l1_pubdata_price: 2, + expected_minimal_l2_gas_price: u64::MAX, + expected_l1_gas_price: u64::MAX, + expected_l1_pubdata_price: u64::MAX, }, ]; for case in test_cases { let gas_adjuster = Arc::new(MockGasAdjuster::new( - case.effective_l1_gas_price, - case.effective_l1_pubdata_price, + case.input_l1_gas_price, + case.input_l1_pubdata_price, )); - let base_token_adjuster = Arc::new(MockBaseTokenAdjuster::new( - case.base_token_to_eth.clone(), - case.base_token.clone(), - )); + let base_token_adjuster = Arc::new(MockBaseTokenAdjuster::new(case.conversion_ratio)); let config = FeeModelConfig::V2(FeeModelConfigV2 { - minimal_l2_gas_price: case.minimal_l2_gas_price, - // The below values don't matter for this test. + minimal_l2_gas_price: case.input_minimal_l2_gas_price, compute_overhead_part: 1.0, pubdata_overhead_part: 1.0, batch_overhead_l1_gas: 1, @@ -551,17 +630,20 @@ mod tests { if let FeeParams::V2(params) = fee_params { assert_eq!( - params.l1_gas_price, case.expected_l1_gas_price, + params.l1_gas_price(), + case.expected_l1_gas_price, "Test case '{}' failed: l1_gas_price mismatch", case.name ); assert_eq!( - params.l1_pubdata_price, case.expected_l1_pubdata_price, + params.l1_pubdata_price(), + case.expected_l1_pubdata_price, "Test case '{}' failed: l1_pubdata_price mismatch", case.name ); assert_eq!( - params.config.minimal_l2_gas_price, case.expected_minimal_l2_gas_price, + params.config().minimal_l2_gas_price, + case.expected_minimal_l2_gas_price, "Test case '{}' failed: minimal_l2_gas_price mismatch", case.name ); diff --git a/core/node/node_framework/examples/main_node.rs b/core/node/node_framework/examples/main_node.rs index b2cd8c675d89..83988158ca7e 100644 --- a/core/node/node_framework/examples/main_node.rs +++ b/core/node/node_framework/examples/main_node.rs @@ -113,6 +113,7 @@ impl MainNodeBuilder { let genesis_config = GenesisConfig::from_env()?; let eth_sender_config = EthConfig::from_env()?; let base_token_adjuster_config = BaseTokenAdjusterConfig::from_env()?; + let contracts_config = ContractsConfig::from_env()?; let sequencer_l1_gas_layer = SequencerL1GasLayer::new( gas_adjuster_config, genesis_config, @@ -122,6 +123,7 @@ impl MainNodeBuilder { .context("eth_sender")? .pubdata_sending_mode, base_token_adjuster_config, + contracts_config.base_token_addr, ); self.node.add_layer(sequencer_l1_gas_layer); Ok(self) diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index ca1c61a04dd1..90c3f447f481 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -13,12 +13,12 @@ use crate::{ /// A layer that wires the Base Token Adjuster task. #[derive(Debug)] pub struct BaseTokenAdjusterLayer { - base_token_l1_address: Option
, + base_token_l1_address: Address, config: BaseTokenAdjusterConfig, } impl BaseTokenAdjusterLayer { - pub fn new(base_token_l1_address: Option
, config: BaseTokenAdjusterConfig) -> Self { + pub fn new(base_token_l1_address: Address, config: BaseTokenAdjusterConfig) -> Self { Self { base_token_l1_address, config, @@ -49,7 +49,7 @@ impl WiringLayer for BaseTokenAdjusterLayer { #[derive(Debug)] struct BaseTokenAdjusterTask { main_pool: ConnectionPool, - base_token_l1_address: Option
, + base_token_l1_address: Address, config: BaseTokenAdjusterConfig, } diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index 3f4ddc7e1d56..ff26ffbc90a3 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -28,7 +28,7 @@ pub struct SequencerL1GasLayer { pubdata_sending_mode: PubdataSendingMode, state_keeper_config: StateKeeperConfig, base_token_adjuster_config: BaseTokenAdjusterConfig, - base_token_l1_address: Option
, + base_token_l1_address: Address, } impl SequencerL1GasLayer { @@ -38,7 +38,7 @@ impl SequencerL1GasLayer { state_keeper_config: StateKeeperConfig, pubdata_sending_mode: PubdataSendingMode, base_token_adjuster_config: BaseTokenAdjusterConfig, - base_token_l1_address: Option
, + base_token_l1_address: Address, ) -> Self { Self { gas_adjuster_config, diff --git a/prover/Cargo.lock b/prover/Cargo.lock index ccb07a1162d6..7e7888bbf160 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8485,6 +8485,7 @@ dependencies = [ "serde_json", "strum", "thiserror", + "tracing", "zksync_basic_types", "zksync_config", "zksync_contracts", From 58fea329138fb51c9bb1eb095de1e32c9bbeb107 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 26 Jun 2024 01:51:02 +0300 Subject: [PATCH 26/48] zk db prepare --- ...c7cb0460a87fdbe5bf0e474b2711d7fc5d765.json | 56 +++++++++++++++++++ ...9713f437db492e2075ca69e11e2ef5728ccaa.json | 24 ++++++++ 2 files changed, 80 insertions(+) create mode 100644 core/lib/dal/.sqlx/query-643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765.json create mode 100644 core/lib/dal/.sqlx/query-c5aef75dbeb520c965a0996abed9713f437db492e2075ca69e11e2ef5728ccaa.json diff --git a/core/lib/dal/.sqlx/query-643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765.json b/core/lib/dal/.sqlx/query-643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765.json new file mode 100644 index 000000000000..1c670b0b7c6e --- /dev/null +++ b/core/lib/dal/.sqlx/query-643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765.json @@ -0,0 +1,56 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n *\n FROM\n base_token_ratios\n ORDER BY\n created_at DESC\n LIMIT\n 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "created_at", + "type_info": "Timestamp" + }, + { + "ordinal": 2, + "name": "updated_at", + "type_info": "Timestamp" + }, + { + "ordinal": 3, + "name": "ratio_timestamp", + "type_info": "Timestamp" + }, + { + "ordinal": 4, + "name": "numerator", + "type_info": "Numeric" + }, + { + "ordinal": 5, + "name": "denominator", + "type_info": "Numeric" + }, + { + "ordinal": 6, + "name": "used_in_l1", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765" +} diff --git a/core/lib/dal/.sqlx/query-c5aef75dbeb520c965a0996abed9713f437db492e2075ca69e11e2ef5728ccaa.json b/core/lib/dal/.sqlx/query-c5aef75dbeb520c965a0996abed9713f437db492e2075ca69e11e2ef5728ccaa.json new file mode 100644 index 000000000000..6dd2f6cc7a9f --- /dev/null +++ b/core/lib/dal/.sqlx/query-c5aef75dbeb520c965a0996abed9713f437db492e2075ca69e11e2ef5728ccaa.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO\n base_token_ratios (numerator, denominator, ratio_timestamp, created_at, updated_at)\n VALUES\n ($1, $2, $3, NOW(), NOW())\n RETURNING\n id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Numeric", + "Numeric", + "Timestamp" + ] + }, + "nullable": [ + false + ] + }, + "hash": "c5aef75dbeb520c965a0996abed9713f437db492e2075ca69e11e2ef5728ccaa" +} From 2909d4a2e9aef51aac14a266507d8175bae5a88f Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 26 Jun 2024 14:30:11 +0300 Subject: [PATCH 27/48] revert unwanted contract changes. run adjuster in ci --- .github/workflows/ci-core-reusable.yml | 4 ++-- contracts | 2 +- core/lib/config/src/configs/contracts.rs | 2 +- core/lib/dal/src/base_token_dal.rs | 2 +- core/lib/dal/src/models/storage_base_token_ratio.rs | 4 ++-- core/lib/external_price_api/src/lib.rs | 2 +- .../types/src/{base_token_price.rs => base_token_ratio.rs} | 2 +- core/lib/types/src/lib.rs | 2 +- core/node/base_token_adjuster/src/base_token_adjuster.rs | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) rename core/lib/types/src/{base_token_price.rs => base_token_ratio.rs} (86%) diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index d860d79e06ac..93e5b72aaa06 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -135,7 +135,7 @@ jobs: base_token: ["Eth", "Custom"] deployment_mode: ["Rollup", "Validium"] env: - SERVER_COMPONENTS: "api,tree,eth,state_keeper,housekeeper,commitment_generator,vm_runner_protective_reads${{ matrix.consensus && ',consensus' || '' }}" + SERVER_COMPONENTS: "api,tree,eth,state_keeper,base_token_adjuster,housekeeper,commitment_generator,vm_runner_protective_reads${{ matrix.consensus && ',consensus' || '' }}" runs-on: [matterlabs-ci-runner] steps: @@ -304,7 +304,7 @@ jobs: runs-on: [matterlabs-ci-runner] env: - SERVER_COMPONENTS: "api,tree,eth,state_keeper,housekeeper,commitment_generator,vm_runner_protective_reads${{ matrix.consensus && ',consensus' || '' }}" + SERVER_COMPONENTS: "api,tree,eth,state_keeper,base_token_adjuster,housekeeper,commitment_generator,vm_runner_protective_reads${{ matrix.consensus && ',consensus' || '' }}" EXT_NODE_FLAGS: "${{ matrix.consensus && '-- --enable-consensus' || '' }}" steps: diff --git a/contracts b/contracts index 8a70bbbc4812..db9387690502 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 8a70bbbc48125f5bde6189b4e3c6a3ee79631678 +Subproject commit db9387690502937de081a959b164db5a5262ce0a diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index 2497eed947e0..5d6fbd3e2d1f 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -37,7 +37,7 @@ pub struct ContractsConfig { pub l2_testnet_paymaster_addr: Option
, pub l1_multicall3_addr: Address, pub ecosystem_contracts: Option, - // Used for the RPC API and by the BaseTokenAdjuster. If not set, ETH is the chain's base token + // Used by the RPC API and by the BaseTokenAdjuster. If not set, ETH is the chain's base token // and the "ETH address" is SHARED_BRIDGE_ETHER_TOKEN_ADDRESS. pub base_token_addr: Address, } diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index a069afcaf429..47237260f2d7 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -1,6 +1,6 @@ use bigdecimal::{BigDecimal, FromPrimitive}; use zksync_db_connection::{connection::Connection, error::DalResult, instrument::InstrumentExt}; -use zksync_types::base_token_price::BaseTokenRatio; +use zksync_types::base_token_ratio::BaseTokenRatio; use crate::{models::storage_base_token_ratio::StorageBaseTokenRatio, Core}; diff --git a/core/lib/dal/src/models/storage_base_token_ratio.rs b/core/lib/dal/src/models/storage_base_token_ratio.rs index b70e3136d8b6..92b9578070ec 100644 --- a/core/lib/dal/src/models/storage_base_token_ratio.rs +++ b/core/lib/dal/src/models/storage_base_token_ratio.rs @@ -1,8 +1,8 @@ use bigdecimal::{BigDecimal, ToPrimitive}; use chrono::NaiveDateTime; -use zksync_types::base_token_price::BaseTokenRatio; +use zksync_types::base_token_ratio::BaseTokenRatio; -/// Represents a row in the `storage_base_token_price` table. +/// Represents a row in the `base_token_ratios` table. #[derive(Debug, Clone)] pub struct StorageBaseTokenRatio { pub id: i64, diff --git a/core/lib/external_price_api/src/lib.rs b/core/lib/external_price_api/src/lib.rs index 6fa2574fdf86..4128c0f231f8 100644 --- a/core/lib/external_price_api/src/lib.rs +++ b/core/lib/external_price_api/src/lib.rs @@ -1,7 +1,7 @@ use std::fmt; use async_trait::async_trait; -use zksync_types::{base_token_price::BaseTokenAPIRatio, Address}; +use zksync_types::{base_token_ratio::BaseTokenAPIRatio, Address}; /// Trait that defines the interface for a client connecting with an external API to get prices. #[async_trait] diff --git a/core/lib/types/src/base_token_price.rs b/core/lib/types/src/base_token_ratio.rs similarity index 86% rename from core/lib/types/src/base_token_price.rs rename to core/lib/types/src/base_token_ratio.rs index 26c1a7f09c9e..6766adfd8579 100644 --- a/core/lib/types/src/base_token_price.rs +++ b/core/lib/types/src/base_token_ratio.rs @@ -1,6 +1,6 @@ use chrono::{DateTime, Utc}; -/// Represents the base token to ETH ratio at a given point in time. +/// Represents the base token to ETH conversion ratio at a given point in time. #[derive(Debug, Clone)] pub struct BaseTokenRatio { pub id: i64, diff --git a/core/lib/types/src/lib.rs b/core/lib/types/src/lib.rs index e54469bf0c49..105d43aa6c6c 100644 --- a/core/lib/types/src/lib.rs +++ b/core/lib/types/src/lib.rs @@ -57,7 +57,7 @@ pub mod vm_trace; pub mod zk_evm_types; pub mod api; -pub mod base_token_price; +pub mod base_token_ratio; pub mod eth_sender; pub mod helpers; pub mod proto; diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index 58ae6e612a02..daa48200e145 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -11,7 +11,7 @@ use tokio::{sync::watch, time::sleep}; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ - base_token_price::BaseTokenAPIRatio, fee_model::BaseTokenConversionRatio, Address, + base_token_ratio::BaseTokenAPIRatio, fee_model::BaseTokenConversionRatio, Address, SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, }; @@ -211,6 +211,6 @@ impl Default for MockBaseTokenAdjuster { #[async_trait] impl BaseTokenAdjuster for MockBaseTokenAdjuster { async fn get_conversion_ratio(&self) -> anyhow::Result { - Ok(self.last_ratio.clone()) + Ok(self.last_ratio) } } From 66f8aea5e75b2e3ca3f665993cd98afd0ff7e96c Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 26 Jun 2024 19:02:29 +0300 Subject: [PATCH 28/48] self review nits --- .../src/base_token_adjuster.rs | 70 +++++++++---------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index daa48200e145..accceb45a3b6 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -17,7 +17,11 @@ use zksync_types::{ #[async_trait] pub trait BaseTokenAdjuster: Debug + Send + Sync { - /// Returns the last ratio cached by the adjuster and ensure it's still usable. + /// Retrieves the most recent ratio persisted by the adjuster and ensures it's still usable. + /// + /// Returns: + /// - The `BaseTokenConversionRatio` if successful. + /// - An error if the ratio is not usable or cannot be retrieved. async fn get_conversion_ratio(&self) -> anyhow::Result; } @@ -72,7 +76,7 @@ impl MainNodeBaseTokenAdjuster { } // TODO (PE-135): Use real API client to fetch new ratio through self.PriceAPIClient & mock for tests. - // For now, these hard coded values are also hard coded in the integration tests. + // For now, these are hard coded dummy values. async fn fetch_new_ratio(&self) -> anyhow::Result { let ratio_timestamp = Utc::now(); @@ -101,7 +105,6 @@ impl MainNodeBaseTokenAdjuster { &api_price.ratio_timestamp.naive_utc(), ) .await?; - drop(conn); Ok(id) } @@ -118,37 +121,40 @@ impl MainNodeBaseTokenAdjuster { .await .expect("Failed to obtain connection to the database"); - let result = conn.base_token_dal().get_latest_ratio().await; + let dal_result = conn.base_token_dal().get_latest_ratio().await; - drop(conn); + drop(conn); // Don't sleep with your connections. - if let Ok(last_storage_price) = result { - return Ok(BaseTokenConversionRatio { - numerator: last_storage_price.numerator, - denominator: last_storage_price.denominator, - }); - } else { - if attempts >= max_retries { - break; + match dal_result { + Ok(last_storage_price) => { + return Ok(BaseTokenConversionRatio { + numerator: last_storage_price.numerator, + denominator: last_storage_price.denominator, + }); } - let sleep_duration = Duration::from_secs(retry_delay) - .mul_f32(rand::thread_rng().gen_range(0.8..1.2)); - tracing::warn!( - "Attempt {}/{} failed to get latest base token price, retrying in {} seconds...", - attempts, max_retries, sleep_duration.as_secs() + Err(err) if attempts < max_retries => { + let sleep_duration = Duration::from_secs(retry_delay) + .mul_f32(rand::thread_rng().gen_range(0.8..1.2)); + tracing::warn!( + "Attempt {}/{} failed to get latest base token price: {:?}, retrying in {} seconds...", + attempts + 1, max_retries, err, sleep_duration.as_secs() ); - sleep(sleep_duration).await; - retry_delay *= 2; - attempts += 1; + sleep(sleep_duration).await; + retry_delay *= 2; + attempts += 1; + } + Err(err) => { + anyhow::bail!( + "Failed to get latest base token price after {} attempts: {:?}", + max_retries, + err + ); + } } } - anyhow::bail!( - "Failed to get latest base token price after {} attempts", - max_retries - ); } - // Sleep for the remaining duration of the polling period + /// Sleep for the remaining duration of the polling period async fn sleep_until_next_fetch(&self, start_time: Instant) { let elapsed_time = start_time.elapsed(); let sleep_duration = if elapsed_time >= self.config.price_polling_interval() { @@ -160,7 +166,7 @@ impl MainNodeBaseTokenAdjuster { tokio::time::sleep(sleep_duration).await; } - /// Compare with SHARED_BRIDGE_ETHER_TOKEN_ADDRESS + /// Returns true if the base token is ETH by comparing its address to the shared bridge ETH token address placeholder. fn is_base_token_eth(&self) -> bool { self.base_token_l1_address == SHARED_BRIDGE_ETHER_TOKEN_ADDRESS } @@ -169,9 +175,7 @@ impl MainNodeBaseTokenAdjuster { #[async_trait] impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { async fn get_conversion_ratio(&self) -> anyhow::Result { - let is_eth = self.is_base_token_eth(); - - if is_eth { + if self.is_base_token_eth() { return Ok(BaseTokenConversionRatio { numerator: 1, denominator: 1, @@ -191,12 +195,6 @@ pub struct MockBaseTokenAdjuster { last_ratio: BaseTokenConversionRatio, } -impl MockBaseTokenAdjuster { - pub fn new(last_ratio: BaseTokenConversionRatio) -> Self { - Self { last_ratio } - } -} - impl Default for MockBaseTokenAdjuster { fn default() -> Self { Self { From 06a4c1e637c164a6982f072e6fff31a15ef4cbc1 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 26 Jun 2024 19:11:35 +0300 Subject: [PATCH 29/48] use constructor instead of deleted new --- core/node/base_token_adjuster/src/base_token_adjuster.rs | 2 +- core/node/fee_model/src/lib.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index accceb45a3b6..b490bf2824f0 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -192,7 +192,7 @@ impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { #[derive(Debug)] #[allow(dead_code)] pub struct MockBaseTokenAdjuster { - last_ratio: BaseTokenConversionRatio, + pub last_ratio: BaseTokenConversionRatio, } impl Default for MockBaseTokenAdjuster { diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 86ca4b94eabb..88fbb3fcc6eb 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -609,7 +609,9 @@ mod tests { case.input_l1_pubdata_price, )); - let base_token_adjuster = Arc::new(MockBaseTokenAdjuster::new(case.conversion_ratio)); + let base_token_adjuster = Arc::new(MockBaseTokenAdjuster { + last_ratio: case.conversion_ratio, + }); let config = FeeModelConfig::V2(FeeModelConfigV2 { minimal_l2_gas_price: case.input_minimal_l2_gas_price, From 2a54ef19cccb24cff4b12c7b802cc31888b195a7 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Fri, 28 Jun 2024 18:11:20 +0300 Subject: [PATCH 30/48] separate the adjuster to two --- core/bin/zksync_server/src/node_builder.rs | 15 +- core/lib/config/src/configs/contracts.rs | 4 +- core/lib/dal/src/base_token_dal.rs | 17 +- .../src/models/storage_base_token_ratio.rs | 8 +- core/lib/types/src/base_token_ratio.rs | 10 +- core/lib/types/src/fee_model.rs | 19 +- .../src/base_token_adjuster.rs | 181 +++--------------- .../src/base_token_fetcher.rs | 104 ++++++++++ core/node/base_token_adjuster/src/lib.rs | 5 +- core/node/fee_model/src/lib.rs | 73 ++++--- .../node/node_framework/examples/main_node.rs | 2 - core/node/node_framework/examples/showcase.rs | 4 +- .../layers/base_token_adjuster.rs | 40 +--- .../layers/base_token_fetcher.rs | 51 +++++ .../src/implementations/layers/l1_gas.rs | 28 +-- .../src/implementations/layers/mod.rs | 1 + .../resources/base_token_fetcher.rs | 15 ++ .../src/implementations/resources/mod.rs | 1 + core/node/state_keeper/src/io/tests/tester.rs | 6 +- 19 files changed, 300 insertions(+), 284 deletions(-) create mode 100644 core/node/base_token_adjuster/src/base_token_fetcher.rs create mode 100644 core/node/node_framework/src/implementations/layers/base_token_fetcher.rs create mode 100644 core/node/node_framework/src/implementations/resources/base_token_fetcher.rs diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 2afb954bb689..77178c936e85 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -15,6 +15,7 @@ use zksync_node_api_server::{ use zksync_node_framework::{ implementations::layers::{ base_token_adjuster::BaseTokenAdjusterLayer, + base_token_fetcher::BaseTokenFetcherLayer, circuit_breaker_checker::CircuitBreakerCheckerLayer, commitment_generator::CommitmentGeneratorLayer, consensus::{ConsensusLayer, Mode as ConsensusMode}, @@ -50,6 +51,7 @@ use zksync_node_framework::{ }, service::{ZkStackService, ZkStackServiceBuilder}, }; +use zksync_types::SHARED_BRIDGE_ETHER_TOKEN_ADDRESS; use zksync_vlog::prometheus::PrometheusExporterConfig; /// Macro that looks into a path to fetch an optional config, @@ -141,19 +143,21 @@ impl MainNodeBuilder { } fn add_sequencer_l1_gas_layer(mut self) -> anyhow::Result { + // Prerequisite for the layer that inserts a resource the sequencer_l1_gas_layer uses. + if self.contracts_config.base_token_addr != SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { + self.node.add_layer(BaseTokenFetcherLayer {}); + } + let gas_adjuster_config = try_load_config!(self.configs.eth) .gas_adjuster .context("Gas adjuster")?; let state_keeper_config = try_load_config!(self.configs.state_keeper_config); let eth_sender_config = try_load_config!(self.configs.eth); - let base_token_adjuster_config = try_load_config!(self.configs.base_token_adjuster); let sequencer_l1_gas_layer = SequencerL1GasLayer::new( gas_adjuster_config, self.genesis_config.clone(), state_keeper_config, try_load_config!(eth_sender_config.sender).pubdata_sending_mode, - base_token_adjuster_config, - self.contracts_config.base_token_addr, ); self.node.add_layer(sequencer_l1_gas_layer); Ok(self) @@ -461,10 +465,7 @@ impl MainNodeBuilder { fn add_base_token_adjuster_layer(mut self) -> anyhow::Result { let config = try_load_config!(self.configs.base_token_adjuster); - self.node.add_layer(BaseTokenAdjusterLayer::new( - self.contracts_config.base_token_addr, - config, - )); + self.node.add_layer(BaseTokenAdjusterLayer::new(config)); Ok(self) } diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index 5d6fbd3e2d1f..1a81c1b2206d 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -37,8 +37,8 @@ pub struct ContractsConfig { pub l2_testnet_paymaster_addr: Option
, pub l1_multicall3_addr: Address, pub ecosystem_contracts: Option, - // Used by the RPC API and by the BaseTokenAdjuster. If not set, ETH is the chain's base token - // and the "ETH address" is SHARED_BRIDGE_ETHER_TOKEN_ADDRESS. + // Used by the RPC API and by the node builders in wiring the BaseTokenAdjuster layer. If not set, + // ETH is the chain's base token and the "ETH address" is SHARED_BRIDGE_ETHER_TOKEN_ADDRESS. pub base_token_addr: Address, } diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index 47237260f2d7..0f5f68b7e50e 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -1,3 +1,5 @@ +use std::num::NonZeroU64; + use bigdecimal::{BigDecimal, FromPrimitive}; use zksync_db_connection::{connection::Connection, error::DalResult, instrument::InstrumentExt}; use zksync_types::base_token_ratio::BaseTokenRatio; @@ -12,8 +14,8 @@ pub struct BaseTokenDal<'a, 'c> { impl BaseTokenDal<'_, '_> { pub async fn insert_token_ratio( &mut self, - numerator: u64, - denominator: u64, + numerator: NonZeroU64, + denominator: NonZeroU64, ratio_timestamp: &chrono::NaiveDateTime, ) -> DalResult { let row = sqlx::query!( @@ -25,8 +27,8 @@ impl BaseTokenDal<'_, '_> { RETURNING id "#, - BigDecimal::from_u64(numerator), - BigDecimal::from_u64(denominator), + BigDecimal::from_u64(numerator.get()), + BigDecimal::from_u64(denominator.get()), ratio_timestamp, ) .instrument("insert_token_ratio") @@ -38,7 +40,7 @@ impl BaseTokenDal<'_, '_> { // TODO (PE-128): pub async fn mark_l1_update() - pub async fn get_latest_ratio(&mut self) -> DalResult { + pub async fn get_latest_ratio(&mut self) -> DalResult> { let row = sqlx::query_as!( StorageBaseTokenRatio, r#" @@ -53,8 +55,9 @@ impl BaseTokenDal<'_, '_> { "#, ) .instrument("get_latest_ratio") - .fetch_one(self.storage) + .fetch_optional(self.storage) .await?; - Ok(row.into()) + + Ok(row.map(|r| r.into())) } } diff --git a/core/lib/dal/src/models/storage_base_token_ratio.rs b/core/lib/dal/src/models/storage_base_token_ratio.rs index 92b9578070ec..996eae999588 100644 --- a/core/lib/dal/src/models/storage_base_token_ratio.rs +++ b/core/lib/dal/src/models/storage_base_token_ratio.rs @@ -1,3 +1,5 @@ +use std::num::NonZeroU64; + use bigdecimal::{BigDecimal, ToPrimitive}; use chrono::NaiveDateTime; use zksync_types::base_token_ratio::BaseTokenRatio; @@ -19,8 +21,10 @@ impl From for BaseTokenRatio { BaseTokenRatio { id: row.id, ratio_timestamp: row.ratio_timestamp.and_utc(), - numerator: row.numerator.to_u64().expect("numerator is not u64"), - denominator: row.denominator.to_u64().expect("denominator is not u64"), + numerator: NonZeroU64::new(row.numerator.to_u64().expect("numerator is not u64")) + .unwrap(), + denominator: NonZeroU64::new(row.denominator.to_u64().expect("denominator is not u64")) + .unwrap(), used_in_l1: row.used_in_l1, } } diff --git a/core/lib/types/src/base_token_ratio.rs b/core/lib/types/src/base_token_ratio.rs index 6766adfd8579..f63ba7f13936 100644 --- a/core/lib/types/src/base_token_ratio.rs +++ b/core/lib/types/src/base_token_ratio.rs @@ -1,3 +1,5 @@ +use std::num::NonZeroU64; + use chrono::{DateTime, Utc}; /// Represents the base token to ETH conversion ratio at a given point in time. @@ -5,16 +7,16 @@ use chrono::{DateTime, Utc}; pub struct BaseTokenRatio { pub id: i64, pub ratio_timestamp: DateTime, - pub numerator: u64, - pub denominator: u64, + pub numerator: NonZeroU64, + pub denominator: NonZeroU64, pub used_in_l1: bool, } /// Struct to represent API response containing denominator, numerator and optional timestamp. #[derive(Debug)] pub struct BaseTokenAPIRatio { - pub numerator: u64, - pub denominator: u64, + pub numerator: NonZeroU64, + pub denominator: NonZeroU64, // Either the timestamp of the quote or the timestamp of the request. pub ratio_timestamp: DateTime, } diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 69444c31dfc1..97f99b013571 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -1,3 +1,5 @@ +use std::num::NonZeroU64; + use bigdecimal::{BigDecimal, ToPrimitive}; use serde::{Deserialize, Serialize}; use zksync_config::configs::chain::{FeeModelVersion, StateKeeperConfig}; @@ -278,8 +280,8 @@ impl FeeParamsV2 { /// Converts the fee param to the base token. fn convert_to_base_token(&self, price_in_wei: u64) -> u64 { - let conversion_ratio = BigDecimal::from(self.conversion_ratio.numerator) - / BigDecimal::from(self.conversion_ratio.denominator); + let conversion_ratio = BigDecimal::from(self.conversion_ratio.numerator.get()) + / BigDecimal::from(self.conversion_ratio.denominator.get()); let converted_price_bd = BigDecimal::from(price_in_wei) * conversion_ratio; // Match on the converted price to ensure it can be represented as a u64 @@ -305,8 +307,17 @@ impl FeeParamsV2 { #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct BaseTokenConversionRatio { - pub numerator: u64, - pub denominator: u64, + pub numerator: NonZeroU64, + pub denominator: NonZeroU64, +} + +impl Default for BaseTokenConversionRatio { + fn default() -> Self { + Self { + numerator: NonZeroU64::new(1).unwrap(), + denominator: NonZeroU64::new(1).unwrap(), + } + } } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index b490bf2824f0..ac60c0a9c13d 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -1,77 +1,42 @@ -use std::{ - fmt::Debug, - time::{Duration, Instant}, -}; +use std::{fmt::Debug, num::NonZero}; use anyhow::Context as _; -use async_trait::async_trait; use chrono::Utc; -use rand::Rng; -use tokio::{sync::watch, time::sleep}; +use tokio::sync::watch; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use zksync_dal::{ConnectionPool, Core, CoreDal}; -use zksync_types::{ - base_token_ratio::BaseTokenAPIRatio, fee_model::BaseTokenConversionRatio, Address, - SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, -}; - -#[async_trait] -pub trait BaseTokenAdjuster: Debug + Send + Sync { - /// Retrieves the most recent ratio persisted by the adjuster and ensures it's still usable. - /// - /// Returns: - /// - The `BaseTokenConversionRatio` if successful. - /// - An error if the ratio is not usable or cannot be retrieved. - async fn get_conversion_ratio(&self) -> anyhow::Result; -} +use zksync_types::base_token_ratio::BaseTokenAPIRatio; #[derive(Debug, Clone)] /// BaseTokenAdjuster implementation for the main node (not the External Node). TODO (PE-137): impl APIBaseTokenAdjuster -pub struct MainNodeBaseTokenAdjuster { +pub struct BaseTokenConversionPersister { pool: ConnectionPool, - base_token_l1_address: Address, config: BaseTokenAdjusterConfig, } -impl MainNodeBaseTokenAdjuster { - pub fn new( - pool: ConnectionPool, - config: BaseTokenAdjusterConfig, - base_token_l1_address: Address, - ) -> Self { - Self { - pool, - config, - base_token_l1_address, - } +impl BaseTokenConversionPersister { + pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { + Self { pool, config } } /// Main loop for the base token adjuster. /// Orchestrates fetching new ratio, persisting it, and updating L1. - pub async fn run(&mut self, stop_receiver: watch::Receiver) -> anyhow::Result<()> { + pub async fn run(&mut self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { + let mut timer = tokio::time::interval(self.config.price_polling_interval()); let pool = self.pool.clone(); - loop { - if *stop_receiver.borrow() { - tracing::info!("Stop signal received, base_token_adjuster is shutting down"); - break; - } - - let start_time = Instant::now(); - match self.fetch_new_ratio().await { - Ok(new_ratio) => { - match self.persist_ratio(&new_ratio, &pool).await { - Ok(_) => { - // TODO(PE-128): Update L1 ratio - } - Err(err) => tracing::error!("Error persisting ratio: {:?}", err), - } - } - Err(err) => tracing::error!("Error fetching new ratio: {:?}", err), + while !*stop_receiver.borrow_and_update() { + tokio::select! { + _ = timer.tick() => { /* continue iterations */ } + _ = stop_receiver.changed() => break, } - self.sleep_until_next_fetch(start_time).await; + let new_ratio = self.fetch_new_ratio().await?; + self.persist_ratio(&new_ratio, &pool).await?; + // TODO(PE-128): Update L1 ratio } + + tracing::info!("Stop signal received, eth_watch is shutting down"); Ok(()) } @@ -81,8 +46,8 @@ impl MainNodeBaseTokenAdjuster { let ratio_timestamp = Utc::now(); Ok(BaseTokenAPIRatio { - numerator: 1, - denominator: 100000, + numerator: NonZero::new(1).unwrap(), + denominator: NonZero::new(100000).unwrap(), ratio_timestamp, }) } @@ -104,111 +69,9 @@ impl MainNodeBaseTokenAdjuster { api_price.denominator, &api_price.ratio_timestamp.naive_utc(), ) - .await?; + .await + .context("Failed to insert token ratio into the database")?; Ok(id) } - - async fn retry_get_latest_price(&self) -> anyhow::Result { - let mut retry_delay = 1; // seconds - let max_retries = 5; - let mut attempts = 1; - - loop { - let mut conn = self - .pool - .connection_tagged("base_token_adjuster") - .await - .expect("Failed to obtain connection to the database"); - - let dal_result = conn.base_token_dal().get_latest_ratio().await; - - drop(conn); // Don't sleep with your connections. - - match dal_result { - Ok(last_storage_price) => { - return Ok(BaseTokenConversionRatio { - numerator: last_storage_price.numerator, - denominator: last_storage_price.denominator, - }); - } - Err(err) if attempts < max_retries => { - let sleep_duration = Duration::from_secs(retry_delay) - .mul_f32(rand::thread_rng().gen_range(0.8..1.2)); - tracing::warn!( - "Attempt {}/{} failed to get latest base token price: {:?}, retrying in {} seconds...", - attempts + 1, max_retries, err, sleep_duration.as_secs() - ); - sleep(sleep_duration).await; - retry_delay *= 2; - attempts += 1; - } - Err(err) => { - anyhow::bail!( - "Failed to get latest base token price after {} attempts: {:?}", - max_retries, - err - ); - } - } - } - } - - /// Sleep for the remaining duration of the polling period - async fn sleep_until_next_fetch(&self, start_time: Instant) { - let elapsed_time = start_time.elapsed(); - let sleep_duration = if elapsed_time >= self.config.price_polling_interval() { - Duration::from_secs(0) - } else { - self.config.price_polling_interval() - elapsed_time - }; - - tokio::time::sleep(sleep_duration).await; - } - - /// Returns true if the base token is ETH by comparing its address to the shared bridge ETH token address placeholder. - fn is_base_token_eth(&self) -> bool { - self.base_token_l1_address == SHARED_BRIDGE_ETHER_TOKEN_ADDRESS - } -} - -#[async_trait] -impl BaseTokenAdjuster for MainNodeBaseTokenAdjuster { - async fn get_conversion_ratio(&self) -> anyhow::Result { - if self.is_base_token_eth() { - return Ok(BaseTokenConversionRatio { - numerator: 1, - denominator: 1, - }); - } - - // Retries are necessary for the initial setup, where prices may not yet be persisted. - self.retry_get_latest_price().await - - // TODO(PE-129): Implement latest ratio usability logic. - } -} - -#[derive(Debug)] -#[allow(dead_code)] -pub struct MockBaseTokenAdjuster { - pub last_ratio: BaseTokenConversionRatio, -} - -impl Default for MockBaseTokenAdjuster { - fn default() -> Self { - Self { - last_ratio: BaseTokenConversionRatio { - numerator: 1, - denominator: 1, - }, - } - } -} - -#[async_trait] -impl BaseTokenAdjuster for MockBaseTokenAdjuster { - async fn get_conversion_ratio(&self) -> anyhow::Result { - Ok(self.last_ratio) - } } diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_fetcher.rs new file mode 100644 index 000000000000..2be5369df6dc --- /dev/null +++ b/core/node/base_token_adjuster/src/base_token_fetcher.rs @@ -0,0 +1,104 @@ +use std::{fmt::Debug, num::NonZero, time::Duration}; + +use rand::Rng; +use tokio::{sync::watch, time::sleep}; +use zksync_dal::{ConnectionPool, Core, CoreDal}; +use zksync_types::{base_token_ratio::BaseTokenRatio, fee_model::BaseTokenConversionRatio}; + +const CACHE_UPDATE_INTERVAL: Duration = Duration::from_millis(500); + +#[derive(Debug, Clone)] +/// BaseTokenAdjuster implementation for the main node (not the External Node). +pub struct BaseTokenFetcher { + pub pool: Option>, + pub latest_ratio: Option, +} + +impl BaseTokenFetcher { + pub async fn run(&mut self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { + let mut timer = tokio::time::interval(CACHE_UPDATE_INTERVAL); + + while !*stop_receiver.borrow_and_update() { + tokio::select! { + _ = timer.tick() => { /* continue iterations */ } + _ = stop_receiver.changed() => break, + } + + let latest_storage_ratio = self.retry_get_latest_price().await?; + + // TODO(PE-129): Implement latest ratio usability logic. + self.latest_ratio = Some(BaseTokenConversionRatio { + numerator: latest_storage_ratio.numerator, + denominator: latest_storage_ratio.denominator, + }); + } + + tracing::info!("Stop signal received, eth_watch is shutting down"); + Ok(()) + } + + async fn retry_get_latest_price(&self) -> anyhow::Result { + let mut retry_delay = 1; // seconds + let max_retries = 5; + let mut attempts = 1; + + loop { + let mut conn = self + .pool + .as_ref() + .expect("Connection pool is not set") + .connection_tagged("base_token_adjuster") + .await + .expect("Failed to obtain connection to the database"); + + let dal_result = conn.base_token_dal().get_latest_ratio().await; + + drop(conn); // Don't sleep with your connections. + + match dal_result { + Ok(Some(last_storage_price)) => { + return Ok(last_storage_price); + } + Ok(None) if attempts < max_retries => { + let sleep_duration = Duration::from_secs(retry_delay) + .mul_f32(rand::thread_rng().gen_range(0.8..1.2)); + tracing::warn!( + "Attempt {}/{} found no latest base token price, retrying in {} seconds...", + attempts, + max_retries, + sleep_duration.as_secs() + ); + sleep(sleep_duration).await; + retry_delay *= 2; + attempts += 1; + } + Ok(None) => { + anyhow::bail!( + "No latest base token price found after {} attempts", + max_retries + ); + } + Err(err) => { + anyhow::bail!("Failed to get latest base token price: {:?}", err); + } + } + } + } + + pub fn get_conversion_ratio(&self) -> Option { + self.latest_ratio + } +} + +// Default impl for a No Op BaseTokenFetcher. +impl Default for BaseTokenFetcher { + fn default() -> Self { + Self { + pool: None, + latest_ratio: Some(BaseTokenConversionRatio { + numerator: NonZero::new(1).unwrap(), + denominator: NonZero::new(1).unwrap(), + }), + } + } +} diff --git a/core/node/base_token_adjuster/src/lib.rs b/core/node/base_token_adjuster/src/lib.rs index 45c0615dfc01..646f182d81cc 100644 --- a/core/node/base_token_adjuster/src/lib.rs +++ b/core/node/base_token_adjuster/src/lib.rs @@ -1,7 +1,8 @@ extern crate core; -pub use self::base_token_adjuster::{ - BaseTokenAdjuster, MainNodeBaseTokenAdjuster, MockBaseTokenAdjuster, +pub use self::{ + base_token_adjuster::BaseTokenConversionPersister, base_token_fetcher::BaseTokenFetcher, }; mod base_token_adjuster; +mod base_token_fetcher; diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 88fbb3fcc6eb..ac4f9438dc5f 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -2,7 +2,7 @@ use std::{fmt, sync::Arc}; use anyhow::Context as _; use async_trait::async_trait; -use zksync_base_token_adjuster::BaseTokenAdjuster; +use zksync_base_token_adjuster::BaseTokenFetcher; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ fee_model::{ @@ -61,7 +61,7 @@ impl dyn BatchFeeModelInputProvider { #[derive(Debug)] pub struct MainNodeFeeInputProvider { provider: Arc, - base_token_adjuster: Arc, + base_token_adjuster: Arc, config: FeeModelConfig, } @@ -77,7 +77,7 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { config, self.provider.estimate_effective_gas_price(), self.provider.estimate_effective_pubdata_price(), - self.base_token_adjuster.get_conversion_ratio().await?, + self.base_token_adjuster.get_conversion_ratio().unwrap(), ))), } } @@ -86,12 +86,12 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { impl MainNodeFeeInputProvider { pub fn new( provider: Arc, - base_token_adjuster: Arc, + base_token_fetcher: Arc, config: FeeModelConfig, ) -> Self { Self { provider, - base_token_adjuster, + base_token_adjuster: base_token_fetcher, config, } } @@ -248,7 +248,8 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { #[cfg(test)] mod tests { - use zksync_base_token_adjuster::MockBaseTokenAdjuster; + use std::num::NonZeroU64; + use zksync_types::fee_model::BaseTokenConversionRatio; use super::*; @@ -262,11 +263,6 @@ mod tests { // As a small small L2 gas price we'll use the value of 1 wei. const SMALL_L1_GAS_PRICE: u64 = 1; - const ONE_TO_ONE_CONVERSION: BaseTokenConversionRatio = BaseTokenConversionRatio { - numerator: 1, - denominator: 1, - }; - #[test] fn test_compute_batch_fee_model_input_v2_giant_numbers() { let config = FeeModelConfigV2 { @@ -287,7 +283,7 @@ mod tests { config, GIANT_L1_GAS_PRICE, GIANT_L1_GAS_PRICE, - ONE_TO_ONE_CONVERSION, + BaseTokenConversionRatio::default(), ); // We'll use scale factor of 3.0 @@ -314,7 +310,7 @@ mod tests { config, SMALL_L1_GAS_PRICE, SMALL_L1_GAS_PRICE, - ONE_TO_ONE_CONVERSION, + BaseTokenConversionRatio::default(), ); let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -340,7 +336,7 @@ mod tests { config, GIANT_L1_GAS_PRICE, GIANT_L1_GAS_PRICE, - ONE_TO_ONE_CONVERSION, + BaseTokenConversionRatio::default(), ); let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -367,7 +363,7 @@ mod tests { config, GIANT_L1_GAS_PRICE, GIANT_L1_GAS_PRICE, - ONE_TO_ONE_CONVERSION, + BaseTokenConversionRatio::default(), ); let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); @@ -394,7 +390,7 @@ mod tests { base_config, 1_000_000_000, 1_000_000_000, - ONE_TO_ONE_CONVERSION, + BaseTokenConversionRatio::default(), ); let base_input = compute_batch_fee_model_input_v2(base_params, 1.0, 1.0); @@ -405,7 +401,7 @@ mod tests { // We double the L1 gas price 2_000_000_000, 1_000_000_000, - ONE_TO_ONE_CONVERSION, + BaseTokenConversionRatio::default(), ), 1.0, 1.0, @@ -431,7 +427,7 @@ mod tests { 1_000_000_000, // We double the L1 pubdata price 2_000_000_000, - ONE_TO_ONE_CONVERSION, + BaseTokenConversionRatio::default(), ), 1.0, 1.0, @@ -459,7 +455,7 @@ mod tests { }, base_params.l1_gas_price(), base_params.l1_pubdata_price(), - ONE_TO_ONE_CONVERSION, + BaseTokenConversionRatio::default(), ), 1.0, 1.0, @@ -481,7 +477,7 @@ mod tests { }, base_params.l1_gas_price(), base_params.l1_pubdata_price(), - ONE_TO_ONE_CONVERSION, + BaseTokenConversionRatio::default(), ), 1.0, 1.0, @@ -513,8 +509,8 @@ mod tests { TestCase { name: "1 ETH = 2 BaseToken", conversion_ratio: BaseTokenConversionRatio { - numerator: 2, - denominator: 1, + numerator: NonZeroU64::new(2).unwrap(), + denominator: NonZeroU64::new(1).unwrap(), }, input_minimal_l2_gas_price: 1000, input_l1_gas_price: 2000, @@ -526,8 +522,8 @@ mod tests { TestCase { name: "1 ETH = 0.5 BaseToken", conversion_ratio: BaseTokenConversionRatio { - numerator: 1, - denominator: 2, + numerator: NonZeroU64::new(1).unwrap(), + denominator: NonZeroU64::new(2).unwrap(), }, input_minimal_l2_gas_price: 1000, input_l1_gas_price: 2000, @@ -539,8 +535,8 @@ mod tests { TestCase { name: "1 ETH = 1 BaseToken", conversion_ratio: BaseTokenConversionRatio { - numerator: 1, - denominator: 1, + numerator: NonZeroU64::new(1).unwrap(), + denominator: NonZeroU64::new(1).unwrap(), }, input_minimal_l2_gas_price: 1000, input_l1_gas_price: 2000, @@ -552,8 +548,8 @@ mod tests { TestCase { name: "Large conversion - 1 ETH = 1_000 BaseToken", conversion_ratio: BaseTokenConversionRatio { - numerator: 1_000_000, - denominator: 1, + numerator: NonZeroU64::new(1_000_000).unwrap(), + denominator: NonZeroU64::new(1).unwrap(), }, input_minimal_l2_gas_price: 1_000_000, input_l1_gas_price: 2_000_000, @@ -565,8 +561,8 @@ mod tests { TestCase { name: "Small conversion - 1 ETH = 0.001 BaseToken", conversion_ratio: BaseTokenConversionRatio { - numerator: 1, - denominator: 1_000, + numerator: NonZeroU64::new(1).unwrap(), + denominator: NonZeroU64::new(1_000).unwrap(), }, input_minimal_l2_gas_price: 1_000_000, input_l1_gas_price: 2_000_000, @@ -578,8 +574,8 @@ mod tests { TestCase { name: "Fractional conversion ratio 123456789", conversion_ratio: BaseTokenConversionRatio { - numerator: 1123456789, - denominator: 1_000_000_000, + numerator: NonZeroU64::new(1123456789).unwrap(), + denominator: NonZeroU64::new(1_000_000_000).unwrap(), }, input_minimal_l2_gas_price: 1_000_000, input_l1_gas_price: 2_000_000, @@ -591,8 +587,8 @@ mod tests { TestCase { name: "Conversion ratio too large so clamp down to u64::MAX", conversion_ratio: BaseTokenConversionRatio { - numerator: u64::MAX, - denominator: 1, + numerator: NonZeroU64::new(u64::MAX).unwrap(), + denominator: NonZeroU64::new(1).unwrap(), }, input_minimal_l2_gas_price: 2, input_l1_gas_price: 2, @@ -609,9 +605,10 @@ mod tests { case.input_l1_pubdata_price, )); - let base_token_adjuster = Arc::new(MockBaseTokenAdjuster { - last_ratio: case.conversion_ratio, - }); + let base_token_fetcher = BaseTokenFetcher { + pool: None, + latest_ratio: Some(case.conversion_ratio), + }; let config = FeeModelConfig::V2(FeeModelConfigV2 { minimal_l2_gas_price: case.input_minimal_l2_gas_price, @@ -624,7 +621,7 @@ mod tests { let fee_provider = MainNodeFeeInputProvider::new( gas_adjuster.clone(), - base_token_adjuster.clone(), + Arc::new(base_token_fetcher), config, ); diff --git a/core/node/node_framework/examples/main_node.rs b/core/node/node_framework/examples/main_node.rs index d1a61243ff4a..9429cbd4a1d1 100644 --- a/core/node/node_framework/examples/main_node.rs +++ b/core/node/node_framework/examples/main_node.rs @@ -112,7 +112,6 @@ impl MainNodeBuilder { let state_keeper_config = StateKeeperConfig::from_env()?; let genesis_config = GenesisConfig::from_env()?; let eth_sender_config = EthConfig::from_env()?; - let base_token_adjuster_config = BaseTokenAdjusterConfig::from_env()?; let contracts_config = ContractsConfig::from_env()?; let sequencer_l1_gas_layer = SequencerL1GasLayer::new( gas_adjuster_config, @@ -122,7 +121,6 @@ impl MainNodeBuilder { .sender .context("eth_sender")? .pubdata_sending_mode, - base_token_adjuster_config, contracts_config.base_token_addr, ); self.node.add_layer(sequencer_l1_gas_layer); diff --git a/core/node/node_framework/examples/showcase.rs b/core/node/node_framework/examples/showcase.rs index 98baa5bc9683..b5ea44853407 100644 --- a/core/node/node_framework/examples/showcase.rs +++ b/core/node/node_framework/examples/showcase.rs @@ -31,7 +31,7 @@ struct MemoryDatabase { /// but in real envs we use GCP. Alternatively, we have different resource implementations for /// main node and EN, like `MempoolIO` and `ExternalIO`. /// -/// Whether it makes sense to hdie the actual resource behind a trait often depends on the resource +/// Whether it makes sense to hide the actual resource behind a trait often depends on the resource /// itself. For example, our DAL is massive and cannot realistically be changed easily, so it's OK /// for it to be a concrete resource. But for anything that may realistically have two different /// implementations, it's often a good idea to hide it behind a trait. @@ -51,7 +51,7 @@ impl Database for MemoryDatabase { } /// An idiomatic way to create a resource is to prepare a wrapper for it. -/// This way we separate the logic of framework (which is primarily about glueing things together) +/// This way we separate the logic of the framework (which is primarily about glueing things together) /// from an actual logic of the resource. #[derive(Clone)] struct DatabaseResource(pub Arc); diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index 90c3f447f481..8b0a037f0110 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -1,7 +1,5 @@ +use zksync_base_token_adjuster::BaseTokenConversionPersister; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; -use zksync_dal::Core; -use zksync_db_connection::connection_pool::ConnectionPool; -use zksync_types::Address; use crate::{ implementations::resources::pools::{MasterPool, PoolResource}, @@ -13,16 +11,12 @@ use crate::{ /// A layer that wires the Base Token Adjuster task. #[derive(Debug)] pub struct BaseTokenAdjusterLayer { - base_token_l1_address: Address, config: BaseTokenAdjusterConfig, } impl BaseTokenAdjusterLayer { - pub fn new(base_token_l1_address: Address, config: BaseTokenAdjusterConfig) -> Self { - Self { - base_token_l1_address, - config, - } + pub fn new(config: BaseTokenAdjusterConfig) -> Self { + Self { config } } } @@ -36,36 +30,22 @@ impl WiringLayer for BaseTokenAdjusterLayer { let master_pool_resource = context.get_resource::>().await?; let master_pool = master_pool_resource.get().await?; - context.add_task(Box::new(BaseTokenAdjusterTask { - main_pool: master_pool, - base_token_l1_address: self.base_token_l1_address, - config: self.config, - })); + let persister = BaseTokenConversionPersister::new(master_pool, self.config); + + context.add_task(Box::new(persister)); Ok(()) } } -#[derive(Debug)] -struct BaseTokenAdjusterTask { - main_pool: ConnectionPool, - base_token_l1_address: Address, - config: BaseTokenAdjusterConfig, -} - #[async_trait::async_trait] -impl Task for BaseTokenAdjusterTask { +impl Task for BaseTokenConversionPersister { fn id(&self) -> TaskId { "base_token_adjuster".into() } - async fn run(self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { - let mut adjuster = zksync_base_token_adjuster::MainNodeBaseTokenAdjuster::new( - self.main_pool, - self.config, - self.base_token_l1_address, - ); - - adjuster.run(stop_receiver.0).await + async fn run(mut self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { + (*self).run(stop_receiver.0).await?; + Ok(()) } } diff --git a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs new file mode 100644 index 000000000000..dc5b32983398 --- /dev/null +++ b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs @@ -0,0 +1,51 @@ +use std::sync::Arc; + +use zksync_base_token_adjuster::BaseTokenFetcher; + +use crate::{ + implementations::resources::{ + base_token_fetcher::BaseTokenFetcherResource, + pools::{MasterPool, PoolResource}, + }, + service::{ServiceContext, StopReceiver}, + task::{Task, TaskId}, + wiring_layer::{WiringError, WiringLayer}, +}; + +/// A layer that inserts a resource for [`BaseTokenFetcher`]. +#[derive(Debug)] +pub struct BaseTokenFetcherLayer; + +#[async_trait::async_trait] +impl WiringLayer for BaseTokenFetcherLayer { + fn layer_name(&self) -> &'static str { + "base_token_fetcher" + } + + async fn wire(self: Box, mut context: ServiceContext<'_>) -> Result<(), WiringError> { + let master_pool_resource = context.get_resource::>().await?; + let master_pool = master_pool_resource.get().await?; + + let fetcher = BaseTokenFetcher { + pool: Some(master_pool), + latest_ratio: None, + }; + + context.insert_resource(BaseTokenFetcherResource(Arc::new(fetcher.clone())))?; + context.add_task(Box::new(fetcher)); + + Ok(()) + } +} + +#[async_trait::async_trait] +impl Task for BaseTokenFetcher { + fn id(&self) -> TaskId { + "base_token_fetcher".into() + } + + async fn run(mut self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { + (*self).run(stop_receiver.0).await?; + Ok(()) + } +} diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index 3733b6638148..5ba67a20ac58 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -1,20 +1,17 @@ use std::sync::Arc; use anyhow::Context; -use zksync_base_token_adjuster::MainNodeBaseTokenAdjuster; use zksync_config::{ configs::{chain::StateKeeperConfig, eth_sender::PubdataSendingMode}, - BaseTokenAdjusterConfig, GasAdjusterConfig, GenesisConfig, + GasAdjusterConfig, GenesisConfig, }; use zksync_node_fee_model::{l1_gas_price::GasAdjuster, MainNodeFeeInputProvider}; -use zksync_types::{fee_model::FeeModelConfig, Address}; +use zksync_types::fee_model::FeeModelConfig; use crate::{ implementations::resources::{ - eth_interface::EthInterfaceResource, - fee_input::FeeInputResource, - l1_tx_params::L1TxParamsResource, - pools::{PoolResource, ReplicaPool}, + base_token_fetcher::BaseTokenFetcherResource, eth_interface::EthInterfaceResource, + fee_input::FeeInputResource, l1_tx_params::L1TxParamsResource, }, service::{ServiceContext, StopReceiver}, task::{Task, TaskId}, @@ -42,8 +39,6 @@ pub struct SequencerL1GasLayer { genesis_config: GenesisConfig, pubdata_sending_mode: PubdataSendingMode, state_keeper_config: StateKeeperConfig, - base_token_adjuster_config: BaseTokenAdjusterConfig, - base_token_l1_address: Address, } impl SequencerL1GasLayer { @@ -52,16 +47,12 @@ impl SequencerL1GasLayer { genesis_config: GenesisConfig, state_keeper_config: StateKeeperConfig, pubdata_sending_mode: PubdataSendingMode, - base_token_adjuster_config: BaseTokenAdjusterConfig, - base_token_l1_address: Address, ) -> Self { Self { gas_adjuster_config, genesis_config, pubdata_sending_mode, state_keeper_config, - base_token_adjuster_config, - base_token_l1_address, } } } @@ -84,18 +75,11 @@ impl WiringLayer for SequencerL1GasLayer { .context("GasAdjuster::new()")?; let gas_adjuster = Arc::new(adjuster); - let pool_resource = context.get_resource::>().await?; - let replica_pool = pool_resource.get().await?; - - let base_token_adjuster = MainNodeBaseTokenAdjuster::new( - replica_pool.clone(), - self.base_token_adjuster_config, - self.base_token_l1_address, - ); + let BaseTokenFetcherResource(fetcher) = context.get_resource_or_default().await; let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( gas_adjuster.clone(), - Arc::new(base_token_adjuster), + fetcher, FeeModelConfig::from_state_keeper_config(&self.state_keeper_config), )); context.insert_resource(FeeInputResource(batch_fee_input_provider))?; diff --git a/core/node/node_framework/src/implementations/layers/mod.rs b/core/node/node_framework/src/implementations/layers/mod.rs index e66bfbf01aa3..fd3772538c86 100644 --- a/core/node/node_framework/src/implementations/layers/mod.rs +++ b/core/node/node_framework/src/implementations/layers/mod.rs @@ -1,4 +1,5 @@ pub mod base_token_adjuster; +pub mod base_token_fetcher; pub mod batch_status_updater; pub mod circuit_breaker_checker; pub mod commitment_generator; diff --git a/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs b/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs new file mode 100644 index 000000000000..a01617b5482c --- /dev/null +++ b/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use zksync_base_token_adjuster::BaseTokenFetcher; + +use crate::resource::Resource; + +/// A resource that provides [`BaseTokenFetcher`] implementation to the service. +#[derive(Clone, Default)] +pub struct BaseTokenFetcherResource(pub Arc); + +impl Resource for BaseTokenFetcherResource { + fn name() -> String { + "common/base_token_adjuster".into() + } +} diff --git a/core/node/node_framework/src/implementations/resources/mod.rs b/core/node/node_framework/src/implementations/resources/mod.rs index edfb280d4db7..60eecb4b9e93 100644 --- a/core/node/node_framework/src/implementations/resources/mod.rs +++ b/core/node/node_framework/src/implementations/resources/mod.rs @@ -1,4 +1,5 @@ pub mod action_queue; +pub mod base_token_fetcher; pub mod circuit_breakers; pub mod eth_interface; pub mod fee_input; diff --git a/core/node/state_keeper/src/io/tests/tester.rs b/core/node/state_keeper/src/io/tests/tester.rs index 5b5664a780d0..10bfcef900e6 100644 --- a/core/node/state_keeper/src/io/tests/tester.rs +++ b/core/node/state_keeper/src/io/tests/tester.rs @@ -2,7 +2,7 @@ use std::{slice, sync::Arc, time::Duration}; -use zksync_base_token_adjuster::MockBaseTokenAdjuster; +use zksync_base_token_adjuster::BaseTokenFetcher; use zksync_config::{ configs::{chain::StateKeeperConfig, eth_sender::PubdataSendingMode, wallets::Wallets}, GasAdjusterConfig, @@ -82,7 +82,7 @@ impl Tester { MainNodeFeeInputProvider::new( gas_adjuster, - Arc::new(MockBaseTokenAdjuster::default()), + Arc::new(BaseTokenFetcher::default()), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), @@ -101,7 +101,7 @@ impl Tester { let gas_adjuster = Arc::new(self.create_gas_adjuster().await); let batch_fee_input_provider = MainNodeFeeInputProvider::new( gas_adjuster, - Arc::new(MockBaseTokenAdjuster::default()), + Arc::new(BaseTokenFetcher::default()), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), From c044dcd6528a8fb4b6edd4a3b638508fc2c69d48 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Fri, 28 Jun 2024 22:47:38 +0300 Subject: [PATCH 31/48] happy tests --- core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs | 3 ++- core/node/node_framework/examples/main_node.rs | 6 ++---- etc/env/file_based/general.yaml | 2 ++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs b/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs index f9229adf3688..acce8d74462e 100644 --- a/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs +++ b/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs @@ -2,6 +2,7 @@ use std::{ collections::VecDeque, + fmt, sync::{Arc, RwLock}, }; @@ -18,7 +19,7 @@ mod metrics; #[cfg(test)] mod tests; -pub trait L1GasAdjuster: Debug + Send + Sync { +pub trait L1GasAdjuster: fmt::Debug + Send + Sync { fn estimate_effective_gas_price(&self) -> u64; fn estimate_effective_pubdata_price(&self) -> u64; } diff --git a/core/node/node_framework/examples/main_node.rs b/core/node/node_framework/examples/main_node.rs index 9429cbd4a1d1..9fb81aa4069b 100644 --- a/core/node/node_framework/examples/main_node.rs +++ b/core/node/node_framework/examples/main_node.rs @@ -15,8 +15,8 @@ use zksync_config::{ DatabaseSecrets, FriProofCompressorConfig, FriProverConfig, FriWitnessGeneratorConfig, L1Secrets, ObservabilityConfig, ProofDataHandlerConfig, }, - ApiConfig, BaseTokenAdjusterConfig, ContractVerifierConfig, ContractsConfig, DBConfig, - EthConfig, EthWatchConfig, GasAdjusterConfig, GenesisConfig, ObjectStoreConfig, PostgresConfig, + ApiConfig, ContractVerifierConfig, ContractsConfig, DBConfig, EthConfig, EthWatchConfig, + GasAdjusterConfig, GenesisConfig, ObjectStoreConfig, PostgresConfig, }; use zksync_env_config::FromEnv; use zksync_metadata_calculator::MetadataCalculatorConfig; @@ -112,7 +112,6 @@ impl MainNodeBuilder { let state_keeper_config = StateKeeperConfig::from_env()?; let genesis_config = GenesisConfig::from_env()?; let eth_sender_config = EthConfig::from_env()?; - let contracts_config = ContractsConfig::from_env()?; let sequencer_l1_gas_layer = SequencerL1GasLayer::new( gas_adjuster_config, genesis_config, @@ -121,7 +120,6 @@ impl MainNodeBuilder { .sender .context("eth_sender")? .pubdata_sending_mode, - contracts_config.base_token_addr, ); self.node.add_layer(sequencer_l1_gas_layer); Ok(self) diff --git a/etc/env/file_based/general.yaml b/etc/env/file_based/general.yaml index f2733d5d1ee4..def36b243b45 100644 --- a/etc/env/file_based/general.yaml +++ b/etc/env/file_based/general.yaml @@ -295,6 +295,8 @@ prover_group: aggregation_round: 1 - circuit_id: 18 aggregation_round: 1 +base_token_adjuster: + price_polling_interval_ms: 10000 house_keeper: l1_batch_metrics_reporting_interval_ms: 10000 From 5f8dac7e22ad5aa6e06c5193c79cd7000e7f2cbe Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Sat, 29 Jun 2024 12:46:47 +0300 Subject: [PATCH 32/48] remove gas adjuster abstraction --- core/lib/config/src/configs/eth_sender.rs | 2 +- .../src/base_token_fetcher.rs | 9 +- core/node/base_token_adjuster/src/lib.rs | 2 - .../src/l1_gas_price/gas_adjuster/mod.rs | 128 +++++++----------- core/node/fee_model/src/l1_gas_price/mod.rs | 3 +- core/node/fee_model/src/lib.rs | 110 +++++++++++---- 6 files changed, 133 insertions(+), 121 deletions(-) diff --git a/core/lib/config/src/configs/eth_sender.rs b/core/lib/config/src/configs/eth_sender.rs index 58b81fa0a145..f60d2ac452ff 100644 --- a/core/lib/config/src/configs/eth_sender.rs +++ b/core/lib/config/src/configs/eth_sender.rs @@ -152,7 +152,7 @@ impl SenderConfig { } } -#[derive(Debug, Deserialize, Copy, Clone, PartialEq)] +#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Default)] pub struct GasAdjusterConfig { /// Priority Fee to be used by GasAdjuster pub default_priority_fee_per_gas: u64, diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_fetcher.rs index 2be5369df6dc..0f7902082c0a 100644 --- a/core/node/base_token_adjuster/src/base_token_fetcher.rs +++ b/core/node/base_token_adjuster/src/base_token_fetcher.rs @@ -1,6 +1,5 @@ use std::{fmt::Debug, num::NonZero, time::Duration}; -use rand::Rng; use tokio::{sync::watch, time::sleep}; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{base_token_ratio::BaseTokenRatio, fee_model::BaseTokenConversionRatio}; @@ -38,8 +37,8 @@ impl BaseTokenFetcher { } async fn retry_get_latest_price(&self) -> anyhow::Result { - let mut retry_delay = 1; // seconds - let max_retries = 5; + let retry_delay = 1; // seconds + let max_retries = 10; let mut attempts = 1; loop { @@ -60,8 +59,7 @@ impl BaseTokenFetcher { return Ok(last_storage_price); } Ok(None) if attempts < max_retries => { - let sleep_duration = Duration::from_secs(retry_delay) - .mul_f32(rand::thread_rng().gen_range(0.8..1.2)); + let sleep_duration = Duration::from_secs(retry_delay); tracing::warn!( "Attempt {}/{} found no latest base token price, retrying in {} seconds...", attempts, @@ -69,7 +67,6 @@ impl BaseTokenFetcher { sleep_duration.as_secs() ); sleep(sleep_duration).await; - retry_delay *= 2; attempts += 1; } Ok(None) => { diff --git a/core/node/base_token_adjuster/src/lib.rs b/core/node/base_token_adjuster/src/lib.rs index 646f182d81cc..eaca2d09fdfb 100644 --- a/core/node/base_token_adjuster/src/lib.rs +++ b/core/node/base_token_adjuster/src/lib.rs @@ -1,5 +1,3 @@ -extern crate core; - pub use self::{ base_token_adjuster::BaseTokenConversionPersister, base_token_fetcher::BaseTokenFetcher, }; diff --git a/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs b/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs index acce8d74462e..a3a1ed78e5b7 100644 --- a/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs +++ b/core/node/fee_model/src/l1_gas_price/gas_adjuster/mod.rs @@ -2,7 +2,6 @@ use std::{ collections::VecDeque, - fmt, sync::{Arc, RwLock}, }; @@ -19,11 +18,6 @@ mod metrics; #[cfg(test)] mod tests; -pub trait L1GasAdjuster: fmt::Debug + Send + Sync { - fn estimate_effective_gas_price(&self) -> u64; - fn estimate_effective_pubdata_price(&self) -> u64; -} - /// This component keeps track of the median `base_fee` from the last `max_base_fee_samples` blocks /// and of the median `blob_base_fee` from the last `max_blob_base_fee_sample` blocks. /// It is used to adjust the base_fee of transactions sent to L1. @@ -40,54 +34,6 @@ pub struct GasAdjuster { commitment_mode: L1BatchCommitmentMode, } -impl L1GasAdjuster for GasAdjuster { - fn estimate_effective_gas_price(&self) -> u64 { - if let Some(price) = self.config.internal_enforced_l1_gas_price { - return price; - } - - let effective_gas_price = self.get_base_fee(0) + self.get_priority_fee(); - - let calculated_price = - (self.config.internal_l1_pricing_multiplier * effective_gas_price as f64) as u64; - - // Bound the price if it's too high. - self.bound_gas_price(calculated_price) - } - - fn estimate_effective_pubdata_price(&self) -> u64 { - if let Some(price) = self.config.internal_enforced_pubdata_price { - return price; - } - - match self.pubdata_sending_mode { - PubdataSendingMode::Blobs => { - const BLOB_GAS_PER_BYTE: u64 = 1; // `BYTES_PER_BLOB` = `GAS_PER_BLOB` = 2 ^ 17. - - let blob_base_fee_median = self.blob_base_fee_statistics.median(); - - // Check if blob base fee overflows `u64` before converting. Can happen only in very extreme cases. - if blob_base_fee_median > U256::from(u64::MAX) { - let max_allowed = self.config.max_blob_base_fee(); - tracing::error!("Blob base fee is too high: {blob_base_fee_median}, using max allowed: {max_allowed}"); - return max_allowed; - } - METRICS - .median_blob_base_fee - .set(blob_base_fee_median.as_u64()); - let calculated_price = blob_base_fee_median.as_u64() as f64 - * BLOB_GAS_PER_BYTE as f64 - * self.config.internal_pubdata_pricing_multiplier; - - self.bound_blob_base_fee(calculated_price) - } - PubdataSendingMode::Calldata => { - self.estimate_effective_gas_price() * self.pubdata_byte_gas() - } - } - } -} - impl GasAdjuster { pub async fn new( eth_client: Box>, @@ -211,6 +157,54 @@ impl GasAdjuster { Ok(()) } + /// Returns the sum of base and priority fee, in wei, not considering time in mempool. + /// Can be used to get an estimate of current gas price. + pub(crate) fn estimate_effective_gas_price(&self) -> u64 { + if let Some(price) = self.config.internal_enforced_l1_gas_price { + return price; + } + + let effective_gas_price = self.get_base_fee(0) + self.get_priority_fee(); + + let calculated_price = + (self.config.internal_l1_pricing_multiplier * effective_gas_price as f64) as u64; + + // Bound the price if it's too high. + self.bound_gas_price(calculated_price) + } + + pub(crate) fn estimate_effective_pubdata_price(&self) -> u64 { + if let Some(price) = self.config.internal_enforced_pubdata_price { + return price; + } + + match self.pubdata_sending_mode { + PubdataSendingMode::Blobs => { + const BLOB_GAS_PER_BYTE: u64 = 1; // `BYTES_PER_BLOB` = `GAS_PER_BLOB` = 2 ^ 17. + + let blob_base_fee_median = self.blob_base_fee_statistics.median(); + + // Check if blob base fee overflows `u64` before converting. Can happen only in very extreme cases. + if blob_base_fee_median > U256::from(u64::MAX) { + let max_allowed = self.config.max_blob_base_fee(); + tracing::error!("Blob base fee is too high: {blob_base_fee_median}, using max allowed: {max_allowed}"); + return max_allowed; + } + METRICS + .median_blob_base_fee + .set(blob_base_fee_median.as_u64()); + let calculated_price = blob_base_fee_median.as_u64() as f64 + * BLOB_GAS_PER_BYTE as f64 + * self.config.internal_pubdata_pricing_multiplier; + + self.bound_blob_base_fee(calculated_price) + } + PubdataSendingMode::Calldata => { + self.estimate_effective_gas_price() * self.pubdata_byte_gas() + } + } + } + fn pubdata_byte_gas(&self) -> u64 { match self.commitment_mode { L1BatchCommitmentMode::Validium => 0, @@ -385,29 +379,3 @@ impl GasStatistics { self.0.read().unwrap().last_processed_block } } - -#[derive(Debug)] -#[allow(dead_code)] -pub struct MockGasAdjuster { - pub effective_l1_gas_price: u64, - pub effective_l1_pubdata_price: u64, -} - -impl L1GasAdjuster for MockGasAdjuster { - fn estimate_effective_gas_price(&self) -> u64 { - self.effective_l1_gas_price - } - - fn estimate_effective_pubdata_price(&self) -> u64 { - self.effective_l1_pubdata_price - } -} - -impl MockGasAdjuster { - pub fn new(effective_l1_gas_price: u64, effective_l1_pubdata_price: u64) -> Self { - Self { - effective_l1_gas_price, - effective_l1_pubdata_price, - } - } -} diff --git a/core/node/fee_model/src/l1_gas_price/mod.rs b/core/node/fee_model/src/l1_gas_price/mod.rs index 8ab197fff497..0dab2d921c40 100644 --- a/core/node/fee_model/src/l1_gas_price/mod.rs +++ b/core/node/fee_model/src/l1_gas_price/mod.rs @@ -3,8 +3,7 @@ use std::fmt; pub use self::{ - gas_adjuster::{GasAdjuster, L1GasAdjuster, MockGasAdjuster}, - main_node_fetcher::MainNodeFeeParamsFetcher, + gas_adjuster::GasAdjuster, main_node_fetcher::MainNodeFeeParamsFetcher, singleton::GasAdjusterSingleton, }; diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index ac4f9438dc5f..38d9425412b5 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -13,7 +13,7 @@ use zksync_types::{ }; use zksync_utils::ceil_div_u256; -use crate::l1_gas_price::L1GasAdjuster; +use crate::l1_gas_price::GasAdjuster; pub mod l1_gas_price; @@ -60,7 +60,7 @@ impl dyn BatchFeeModelInputProvider { /// case with the external node. #[derive(Debug)] pub struct MainNodeFeeInputProvider { - provider: Arc, + provider: Arc, base_token_adjuster: Arc, config: FeeModelConfig, } @@ -85,7 +85,7 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { impl MainNodeFeeInputProvider { pub fn new( - provider: Arc, + provider: Arc, base_token_fetcher: Arc, config: FeeModelConfig, ) -> Self { @@ -250,10 +250,11 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { mod tests { use std::num::NonZeroU64; - use zksync_types::fee_model::BaseTokenConversionRatio; + use zksync_config::{configs::eth_sender::PubdataSendingMode, GasAdjusterConfig}; + use zksync_eth_client::{clients::MockEthereum, BaseFees}; + use zksync_types::{commitment::L1BatchCommitmentMode, fee_model::BaseTokenConversionRatio}; use super::*; - use crate::l1_gas_price::MockGasAdjuster; // To test that overflow never happens, we'll use giant L1 gas price, i.e. // almost realistic very large value of 100k gwei. Since it is so large, we'll also @@ -504,7 +505,6 @@ mod tests { expected_l1_gas_price: u64, // BaseToken expected_l1_pubdata_price: u64, // BaseToken } - let test_cases = vec![ TestCase { name: "1 ETH = 2 BaseToken", @@ -600,32 +600,19 @@ mod tests { ]; for case in test_cases { - let gas_adjuster = Arc::new(MockGasAdjuster::new( - case.input_l1_gas_price, - case.input_l1_pubdata_price, - )); - - let base_token_fetcher = BaseTokenFetcher { - pool: None, - latest_ratio: Some(case.conversion_ratio), - }; - - let config = FeeModelConfig::V2(FeeModelConfigV2 { - minimal_l2_gas_price: case.input_minimal_l2_gas_price, - compute_overhead_part: 1.0, - pubdata_overhead_part: 1.0, - batch_overhead_l1_gas: 1, - max_gas_per_batch: 1, - max_pubdata_per_batch: 1, - }); - - let fee_provider = MainNodeFeeInputProvider::new( - gas_adjuster.clone(), - Arc::new(base_token_fetcher), - config, + let gas_adjuster = + setup_gas_adjuster(case.input_l1_gas_price, case.input_l1_pubdata_price).await; + let base_token_fetcher = setup_base_token_fetcher(case.conversion_ratio); + let fee_provider = setup_fee_provider( + gas_adjuster, + base_token_fetcher, + case.input_minimal_l2_gas_price, ); - let fee_params = fee_provider.get_fee_model_params().await.unwrap(); + let fee_params = fee_provider + .get_fee_model_params() + .await + .expect("Failed to get fee model params"); if let FeeParams::V2(params) = fee_params { assert_eq!( @@ -651,4 +638,67 @@ mod tests { } } } + + // Helper function to create base fees + fn base_fees(block: u64, blob: U256) -> BaseFees { + BaseFees { + base_fee_per_gas: block, + base_fee_per_blob_gas: blob, + } + } + + async fn setup_gas_adjuster(l1_gas_price: u64, l1_pubdata_price: u64) -> GasAdjuster { + let mock = MockEthereum::builder() + .with_fee_history(vec![ + base_fees(0, U256::from(4)), + base_fees(1, U256::from(3)), + ]) + .build(); + mock.advance_block_number(2); // Ensure we have enough blocks for the fee history + + let gas_adjuster_config = GasAdjusterConfig { + internal_enforced_l1_gas_price: Some(l1_gas_price), + internal_enforced_pubdata_price: Some(l1_pubdata_price), + max_base_fee_samples: 1, // Ensure this is less than the number of blocks + num_samples_for_blob_base_fee_estimate: 2, + ..Default::default() + }; + + GasAdjuster::new( + Box::new(mock.into_client()), + gas_adjuster_config, + PubdataSendingMode::Blobs, + L1BatchCommitmentMode::Rollup, + ) + .await + .expect("Failed to create GasAdjuster") + } + + fn setup_base_token_fetcher(conversion_ratio: BaseTokenConversionRatio) -> BaseTokenFetcher { + BaseTokenFetcher { + pool: None, + latest_ratio: Some(conversion_ratio), + } + } + + fn setup_fee_provider( + gas_adjuster: GasAdjuster, + base_token_fetcher: BaseTokenFetcher, + minimal_l2_gas_price: u64, + ) -> MainNodeFeeInputProvider { + let config = FeeModelConfig::V2(FeeModelConfigV2 { + minimal_l2_gas_price, + compute_overhead_part: 1.0, + pubdata_overhead_part: 1.0, + batch_overhead_l1_gas: 1, + max_gas_per_batch: 1, + max_pubdata_per_batch: 1, + }); + + MainNodeFeeInputProvider::new( + Arc::new(gas_adjuster.into()), + Arc::new(base_token_fetcher), + config, + ) + } } From f73280473c7374af4448f2f2aadf6c3cbb1c026d Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Sun, 30 Jun 2024 19:20:53 +0300 Subject: [PATCH 33/48] added config for initial values --- core/bin/zksync_server/src/node_builder.rs | 4 ++- .../config/src/configs/base_token_adjuster.rs | 17 ++++++--- .../lib/env_config/src/base_token_adjuster.rs | 9 +++++ core/lib/env_config/src/chain.rs | 14 ++------ core/lib/env_config/src/lib.rs | 1 + .../src/base_token_adjuster.rs | 14 +++++++- .../proto/config/base_token_adjuster.proto | 2 ++ .../src/base_token_fetcher.rs | 34 ++++++++++++++---- core/node/fee_model/src/lib.rs | 35 ++++++++++--------- .../layers/base_token_fetcher.rs | 12 ++++++- etc/env/base/base_token_adjuster.toml | 11 ++++++ etc/env/file_based/general.yaml | 2 ++ 12 files changed, 113 insertions(+), 42 deletions(-) create mode 100644 core/lib/env_config/src/base_token_adjuster.rs create mode 100644 etc/env/base/base_token_adjuster.toml diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 77178c936e85..b646bad5d28f 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -145,7 +145,9 @@ impl MainNodeBuilder { fn add_sequencer_l1_gas_layer(mut self) -> anyhow::Result { // Prerequisite for the layer that inserts a resource the sequencer_l1_gas_layer uses. if self.contracts_config.base_token_addr != SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { - self.node.add_layer(BaseTokenFetcherLayer {}); + let base_token_config = try_load_config!(self.configs.base_token_adjuster); + self.node + .add_layer(BaseTokenFetcherLayer::new(base_token_config)); } let gas_adjuster_config = try_load_config!(self.configs.eth) diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 8507d0e52a6d..d5963eea4c5d 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{num::NonZeroU64, time::Duration}; use serde::Deserialize; @@ -9,15 +9,24 @@ pub const DEFAULT_INTERVAL_MS: u64 = 30_000; pub struct BaseTokenAdjusterConfig { /// How often to fetch external APIs for a new ETH<->Base-Token price. pub price_polling_interval_ms: Option, + /// The numerator and the denominator make up the BaseToken/ETH conversion ratio. Upon initialization, + /// the ratio is set to these configured values to avoid loss of liveness while external APIs are being used. + pub initial_numerator: NonZeroU64, + pub initial_denominator: NonZeroU64, } -impl BaseTokenAdjusterConfig { - pub fn for_tests() -> Self { +// default +impl Default for BaseTokenAdjusterConfig { + fn default() -> Self { Self { - price_polling_interval_ms: Some(DEFAULT_INTERVAL_MS), + price_polling_interval_ms: None, + initial_numerator: NonZeroU64::new(1).unwrap(), + initial_denominator: NonZeroU64::new(1).unwrap(), } } +} +impl BaseTokenAdjusterConfig { pub fn price_polling_interval(&self) -> Duration { match self.price_polling_interval_ms { Some(interval) => Duration::from_millis(interval), diff --git a/core/lib/env_config/src/base_token_adjuster.rs b/core/lib/env_config/src/base_token_adjuster.rs new file mode 100644 index 000000000000..5e4ef39671ca --- /dev/null +++ b/core/lib/env_config/src/base_token_adjuster.rs @@ -0,0 +1,9 @@ +use zksync_config::configs::BaseTokenAdjusterConfig; + +use crate::{envy_load, FromEnv}; + +impl FromEnv for BaseTokenAdjusterConfig { + fn from_env() -> anyhow::Result { + envy_load("base_token_adjuster", "BASE_TOKEN_ADJUSTER_") + } +} diff --git a/core/lib/env_config/src/chain.rs b/core/lib/env_config/src/chain.rs index 156d63c57458..441fcc4159cb 100644 --- a/core/lib/env_config/src/chain.rs +++ b/core/lib/env_config/src/chain.rs @@ -1,9 +1,5 @@ -use zksync_config::{ - configs::chain::{ - CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, - StateKeeperConfig, - }, - BaseTokenAdjusterConfig, +use zksync_config::configs::chain::{ + CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, StateKeeperConfig, }; use crate::{envy_load, FromEnv}; @@ -38,12 +34,6 @@ impl FromEnv for MempoolConfig { } } -impl FromEnv for BaseTokenAdjusterConfig { - fn from_env() -> anyhow::Result { - envy_load("base_token_adjuster", "BASE_TOKEN_ADJUSTER_") - } -} - #[cfg(test)] mod tests { use zksync_basic_types::{commitment::L1BatchCommitmentMode, L2ChainId}; diff --git a/core/lib/env_config/src/lib.rs b/core/lib/env_config/src/lib.rs index 9218467fdaba..cfc23182b55e 100644 --- a/core/lib/env_config/src/lib.rs +++ b/core/lib/env_config/src/lib.rs @@ -21,6 +21,7 @@ mod proof_data_handler; mod snapshots_creator; mod utils; +mod base_token_adjuster; mod genesis; #[cfg(test)] mod test_utils; diff --git a/core/lib/protobuf_config/src/base_token_adjuster.rs b/core/lib/protobuf_config/src/base_token_adjuster.rs index f19f36a84a4d..d77eba0ece0e 100644 --- a/core/lib/protobuf_config/src/base_token_adjuster.rs +++ b/core/lib/protobuf_config/src/base_token_adjuster.rs @@ -1,5 +1,7 @@ +use anyhow::Context as _; +use std::num::NonZeroU64; use zksync_config::configs::{self}; -use zksync_protobuf::ProtoRepr; +use zksync_protobuf::{required, ProtoRepr}; use crate::proto::base_token_adjuster as proto; @@ -9,12 +11,22 @@ impl ProtoRepr for proto::BaseTokenAdjuster { fn read(&self) -> anyhow::Result { Ok(configs::base_token_adjuster::BaseTokenAdjusterConfig { price_polling_interval_ms: self.price_polling_interval_ms, + initial_numerator: NonZeroU64::new( + *required(&self.initial_numerator).context("Missing initial_numerator")?, + ) + .expect("initial_numerator must be non-zero"), + initial_denominator: NonZeroU64::new( + *required(&self.initial_denominator).context("Missing initial_denominator")?, + ) + .expect("initial_denominator must be non-zero"), }) } fn build(this: &Self::Type) -> Self { Self { price_polling_interval_ms: this.price_polling_interval_ms, + initial_numerator: Some(this.initial_numerator.get()), + initial_denominator: Some(this.initial_denominator.get()), } } } diff --git a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto index 67e97dd14cda..35c578d9c4e6 100644 --- a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto +++ b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto @@ -4,4 +4,6 @@ package zksync.config.base_token_adjuster; message BaseTokenAdjuster { optional uint64 price_polling_interval_ms = 1; + optional uint64 initial_numerator = 2; + optional uint64 initial_denominator = 3; } diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_fetcher.rs index 0f7902082c0a..c195b64ecdee 100644 --- a/core/node/base_token_adjuster/src/base_token_fetcher.rs +++ b/core/node/base_token_adjuster/src/base_token_fetcher.rs @@ -1,6 +1,7 @@ -use std::{fmt::Debug, num::NonZero, time::Duration}; +use std::{fmt::Debug, num::NonZeroU64, time::Duration}; use tokio::{sync::watch, time::sleep}; +use zksync_config::BaseTokenAdjusterConfig; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{base_token_ratio::BaseTokenRatio, fee_model::BaseTokenConversionRatio}; @@ -11,9 +12,26 @@ const CACHE_UPDATE_INTERVAL: Duration = Duration::from_millis(500); pub struct BaseTokenFetcher { pub pool: Option>, pub latest_ratio: Option, + pub config: BaseTokenAdjusterConfig, } impl BaseTokenFetcher { + pub fn new(pool: Option>, config: BaseTokenAdjusterConfig) -> Self { + let latest_ratio = Some(BaseTokenConversionRatio { + numerator: NonZeroU64::from(config.initial_numerator), + denominator: NonZeroU64::from(config.initial_denominator), + }); + tracing::debug!( + "Starting the base token fetcher with conversion ratio: {:?}", + latest_ratio + ); + Self { + pool, + latest_ratio, + config, + } + } + pub async fn run(&mut self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { let mut timer = tokio::time::interval(CACHE_UPDATE_INTERVAL); @@ -32,13 +50,13 @@ impl BaseTokenFetcher { }); } - tracing::info!("Stop signal received, eth_watch is shutting down"); + tracing::info!("Stop signal received, base_token_fetcher is shutting down"); Ok(()) } async fn retry_get_latest_price(&self) -> anyhow::Result { let retry_delay = 1; // seconds - let max_retries = 10; + let max_retries = 5; // should be enough time to allow fetching from external APIs & updating the DB upon init let mut attempts = 1; loop { @@ -82,8 +100,9 @@ impl BaseTokenFetcher { } } - pub fn get_conversion_ratio(&self) -> Option { - self.latest_ratio + // TODO(PE-129): Implement latest ratio usability logic. + pub fn get_conversion_ratio(&self) -> BaseTokenConversionRatio { + self.latest_ratio.expect("Conversion ratio is not set") } } @@ -93,9 +112,10 @@ impl Default for BaseTokenFetcher { Self { pool: None, latest_ratio: Some(BaseTokenConversionRatio { - numerator: NonZero::new(1).unwrap(), - denominator: NonZero::new(1).unwrap(), + numerator: NonZeroU64::new(1).unwrap(), + denominator: NonZeroU64::new(1).unwrap(), }), + config: BaseTokenAdjusterConfig::default(), } } } diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 38d9425412b5..8e0fcfd2d7dd 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -77,7 +77,7 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { config, self.provider.estimate_effective_gas_price(), self.provider.estimate_effective_pubdata_price(), - self.base_token_adjuster.get_conversion_ratio().unwrap(), + self.base_token_adjuster.get_conversion_ratio(), ))), } } @@ -250,7 +250,10 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { mod tests { use std::num::NonZeroU64; - use zksync_config::{configs::eth_sender::PubdataSendingMode, GasAdjusterConfig}; + use zksync_config::{ + configs::{base_token_adjuster::BaseTokenAdjusterConfig, eth_sender::PubdataSendingMode}, + GasAdjusterConfig, + }; use zksync_eth_client::{clients::MockEthereum, BaseFees}; use zksync_types::{commitment::L1BatchCommitmentMode, fee_model::BaseTokenConversionRatio}; @@ -602,7 +605,16 @@ mod tests { for case in test_cases { let gas_adjuster = setup_gas_adjuster(case.input_l1_gas_price, case.input_l1_pubdata_price).await; - let base_token_fetcher = setup_base_token_fetcher(case.conversion_ratio); + + let base_token_fetcher = BaseTokenFetcher::new( + None, + BaseTokenAdjusterConfig { + price_polling_interval_ms: None, + initial_numerator: case.conversion_ratio.numerator, + initial_denominator: case.conversion_ratio.denominator, + }, + ); + let fee_provider = setup_fee_provider( gas_adjuster, base_token_fetcher, @@ -639,7 +651,7 @@ mod tests { } } - // Helper function to create base fees + // Helper function to create BaseFees. fn base_fees(block: u64, blob: U256) -> BaseFees { BaseFees { base_fee_per_gas: block, @@ -647,6 +659,7 @@ mod tests { } } + // Helper function to setup the GasAdjuster. async fn setup_gas_adjuster(l1_gas_price: u64, l1_pubdata_price: u64) -> GasAdjuster { let mock = MockEthereum::builder() .with_fee_history(vec![ @@ -674,13 +687,7 @@ mod tests { .expect("Failed to create GasAdjuster") } - fn setup_base_token_fetcher(conversion_ratio: BaseTokenConversionRatio) -> BaseTokenFetcher { - BaseTokenFetcher { - pool: None, - latest_ratio: Some(conversion_ratio), - } - } - + // Helper function to setup the MainNodeFeeInputProvider. fn setup_fee_provider( gas_adjuster: GasAdjuster, base_token_fetcher: BaseTokenFetcher, @@ -695,10 +702,6 @@ mod tests { max_pubdata_per_batch: 1, }); - MainNodeFeeInputProvider::new( - Arc::new(gas_adjuster.into()), - Arc::new(base_token_fetcher), - config, - ) + MainNodeFeeInputProvider::new(Arc::new(gas_adjuster), Arc::new(base_token_fetcher), config) } } diff --git a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs index dc5b32983398..2bd52d0afd4c 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use zksync_base_token_adjuster::BaseTokenFetcher; +use zksync_config::BaseTokenAdjusterConfig; use crate::{ implementations::resources::{ @@ -14,7 +15,15 @@ use crate::{ /// A layer that inserts a resource for [`BaseTokenFetcher`]. #[derive(Debug)] -pub struct BaseTokenFetcherLayer; +pub struct BaseTokenFetcherLayer { + config: BaseTokenAdjusterConfig, +} + +impl BaseTokenFetcherLayer { + pub fn new(config: BaseTokenAdjusterConfig) -> Self { + Self { config } + } +} #[async_trait::async_trait] impl WiringLayer for BaseTokenFetcherLayer { @@ -29,6 +38,7 @@ impl WiringLayer for BaseTokenFetcherLayer { let fetcher = BaseTokenFetcher { pool: Some(master_pool), latest_ratio: None, + config: self.config, }; context.insert_resource(BaseTokenFetcherResource(Arc::new(fetcher.clone())))?; diff --git a/etc/env/base/base_token_adjuster.toml b/etc/env/base/base_token_adjuster.toml new file mode 100644 index 000000000000..bf78c9517318 --- /dev/null +++ b/etc/env/base/base_token_adjuster.toml @@ -0,0 +1,11 @@ +# Configuration for the Base Token Adjuster crate + +[base_token_adjuster] + +# How often to poll external price feeds for the base token price. +price_polling_interval_ms = "50000" + +# The numerator and the denominator make up the BaseToken/ETH conversion ratio. Upon initialization, +# the ratio is set to these configured values to avoid loss of liveness while external APIs are being used. +initial_numerator = "100" +initial_denominator = "1" diff --git a/etc/env/file_based/general.yaml b/etc/env/file_based/general.yaml index def36b243b45..e6f399b8c604 100644 --- a/etc/env/file_based/general.yaml +++ b/etc/env/file_based/general.yaml @@ -297,6 +297,8 @@ prover_group: aggregation_round: 1 base_token_adjuster: price_polling_interval_ms: 10000 + initial_numerator: 100 + initial_denominator: 1 house_keeper: l1_batch_metrics_reporting_interval_ms: 10000 From 50a376487aa6c4cb0c80be701cfb0e351768dd62 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Sun, 30 Jun 2024 19:22:51 +0300 Subject: [PATCH 34/48] fmt --- core/lib/protobuf_config/src/base_token_adjuster.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/protobuf_config/src/base_token_adjuster.rs b/core/lib/protobuf_config/src/base_token_adjuster.rs index d77eba0ece0e..9afe730b2add 100644 --- a/core/lib/protobuf_config/src/base_token_adjuster.rs +++ b/core/lib/protobuf_config/src/base_token_adjuster.rs @@ -1,5 +1,6 @@ -use anyhow::Context as _; use std::num::NonZeroU64; + +use anyhow::Context as _; use zksync_config::configs::{self}; use zksync_protobuf::{required, ProtoRepr}; From 5067528abad5a38d626d19735a9c232089d43a7e Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Mon, 1 Jul 2024 00:35:09 +0300 Subject: [PATCH 35/48] always init fetcher with new --- core/bin/zksync_server/src/node_builder.rs | 2 +- .../config/src/configs/base_token_adjuster.rs | 13 +----- core/lib/config/src/configs/contracts.rs | 2 +- ...afce09869d43eb88ec7fdb526ce8491e35d9.json} | 4 +- core/lib/dal/src/base_token_dal.rs | 2 +- core/lib/types/src/base_token_ratio.rs | 2 +- core/lib/types/src/fee_model.rs | 4 +- .../src/base_token_adjuster.rs | 11 +++-- .../src/base_token_fetcher.rs | 42 ++++++++++--------- core/node/base_token_adjuster/src/lib.rs | 4 +- core/node/fee_model/src/lib.rs | 6 +-- .../layers/base_token_adjuster.rs | 21 +++++++--- .../layers/base_token_fetcher.rs | 31 +++++++++----- .../resources/base_token_fetcher.rs | 2 +- etc/env/base/base_token_adjuster.toml | 4 +- etc/env/file_based/general.yaml | 4 +- 16 files changed, 81 insertions(+), 73 deletions(-) rename core/lib/dal/.sqlx/{query-643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765.json => query-0fc8ede1d0962938d606c6352335afce09869d43eb88ec7fdb526ce8491e35d9.json} (86%) diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index b646bad5d28f..9f75e1485e51 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -143,7 +143,7 @@ impl MainNodeBuilder { } fn add_sequencer_l1_gas_layer(mut self) -> anyhow::Result { - // Prerequisite for the layer that inserts a resource the sequencer_l1_gas_layer uses. + // Ensure the BaseTokenFetcherResource is inserted if the base token is not ETH. if self.contracts_config.base_token_addr != SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { let base_token_config = try_load_config!(self.configs.base_token_adjuster); self.node diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index d5963eea4c5d..c5ec9a7525d2 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -10,22 +10,11 @@ pub struct BaseTokenAdjusterConfig { /// How often to fetch external APIs for a new ETH<->Base-Token price. pub price_polling_interval_ms: Option, /// The numerator and the denominator make up the BaseToken/ETH conversion ratio. Upon initialization, - /// the ratio is set to these configured values to avoid loss of liveness while external APIs are being used. + /// the ratio is set to these configured values to avoid loss of liveness while we wait for external APIs. pub initial_numerator: NonZeroU64, pub initial_denominator: NonZeroU64, } -// default -impl Default for BaseTokenAdjusterConfig { - fn default() -> Self { - Self { - price_polling_interval_ms: None, - initial_numerator: NonZeroU64::new(1).unwrap(), - initial_denominator: NonZeroU64::new(1).unwrap(), - } - } -} - impl BaseTokenAdjusterConfig { pub fn price_polling_interval(&self) -> Duration { match self.price_polling_interval_ms { diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index 1a81c1b2206d..f763081ae8b8 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -37,7 +37,7 @@ pub struct ContractsConfig { pub l2_testnet_paymaster_addr: Option
, pub l1_multicall3_addr: Address, pub ecosystem_contracts: Option, - // Used by the RPC API and by the node builders in wiring the BaseTokenAdjuster layer. If not set, + // Used by the RPC API and by the node builder in wiring the BaseTokenFetcher layer. If not set, // ETH is the chain's base token and the "ETH address" is SHARED_BRIDGE_ETHER_TOKEN_ADDRESS. pub base_token_addr: Address, } diff --git a/core/lib/dal/.sqlx/query-643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765.json b/core/lib/dal/.sqlx/query-0fc8ede1d0962938d606c6352335afce09869d43eb88ec7fdb526ce8491e35d9.json similarity index 86% rename from core/lib/dal/.sqlx/query-643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765.json rename to core/lib/dal/.sqlx/query-0fc8ede1d0962938d606c6352335afce09869d43eb88ec7fdb526ce8491e35d9.json index 1c670b0b7c6e..1ad92abac368 100644 --- a/core/lib/dal/.sqlx/query-643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765.json +++ b/core/lib/dal/.sqlx/query-0fc8ede1d0962938d606c6352335afce09869d43eb88ec7fdb526ce8491e35d9.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n *\n FROM\n base_token_ratios\n ORDER BY\n created_at DESC\n LIMIT\n 1\n ", + "query": "\n SELECT\n *\n FROM\n base_token_ratios\n ORDER BY\n ratio_timestamp DESC\n LIMIT\n 1\n ", "describe": { "columns": [ { @@ -52,5 +52,5 @@ false ] }, - "hash": "643bf0ed5168d96e35bb42a5e1ac7cb0460a87fdbe5bf0e474b2711d7fc5d765" + "hash": "0fc8ede1d0962938d606c6352335afce09869d43eb88ec7fdb526ce8491e35d9" } diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index 0f5f68b7e50e..d5a9c00dc120 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -49,7 +49,7 @@ impl BaseTokenDal<'_, '_> { FROM base_token_ratios ORDER BY - created_at DESC + ratio_timestamp DESC LIMIT 1 "#, diff --git a/core/lib/types/src/base_token_ratio.rs b/core/lib/types/src/base_token_ratio.rs index f63ba7f13936..f584522ee4d7 100644 --- a/core/lib/types/src/base_token_ratio.rs +++ b/core/lib/types/src/base_token_ratio.rs @@ -12,7 +12,7 @@ pub struct BaseTokenRatio { pub used_in_l1: bool, } -/// Struct to represent API response containing denominator, numerator and optional timestamp. +/// Struct to represent API response containing denominator, numerator, and timestamp. #[derive(Debug)] pub struct BaseTokenAPIRatio { pub numerator: NonZeroU64, diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 97f99b013571..85ae7ed2b3d3 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -290,12 +290,12 @@ impl FeeParamsV2 { None => { if converted_price_bd > BigDecimal::from(u64::MAX) { tracing::warn!( - "Conversion to base token price failed: converted price is too large: {}", + "Conversion to base token price failed: converted price is too large: {}. Using u64::MAX instead.", converted_price_bd ); } else { tracing::error!( - "Conversion to base token price failed: converted price is not a valid u64: {}", + "Conversion to base token price failed: converted price is not a valid u64: {}. Using u64::MAX instead.", converted_price_bd ); } diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index ac60c0a9c13d..be8edf0ef261 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -8,19 +8,18 @@ use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::base_token_ratio::BaseTokenAPIRatio; #[derive(Debug, Clone)] -/// BaseTokenAdjuster implementation for the main node (not the External Node). TODO (PE-137): impl APIBaseTokenAdjuster -pub struct BaseTokenConversionPersister { +pub struct BaseTokenAdjuster { pool: ConnectionPool, config: BaseTokenAdjusterConfig, } -impl BaseTokenConversionPersister { +impl BaseTokenAdjuster { pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { Self { pool, config } } /// Main loop for the base token adjuster. - /// Orchestrates fetching new ratio, persisting it, and updating L1. + /// Orchestrates fetching a new ratio, persisting it, and conditionally updating the L1 with it. pub async fn run(&mut self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { let mut timer = tokio::time::interval(self.config.price_polling_interval()); let pool = self.pool.clone(); @@ -36,7 +35,7 @@ impl BaseTokenConversionPersister { // TODO(PE-128): Update L1 ratio } - tracing::info!("Stop signal received, eth_watch is shutting down"); + tracing::info!("Stop signal received, base_token_adjuster is shutting down"); Ok(()) } @@ -70,7 +69,7 @@ impl BaseTokenConversionPersister { &api_price.ratio_timestamp.naive_utc(), ) .await - .context("Failed to insert token ratio into the database")?; + .context("Failed to insert base token ratio into the database")?; Ok(id) } diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_fetcher.rs index c195b64ecdee..3ce1b158a7dd 100644 --- a/core/node/base_token_adjuster/src/base_token_fetcher.rs +++ b/core/node/base_token_adjuster/src/base_token_fetcher.rs @@ -8,19 +8,18 @@ use zksync_types::{base_token_ratio::BaseTokenRatio, fee_model::BaseTokenConvers const CACHE_UPDATE_INTERVAL: Duration = Duration::from_millis(500); #[derive(Debug, Clone)] -/// BaseTokenAdjuster implementation for the main node (not the External Node). pub struct BaseTokenFetcher { pub pool: Option>, - pub latest_ratio: Option, + pub latest_ratio: BaseTokenConversionRatio, pub config: BaseTokenAdjusterConfig, } impl BaseTokenFetcher { pub fn new(pool: Option>, config: BaseTokenAdjusterConfig) -> Self { - let latest_ratio = Some(BaseTokenConversionRatio { - numerator: NonZeroU64::from(config.initial_numerator), - denominator: NonZeroU64::from(config.initial_denominator), - }); + let latest_ratio = BaseTokenConversionRatio { + numerator: config.initial_numerator, + denominator: config.initial_denominator, + }; tracing::debug!( "Starting the base token fetcher with conversion ratio: {:?}", latest_ratio @@ -44,10 +43,10 @@ impl BaseTokenFetcher { let latest_storage_ratio = self.retry_get_latest_price().await?; // TODO(PE-129): Implement latest ratio usability logic. - self.latest_ratio = Some(BaseTokenConversionRatio { + self.latest_ratio = BaseTokenConversionRatio { numerator: latest_storage_ratio.numerator, denominator: latest_storage_ratio.denominator, - }); + }; } tracing::info!("Stop signal received, base_token_fetcher is shutting down"); @@ -55,7 +54,7 @@ impl BaseTokenFetcher { } async fn retry_get_latest_price(&self) -> anyhow::Result { - let retry_delay = 1; // seconds + let sleep_duration = Duration::from_secs(1); let max_retries = 5; // should be enough time to allow fetching from external APIs & updating the DB upon init let mut attempts = 1; @@ -64,7 +63,7 @@ impl BaseTokenFetcher { .pool .as_ref() .expect("Connection pool is not set") - .connection_tagged("base_token_adjuster") + .connection_tagged("base_token_fetcher") .await .expect("Failed to obtain connection to the database"); @@ -76,10 +75,9 @@ impl BaseTokenFetcher { Ok(Some(last_storage_price)) => { return Ok(last_storage_price); } - Ok(None) if attempts < max_retries => { - let sleep_duration = Duration::from_secs(retry_delay); + Ok(None) if attempts <= max_retries => { tracing::warn!( - "Attempt {}/{} found no latest base token price, retrying in {} seconds...", + "Attempt {}/{} found no latest base token ratio. Retrying in {} seconds...", attempts, max_retries, sleep_duration.as_secs() @@ -89,12 +87,12 @@ impl BaseTokenFetcher { } Ok(None) => { anyhow::bail!( - "No latest base token price found after {} attempts", + "No latest base token ratio found after {} attempts", max_retries ); } Err(err) => { - anyhow::bail!("Failed to get latest base token price: {:?}", err); + anyhow::bail!("Failed to get latest base token ratio: {:?}", err); } } } @@ -102,20 +100,24 @@ impl BaseTokenFetcher { // TODO(PE-129): Implement latest ratio usability logic. pub fn get_conversion_ratio(&self) -> BaseTokenConversionRatio { - self.latest_ratio.expect("Conversion ratio is not set") + self.latest_ratio } } -// Default impl for a No Op BaseTokenFetcher. +// Default impl for a no-op BaseTokenFetcher (conversion ratio is always 1:1). impl Default for BaseTokenFetcher { fn default() -> Self { Self { pool: None, - latest_ratio: Some(BaseTokenConversionRatio { + latest_ratio: BaseTokenConversionRatio { numerator: NonZeroU64::new(1).unwrap(), denominator: NonZeroU64::new(1).unwrap(), - }), - config: BaseTokenAdjusterConfig::default(), + }, + config: BaseTokenAdjusterConfig { + price_polling_interval_ms: None, + initial_numerator: NonZeroU64::new(1).unwrap(), + initial_denominator: NonZeroU64::new(1).unwrap(), + }, } } } diff --git a/core/node/base_token_adjuster/src/lib.rs b/core/node/base_token_adjuster/src/lib.rs index eaca2d09fdfb..8ec8befbfbf7 100644 --- a/core/node/base_token_adjuster/src/lib.rs +++ b/core/node/base_token_adjuster/src/lib.rs @@ -1,6 +1,4 @@ -pub use self::{ - base_token_adjuster::BaseTokenConversionPersister, base_token_fetcher::BaseTokenFetcher, -}; +pub use self::{base_token_adjuster::BaseTokenAdjuster, base_token_fetcher::BaseTokenFetcher}; mod base_token_adjuster; mod base_token_fetcher; diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 8e0fcfd2d7dd..c41d210afc3d 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -402,8 +402,7 @@ mod tests { let base_input_larger_l1_gas_price = compute_batch_fee_model_input_v2( FeeParamsV2::new( base_config, - // We double the L1 gas price - 2_000_000_000, + 2_000_000_000, // double the L1 gas price 1_000_000_000, BaseTokenConversionRatio::default(), ), @@ -429,8 +428,7 @@ mod tests { FeeParamsV2::new( base_config, 1_000_000_000, - // We double the L1 pubdata price - 2_000_000_000, + 2_000_000_000, // double the L1 pubdata price BaseTokenConversionRatio::default(), ), 1.0, diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index 8b0a037f0110..3cf5c8749b17 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -1,4 +1,4 @@ -use zksync_base_token_adjuster::BaseTokenConversionPersister; +use zksync_base_token_adjuster::BaseTokenAdjuster; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use crate::{ @@ -8,7 +8,18 @@ use crate::{ wiring_layer::{WiringError, WiringLayer}, }; -/// A layer that wires the Base Token Adjuster task. +/// Wiring layer for `BaseTokenAdjuster` +/// +/// Responsible for orchestrating communications with external API feeds to get ETH<->BaseToken +/// conversion ratios and persisting them both in the DB and in the L1. +/// +/// ## Requests resources +/// +/// - `PoolResource` +/// +/// ## Adds tasks +/// +/// - `BaseTokenAdjuster` #[derive(Debug)] pub struct BaseTokenAdjusterLayer { config: BaseTokenAdjusterConfig, @@ -30,16 +41,16 @@ impl WiringLayer for BaseTokenAdjusterLayer { let master_pool_resource = context.get_resource::>().await?; let master_pool = master_pool_resource.get().await?; - let persister = BaseTokenConversionPersister::new(master_pool, self.config); + let adjuster = BaseTokenAdjuster::new(master_pool, self.config); - context.add_task(Box::new(persister)); + context.add_task(Box::new(adjuster)); Ok(()) } } #[async_trait::async_trait] -impl Task for BaseTokenConversionPersister { +impl Task for BaseTokenAdjuster { fn id(&self) -> TaskId { "base_token_adjuster".into() } diff --git a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs index 2bd52d0afd4c..7b033d9aa0f7 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs @@ -6,14 +6,29 @@ use zksync_config::BaseTokenAdjusterConfig; use crate::{ implementations::resources::{ base_token_fetcher::BaseTokenFetcherResource, - pools::{MasterPool, PoolResource}, + pools::{PoolResource, ReplicaPool}, }, service::{ServiceContext, StopReceiver}, task::{Task, TaskId}, wiring_layer::{WiringError, WiringLayer}, }; -/// A layer that inserts a resource for [`BaseTokenFetcher`]. +/// Wiring layer for `BaseTokenFetcher` +/// +/// Responsible for serving the latest ETH<->BaseToken conversion ratio. This layer is only wired if +/// the base token is not ETH. If wired, this layer inserts the BaseTokenFetcherResource and kicks +/// off a task to poll the DB for the latest ratio and cache it. +/// +/// If the base token is ETH, a default, no-op impl of the BaseTokenFetcherResource is used by other +/// layers to always return a conversion ratio of 1. + +/// ## Requests resources +/// +/// - `PoolResource` +/// +/// ## Adds tasks +/// +/// - `BaseTokenFetcher` #[derive(Debug)] pub struct BaseTokenFetcherLayer { config: BaseTokenAdjusterConfig, @@ -32,14 +47,10 @@ impl WiringLayer for BaseTokenFetcherLayer { } async fn wire(self: Box, mut context: ServiceContext<'_>) -> Result<(), WiringError> { - let master_pool_resource = context.get_resource::>().await?; - let master_pool = master_pool_resource.get().await?; - - let fetcher = BaseTokenFetcher { - pool: Some(master_pool), - latest_ratio: None, - config: self.config, - }; + let replica_pool_resource = context.get_resource::>().await?; + let replica_pool = replica_pool_resource.get().await.unwrap(); + + let fetcher = BaseTokenFetcher::new(Some(replica_pool), self.config); context.insert_resource(BaseTokenFetcherResource(Arc::new(fetcher.clone())))?; context.add_task(Box::new(fetcher)); diff --git a/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs b/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs index a01617b5482c..1f8493be55a4 100644 --- a/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs +++ b/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs @@ -10,6 +10,6 @@ pub struct BaseTokenFetcherResource(pub Arc); impl Resource for BaseTokenFetcherResource { fn name() -> String { - "common/base_token_adjuster".into() + "common/base_token_fetcher".into() } } diff --git a/etc/env/base/base_token_adjuster.toml b/etc/env/base/base_token_adjuster.toml index bf78c9517318..0f24773ced87 100644 --- a/etc/env/base/base_token_adjuster.toml +++ b/etc/env/base/base_token_adjuster.toml @@ -3,9 +3,9 @@ [base_token_adjuster] # How often to poll external price feeds for the base token price. -price_polling_interval_ms = "50000" +price_polling_interval_ms = "30000" # The numerator and the denominator make up the BaseToken/ETH conversion ratio. Upon initialization, -# the ratio is set to these configured values to avoid loss of liveness while external APIs are being used. +# the ratio is set to these configured values to avoid loss of liveness while we wait for external APIs. initial_numerator = "100" initial_denominator = "1" diff --git a/etc/env/file_based/general.yaml b/etc/env/file_based/general.yaml index e6f399b8c604..2e476e3ae0b1 100644 --- a/etc/env/file_based/general.yaml +++ b/etc/env/file_based/general.yaml @@ -296,7 +296,7 @@ prover_group: - circuit_id: 18 aggregation_round: 1 base_token_adjuster: - price_polling_interval_ms: 10000 + price_polling_interval_ms: 30000 initial_numerator: 100 initial_denominator: 1 @@ -323,7 +323,7 @@ prometheus: observability: log_format: plain - log_directives: "zksync_node_test_utils=info,zksync_state_keeper=info,zksync_reorg_detector=info,zksync_consistency_checker=info,zksync_metadata_calculator=info,zksync_node_sync=info,zksync_node_consensus=info,zksync_contract_verification_server=info,zksync_node_api_server=info,zksync_tee_verifier_input_producer=info,zksync_node_framework=info,zksync_block_reverter=info,zksync_commitment_generator=info,zksync_node_db_pruner=info,zksync_eth_sender=info,zksync_node_fee_model=info,zksync_node_genesis=info,zksync_house_keeper=info,zksync_proof_data_handler=info,zksync_shared_metrics=info,zksync_node_test_utils=info,zksync_vm_runner=info,zksync_consensus_bft=info,zksync_consensus_network=info,zksync_consensus_storage=info,zksync_core_leftovers=debug,zksync_server=debug,zksync_contract_verifier=debug,zksync_dal=info,zksync_db_connection=info,zksync_eth_client=info,zksync_eth_watch=debug,zksync_storage=info,zksync_db_manager=info,zksync_merkle_tree=info,zksync_state=debug,zksync_utils=debug,zksync_queued_job_processor=info,zksync_types=info,zksync_mempool=debug,loadnext=info,vm=info,zksync_object_store=info,zksync_external_node=info,zksync_witness_generator=info,zksync_prover_fri=info,zksync_witness_vector_generator=info,zksync_web3_decl=debug,zksync_health_check=debug,zksync_proof_fri_compressor=info,vise_exporter=error,snapshots_creator=debug" + log_directives: "zksync_node_test_utils=info,zksync_state_keeper=info,zksync_reorg_detector=info,zksync_consistency_checker=info,zksync_metadata_calculator=info,zksync_node_sync=info,zksync_node_consensus=info,zksync_contract_verification_server=info,zksync_node_api_server=info,zksync_tee_verifier_input_producer=info,zksync_node_framework=info,zksync_block_reverter=info,zksync_commitment_generator=info,zksync_node_db_pruner=info,zksync_eth_sender=info,zksync_node_fee_model=info,zksync_node_genesis=info,zksync_house_keeper=info,zksync_proof_data_handler=info,zksync_shared_metrics=info,zksync_node_test_utils=info,zksync_vm_runner=info,zksync_consensus_bft=info,zksync_consensus_network=info,zksync_consensus_storage=info,zksync_core_leftovers=debug,zksync_server=debug,zksync_contract_verifier=debug,zksync_dal=info,zksync_db_connection=info,zksync_eth_client=info,zksync_eth_watch=debug,zksync_storage=info,zksync_db_manager=info,zksync_merkle_tree=info,zksync_state=debug,zksync_utils=debug,zksync_queued_job_processor=info,zksync_types=info,zksync_mempool=debug,loadnext=info,vm=info,zksync_object_store=info,zksync_external_node=info,zksync_witness_generator=info,zksync_prover_fri=info,zksync_witness_vector_generator=info,zksync_web3_decl=debug,zksync_health_check=debug,zksync_proof_fri_compressor=info,vise_exporter=error,snapshots_creator=debug,zksync_base_token_adjuster=debug" sentry: url: unset panic_interval: 1800 From 7ad6300961a28665f8aaf6d60e05092da58afa2a Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Mon, 1 Jul 2024 16:01:11 +0300 Subject: [PATCH 36/48] removing initial config. abstracting BaseTokenFetcher --- core/bin/zksync_server/src/node_builder.rs | 4 +- .../config/src/configs/base_token_adjuster.rs | 6 +- .../src/base_token_adjuster.rs | 15 +- .../proto/config/base_token_adjuster.proto | 2 - .../src/base_token_fetcher.rs | 155 ++++++++++-------- core/node/base_token_adjuster/src/lib.rs | 5 +- core/node/fee_model/src/lib.rs | 20 +-- .../layers/base_token_fetcher.rs | 17 +- .../resources/base_token_fetcher.rs | 12 +- core/node/state_keeper/src/io/tests/tester.rs | 6 +- etc/env/base/base_token_adjuster.toml | 5 - etc/env/file_based/general.yaml | 2 - 12 files changed, 119 insertions(+), 130 deletions(-) diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 9f75e1485e51..f7a99ceccc9f 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -145,9 +145,7 @@ impl MainNodeBuilder { fn add_sequencer_l1_gas_layer(mut self) -> anyhow::Result { // Ensure the BaseTokenFetcherResource is inserted if the base token is not ETH. if self.contracts_config.base_token_addr != SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { - let base_token_config = try_load_config!(self.configs.base_token_adjuster); - self.node - .add_layer(BaseTokenFetcherLayer::new(base_token_config)); + self.node.add_layer(BaseTokenFetcherLayer {}); } let gas_adjuster_config = try_load_config!(self.configs.eth) diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index c5ec9a7525d2..4b61ef3dc163 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -1,4 +1,4 @@ -use std::{num::NonZeroU64, time::Duration}; +use std::time::Duration; use serde::Deserialize; @@ -9,10 +9,6 @@ pub const DEFAULT_INTERVAL_MS: u64 = 30_000; pub struct BaseTokenAdjusterConfig { /// How often to fetch external APIs for a new ETH<->Base-Token price. pub price_polling_interval_ms: Option, - /// The numerator and the denominator make up the BaseToken/ETH conversion ratio. Upon initialization, - /// the ratio is set to these configured values to avoid loss of liveness while we wait for external APIs. - pub initial_numerator: NonZeroU64, - pub initial_denominator: NonZeroU64, } impl BaseTokenAdjusterConfig { diff --git a/core/lib/protobuf_config/src/base_token_adjuster.rs b/core/lib/protobuf_config/src/base_token_adjuster.rs index 9afe730b2add..f19f36a84a4d 100644 --- a/core/lib/protobuf_config/src/base_token_adjuster.rs +++ b/core/lib/protobuf_config/src/base_token_adjuster.rs @@ -1,8 +1,5 @@ -use std::num::NonZeroU64; - -use anyhow::Context as _; use zksync_config::configs::{self}; -use zksync_protobuf::{required, ProtoRepr}; +use zksync_protobuf::ProtoRepr; use crate::proto::base_token_adjuster as proto; @@ -12,22 +9,12 @@ impl ProtoRepr for proto::BaseTokenAdjuster { fn read(&self) -> anyhow::Result { Ok(configs::base_token_adjuster::BaseTokenAdjusterConfig { price_polling_interval_ms: self.price_polling_interval_ms, - initial_numerator: NonZeroU64::new( - *required(&self.initial_numerator).context("Missing initial_numerator")?, - ) - .expect("initial_numerator must be non-zero"), - initial_denominator: NonZeroU64::new( - *required(&self.initial_denominator).context("Missing initial_denominator")?, - ) - .expect("initial_denominator must be non-zero"), }) } fn build(this: &Self::Type) -> Self { Self { price_polling_interval_ms: this.price_polling_interval_ms, - initial_numerator: Some(this.initial_numerator.get()), - initial_denominator: Some(this.initial_denominator.get()), } } } diff --git a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto index 35c578d9c4e6..67e97dd14cda 100644 --- a/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto +++ b/core/lib/protobuf_config/src/proto/config/base_token_adjuster.proto @@ -4,6 +4,4 @@ package zksync.config.base_token_adjuster; message BaseTokenAdjuster { optional uint64 price_polling_interval_ms = 1; - optional uint64 initial_numerator = 2; - optional uint64 initial_denominator = 3; } diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_fetcher.rs index 3ce1b158a7dd..1cb0c01a55c1 100644 --- a/core/node/base_token_adjuster/src/base_token_fetcher.rs +++ b/core/node/base_token_adjuster/src/base_token_fetcher.rs @@ -1,34 +1,38 @@ use std::{fmt::Debug, num::NonZeroU64, time::Duration}; +use anyhow::Context; +use async_trait::async_trait; use tokio::{sync::watch, time::sleep}; -use zksync_config::BaseTokenAdjusterConfig; -use zksync_dal::{ConnectionPool, Core, CoreDal}; +use zksync_dal::{ConnectionPool, Core, CoreDal, DalError}; use zksync_types::{base_token_ratio::BaseTokenRatio, fee_model::BaseTokenConversionRatio}; const CACHE_UPDATE_INTERVAL: Duration = Duration::from_millis(500); +#[async_trait] +pub trait BaseTokenFetcher: Debug + Send + Sync { + fn get_conversion_ratio(&self) -> BaseTokenConversionRatio; +} + #[derive(Debug, Clone)] -pub struct BaseTokenFetcher { - pub pool: Option>, +pub struct DBBaseTokenFetcher { + pub pool: ConnectionPool, pub latest_ratio: BaseTokenConversionRatio, - pub config: BaseTokenAdjusterConfig, } -impl BaseTokenFetcher { - pub fn new(pool: Option>, config: BaseTokenAdjusterConfig) -> Self { +impl DBBaseTokenFetcher { + pub async fn new(pool: ConnectionPool) -> anyhow::Result { + let latest_storage_ratio = retry_get_latest_price(pool.clone()).await?; + + // TODO(PE-129): Implement latest ratio usability logic. let latest_ratio = BaseTokenConversionRatio { - numerator: config.initial_numerator, - denominator: config.initial_denominator, + numerator: latest_storage_ratio.numerator, + denominator: latest_storage_ratio.denominator, }; tracing::debug!( "Starting the base token fetcher with conversion ratio: {:?}", latest_ratio ); - Self { - pool, - latest_ratio, - config, - } + Ok(Self { pool, latest_ratio }) } pub async fn run(&mut self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { @@ -40,7 +44,9 @@ impl BaseTokenFetcher { _ = stop_receiver.changed() => break, } - let latest_storage_ratio = self.retry_get_latest_price().await?; + let latest_storage_ratio = get_latest_price(self.pool.clone()) + .await? + .expect("No latest base token ratio found"); // TODO(PE-129): Implement latest ratio usability logic. self.latest_ratio = BaseTokenConversionRatio { @@ -52,72 +58,89 @@ impl BaseTokenFetcher { tracing::info!("Stop signal received, base_token_fetcher is shutting down"); Ok(()) } +} + +async fn get_latest_price(pool: ConnectionPool) -> anyhow::Result> { + let mut conn = pool + .connection_tagged("db_base_token_fetcher") + .await + .context("Failed to obtain connection to the database")?; - async fn retry_get_latest_price(&self) -> anyhow::Result { - let sleep_duration = Duration::from_secs(1); - let max_retries = 5; // should be enough time to allow fetching from external APIs & updating the DB upon init - let mut attempts = 1; - - loop { - let mut conn = self - .pool - .as_ref() - .expect("Connection pool is not set") - .connection_tagged("base_token_fetcher") - .await - .expect("Failed to obtain connection to the database"); - - let dal_result = conn.base_token_dal().get_latest_ratio().await; - - drop(conn); // Don't sleep with your connections. - - match dal_result { - Ok(Some(last_storage_price)) => { - return Ok(last_storage_price); - } - Ok(None) if attempts <= max_retries => { - tracing::warn!( - "Attempt {}/{} found no latest base token ratio. Retrying in {} seconds...", - attempts, - max_retries, - sleep_duration.as_secs() - ); - sleep(sleep_duration).await; - attempts += 1; - } - Ok(None) => { - anyhow::bail!( - "No latest base token ratio found after {} attempts", - max_retries - ); - } - Err(err) => { - anyhow::bail!("Failed to get latest base token ratio: {:?}", err); - } + conn.base_token_dal() + .get_latest_ratio() + .await + .map_err(DalError::generalize) +} + +async fn retry_get_latest_price(pool: ConnectionPool) -> anyhow::Result { + let sleep_duration = Duration::from_secs(1); + let max_retries = 5; // should be enough time to allow fetching from external APIs & updating the DB upon init + let mut attempts = 1; + + loop { + match get_latest_price(pool.clone()).await { + Ok(Some(last_storage_price)) => { + return Ok(last_storage_price); + } + Ok(None) if attempts <= max_retries => { + tracing::warn!( + "Attempt {}/{} found no latest base token ratio. Retrying in {} seconds...", + attempts, + max_retries, + sleep_duration.as_secs() + ); + sleep(sleep_duration).await; + attempts += 1; + } + Ok(None) => { + anyhow::bail!( + "No latest base token ratio found after {} attempts", + max_retries + ); + } + Err(err) => { + anyhow::bail!( + "Failed to get latest base token ratio with DAL error: {:?}", + err + ); } } } +} - // TODO(PE-129): Implement latest ratio usability logic. - pub fn get_conversion_ratio(&self) -> BaseTokenConversionRatio { +#[async_trait] +impl BaseTokenFetcher for DBBaseTokenFetcher { + fn get_conversion_ratio(&self) -> BaseTokenConversionRatio { self.latest_ratio } } -// Default impl for a no-op BaseTokenFetcher (conversion ratio is always 1:1). -impl Default for BaseTokenFetcher { +// Struct for a no-op BaseTokenFetcher (conversion ratio is either always 1:1 or a forced ratio). +#[derive(Debug, Clone)] +pub struct NoOpFetcher { + pub latest_ratio: BaseTokenConversionRatio, +} + +impl NoOpFetcher { + pub fn new(latest_ratio: BaseTokenConversionRatio) -> Self { + Self { latest_ratio } + } +} + +impl Default for NoOpFetcher { fn default() -> Self { Self { - pool: None, latest_ratio: BaseTokenConversionRatio { numerator: NonZeroU64::new(1).unwrap(), denominator: NonZeroU64::new(1).unwrap(), }, - config: BaseTokenAdjusterConfig { - price_polling_interval_ms: None, - initial_numerator: NonZeroU64::new(1).unwrap(), - initial_denominator: NonZeroU64::new(1).unwrap(), - }, } } } + +#[async_trait] +impl BaseTokenFetcher for NoOpFetcher { + fn get_conversion_ratio(&self) -> BaseTokenConversionRatio { + self.latest_ratio + } +} diff --git a/core/node/base_token_adjuster/src/lib.rs b/core/node/base_token_adjuster/src/lib.rs index 8ec8befbfbf7..902893fa9708 100644 --- a/core/node/base_token_adjuster/src/lib.rs +++ b/core/node/base_token_adjuster/src/lib.rs @@ -1,4 +1,7 @@ -pub use self::{base_token_adjuster::BaseTokenAdjuster, base_token_fetcher::BaseTokenFetcher}; +pub use self::{ + base_token_adjuster::BaseTokenAdjuster, + base_token_fetcher::{BaseTokenFetcher, DBBaseTokenFetcher, NoOpFetcher}, +}; mod base_token_adjuster; mod base_token_fetcher; diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index c41d210afc3d..175453c581ff 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -61,7 +61,7 @@ impl dyn BatchFeeModelInputProvider { #[derive(Debug)] pub struct MainNodeFeeInputProvider { provider: Arc, - base_token_adjuster: Arc, + base_token_fetcher: Arc, config: FeeModelConfig, } @@ -77,7 +77,7 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { config, self.provider.estimate_effective_gas_price(), self.provider.estimate_effective_pubdata_price(), - self.base_token_adjuster.get_conversion_ratio(), + self.base_token_fetcher.get_conversion_ratio(), ))), } } @@ -86,12 +86,12 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { impl MainNodeFeeInputProvider { pub fn new( provider: Arc, - base_token_fetcher: Arc, + base_token_fetcher: Arc, config: FeeModelConfig, ) -> Self { Self { provider, - base_token_adjuster: base_token_fetcher, + base_token_fetcher, config, } } @@ -250,6 +250,7 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { mod tests { use std::num::NonZeroU64; + use zksync_base_token_adjuster::NoOpFetcher; use zksync_config::{ configs::{base_token_adjuster::BaseTokenAdjusterConfig, eth_sender::PubdataSendingMode}, GasAdjusterConfig, @@ -604,14 +605,7 @@ mod tests { let gas_adjuster = setup_gas_adjuster(case.input_l1_gas_price, case.input_l1_pubdata_price).await; - let base_token_fetcher = BaseTokenFetcher::new( - None, - BaseTokenAdjusterConfig { - price_polling_interval_ms: None, - initial_numerator: case.conversion_ratio.numerator, - initial_denominator: case.conversion_ratio.denominator, - }, - ); + let base_token_fetcher = NoOpFetcher::new(case.conversion_ratio); let fee_provider = setup_fee_provider( gas_adjuster, @@ -688,7 +682,7 @@ mod tests { // Helper function to setup the MainNodeFeeInputProvider. fn setup_fee_provider( gas_adjuster: GasAdjuster, - base_token_fetcher: BaseTokenFetcher, + base_token_fetcher: DBBaseTokenFetcher, minimal_l2_gas_price: u64, ) -> MainNodeFeeInputProvider { let config = FeeModelConfig::V2(FeeModelConfigV2 { diff --git a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs index 7b033d9aa0f7..b6193858b7fb 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs @@ -1,7 +1,6 @@ use std::sync::Arc; -use zksync_base_token_adjuster::BaseTokenFetcher; -use zksync_config::BaseTokenAdjusterConfig; +use zksync_base_token_adjuster::DBBaseTokenFetcher; use crate::{ implementations::resources::{ @@ -30,15 +29,7 @@ use crate::{ /// /// - `BaseTokenFetcher` #[derive(Debug)] -pub struct BaseTokenFetcherLayer { - config: BaseTokenAdjusterConfig, -} - -impl BaseTokenFetcherLayer { - pub fn new(config: BaseTokenAdjusterConfig) -> Self { - Self { config } - } -} +pub struct BaseTokenFetcherLayer; #[async_trait::async_trait] impl WiringLayer for BaseTokenFetcherLayer { @@ -50,7 +41,7 @@ impl WiringLayer for BaseTokenFetcherLayer { let replica_pool_resource = context.get_resource::>().await?; let replica_pool = replica_pool_resource.get().await.unwrap(); - let fetcher = BaseTokenFetcher::new(Some(replica_pool), self.config); + let fetcher = DBBaseTokenFetcher::new(replica_pool).await?; context.insert_resource(BaseTokenFetcherResource(Arc::new(fetcher.clone())))?; context.add_task(Box::new(fetcher)); @@ -60,7 +51,7 @@ impl WiringLayer for BaseTokenFetcherLayer { } #[async_trait::async_trait] -impl Task for BaseTokenFetcher { +impl Task for DBBaseTokenFetcher { fn id(&self) -> TaskId { "base_token_fetcher".into() } diff --git a/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs b/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs index 1f8493be55a4..be8079eda739 100644 --- a/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs +++ b/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs @@ -1,12 +1,18 @@ use std::sync::Arc; -use zksync_base_token_adjuster::BaseTokenFetcher; +use zksync_base_token_adjuster::{BaseTokenFetcher, NoOpFetcher}; use crate::resource::Resource; /// A resource that provides [`BaseTokenFetcher`] implementation to the service. -#[derive(Clone, Default)] -pub struct BaseTokenFetcherResource(pub Arc); +#[derive(Clone)] +pub struct BaseTokenFetcherResource(pub Arc); + +impl Default for BaseTokenFetcherResource { + fn default() -> Self { + Self(Arc::new(NoOpFetcher::default())) + } +} impl Resource for BaseTokenFetcherResource { fn name() -> String { diff --git a/core/node/state_keeper/src/io/tests/tester.rs b/core/node/state_keeper/src/io/tests/tester.rs index 9a8a298f197b..d291e1935b7d 100644 --- a/core/node/state_keeper/src/io/tests/tester.rs +++ b/core/node/state_keeper/src/io/tests/tester.rs @@ -2,7 +2,7 @@ use std::{slice, sync::Arc, time::Duration}; -use zksync_base_token_adjuster::BaseTokenFetcher; +use zksync_base_token_adjuster::NoOpFetcher; use zksync_config::{ configs::{chain::StateKeeperConfig, eth_sender::PubdataSendingMode, wallets::Wallets}, GasAdjusterConfig, @@ -88,7 +88,7 @@ impl Tester { MainNodeFeeInputProvider::new( gas_adjuster, - Arc::new(BaseTokenFetcher::default()), + Arc::new(NoOpFetcher::default()), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), @@ -107,7 +107,7 @@ impl Tester { let gas_adjuster = Arc::new(self.create_gas_adjuster().await); let batch_fee_input_provider = MainNodeFeeInputProvider::new( gas_adjuster, - Arc::new(BaseTokenFetcher::default()), + Arc::new(NoOpFetcher::default()), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), diff --git a/etc/env/base/base_token_adjuster.toml b/etc/env/base/base_token_adjuster.toml index 0f24773ced87..100da3b7224f 100644 --- a/etc/env/base/base_token_adjuster.toml +++ b/etc/env/base/base_token_adjuster.toml @@ -4,8 +4,3 @@ # How often to poll external price feeds for the base token price. price_polling_interval_ms = "30000" - -# The numerator and the denominator make up the BaseToken/ETH conversion ratio. Upon initialization, -# the ratio is set to these configured values to avoid loss of liveness while we wait for external APIs. -initial_numerator = "100" -initial_denominator = "1" diff --git a/etc/env/file_based/general.yaml b/etc/env/file_based/general.yaml index 2e476e3ae0b1..4a258a7cd99d 100644 --- a/etc/env/file_based/general.yaml +++ b/etc/env/file_based/general.yaml @@ -297,8 +297,6 @@ prover_group: aggregation_round: 1 base_token_adjuster: price_polling_interval_ms: 30000 - initial_numerator: 100 - initial_denominator: 1 house_keeper: l1_batch_metrics_reporting_interval_ms: 10000 From 334a1be8f488762f94a43898fda02fdf1db3eb70 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Mon, 1 Jul 2024 19:48:43 +0300 Subject: [PATCH 37/48] start the adjuster from the l1 gas layer --- core/bin/zksync_server/src/node_builder.rs | 17 ++++---- .../src/base_token_fetcher.rs | 10 ++--- core/node/fee_model/src/lib.rs | 40 +++++++------------ 3 files changed, 28 insertions(+), 39 deletions(-) diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index f7a99ceccc9f..e163960be7a6 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -145,6 +145,8 @@ impl MainNodeBuilder { fn add_sequencer_l1_gas_layer(mut self) -> anyhow::Result { // Ensure the BaseTokenFetcherResource is inserted if the base token is not ETH. if self.contracts_config.base_token_addr != SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { + let config = try_load_config!(self.configs.base_token_adjuster); + self.node.add_layer(BaseTokenAdjusterLayer::new(config)); self.node.add_layer(BaseTokenFetcherLayer {}); } @@ -463,12 +465,12 @@ impl MainNodeBuilder { Ok(self) } - fn add_base_token_adjuster_layer(mut self) -> anyhow::Result { - let config = try_load_config!(self.configs.base_token_adjuster); - self.node.add_layer(BaseTokenAdjusterLayer::new(config)); - - Ok(self) - } + // fn add_base_token_adjuster_layer(mut self) -> anyhow::Result { + // let config = try_load_config!(self.configs.base_token_adjuster); + // self.node.add_layer(BaseTokenAdjusterLayer::new(config)); + // + // Ok(self) + // } pub fn build(mut self, mut components: Vec) -> anyhow::Result { // Add "base" layers (resources and helper tasks). @@ -558,7 +560,8 @@ impl MainNodeBuilder { self = self.add_vm_runner_protective_reads_layer()?; } Component::BaseTokenAdjuster => { - self = self.add_base_token_adjuster_layer()?; + // self = self.add_base_token_adjuster_layer()?; + // Do nothing, will be handled by the `SequencerL1Gas` component. } } } diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_fetcher.rs index 1cb0c01a55c1..9bc9541340c7 100644 --- a/core/node/base_token_adjuster/src/base_token_fetcher.rs +++ b/core/node/base_token_adjuster/src/base_token_fetcher.rs @@ -61,12 +61,10 @@ impl DBBaseTokenFetcher { } async fn get_latest_price(pool: ConnectionPool) -> anyhow::Result> { - let mut conn = pool - .connection_tagged("db_base_token_fetcher") + pool.connection_tagged("db_base_token_fetcher") .await - .context("Failed to obtain connection to the database")?; - - conn.base_token_dal() + .context("Failed to obtain connection to the database")? + .base_token_dal() .get_latest_ratio() .await .map_err(DalError::generalize) @@ -74,7 +72,7 @@ async fn get_latest_price(pool: ConnectionPool) -> anyhow::Result) -> anyhow::Result { let sleep_duration = Duration::from_secs(1); - let max_retries = 5; // should be enough time to allow fetching from external APIs & updating the DB upon init + let max_retries = 6; // should be enough time to allow fetching from external APIs & updating the DB upon init let mut attempts = 1; loop { diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 175453c581ff..6b1af113adc0 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -251,10 +251,7 @@ mod tests { use std::num::NonZeroU64; use zksync_base_token_adjuster::NoOpFetcher; - use zksync_config::{ - configs::{base_token_adjuster::BaseTokenAdjusterConfig, eth_sender::PubdataSendingMode}, - GasAdjusterConfig, - }; + use zksync_config::{configs::eth_sender::PubdataSendingMode, GasAdjusterConfig}; use zksync_eth_client::{clients::MockEthereum, BaseFees}; use zksync_types::{commitment::L1BatchCommitmentMode, fee_model::BaseTokenConversionRatio}; @@ -607,10 +604,19 @@ mod tests { let base_token_fetcher = NoOpFetcher::new(case.conversion_ratio); - let fee_provider = setup_fee_provider( - gas_adjuster, - base_token_fetcher, - case.input_minimal_l2_gas_price, + let config = FeeModelConfig::V2(FeeModelConfigV2 { + minimal_l2_gas_price: case.input_minimal_l2_gas_price, + compute_overhead_part: 1.0, + pubdata_overhead_part: 1.0, + batch_overhead_l1_gas: 1, + max_gas_per_batch: 1, + max_pubdata_per_batch: 1, + }); + + let fee_provider = MainNodeFeeInputProvider::new( + Arc::new(gas_adjuster), + Arc::new(base_token_fetcher), + config, ); let fee_params = fee_provider @@ -678,22 +684,4 @@ mod tests { .await .expect("Failed to create GasAdjuster") } - - // Helper function to setup the MainNodeFeeInputProvider. - fn setup_fee_provider( - gas_adjuster: GasAdjuster, - base_token_fetcher: DBBaseTokenFetcher, - minimal_l2_gas_price: u64, - ) -> MainNodeFeeInputProvider { - let config = FeeModelConfig::V2(FeeModelConfigV2 { - minimal_l2_gas_price, - compute_overhead_part: 1.0, - pubdata_overhead_part: 1.0, - batch_overhead_l1_gas: 1, - max_gas_per_batch: 1, - max_pubdata_per_batch: 1, - }); - - MainNodeFeeInputProvider::new(Arc::new(gas_adjuster), Arc::new(base_token_fetcher), config) - } } From 430aac01a15d56254ba6fb03cf38d2eb6191ac2f Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Mon, 1 Jul 2024 20:21:41 +0300 Subject: [PATCH 38/48] adjusting to framework changes --- .../src/implementations/layers/base_token_adjuster.rs | 4 ++-- .../src/implementations/layers/base_token_fetcher.rs | 4 ++-- core/node/node_framework/src/implementations/layers/l1_gas.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index 3cf5c8749b17..6d04a0477a5b 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -38,12 +38,12 @@ impl WiringLayer for BaseTokenAdjusterLayer { } async fn wire(self: Box, mut context: ServiceContext<'_>) -> Result<(), WiringError> { - let master_pool_resource = context.get_resource::>().await?; + let master_pool_resource = context.get_resource::>()?; let master_pool = master_pool_resource.get().await?; let adjuster = BaseTokenAdjuster::new(master_pool, self.config); - context.add_task(Box::new(adjuster)); + context.add_task(adjuster); Ok(()) } diff --git a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs index b6193858b7fb..63cece18ee5a 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs @@ -38,13 +38,13 @@ impl WiringLayer for BaseTokenFetcherLayer { } async fn wire(self: Box, mut context: ServiceContext<'_>) -> Result<(), WiringError> { - let replica_pool_resource = context.get_resource::>().await?; + let replica_pool_resource = context.get_resource::>()?; let replica_pool = replica_pool_resource.get().await.unwrap(); let fetcher = DBBaseTokenFetcher::new(replica_pool).await?; context.insert_resource(BaseTokenFetcherResource(Arc::new(fetcher.clone())))?; - context.add_task(Box::new(fetcher)); + context.add_task(fetcher); Ok(()) } diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index 3ee670da0330..4f4af39aa997 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -75,11 +75,11 @@ impl WiringLayer for SequencerL1GasLayer { .context("GasAdjuster::new()")?; let gas_adjuster = Arc::new(adjuster); - let BaseTokenFetcherResource(fetcher) = context.get_resource_or_default().await; + let fetcher = context.get_resource_or_default::(); let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( gas_adjuster.clone(), - fetcher, + fetcher.0.clone(), FeeModelConfig::from_state_keeper_config(&self.state_keeper_config), )); context.insert_resource(FeeInputResource(batch_fee_input_provider))?; From ac0bc960badc50619f86d5b738a76c9f7b452834 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Mon, 1 Jul 2024 21:03:19 +0300 Subject: [PATCH 39/48] adjuster constructer async and inserts an initial ratio --- .../src/base_token_adjuster.rs | 15 +++++++++++++-- .../implementations/layers/base_token_adjuster.rs | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index be8edf0ef261..ef05ce2fb9cf 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -14,8 +14,19 @@ pub struct BaseTokenAdjuster { } impl BaseTokenAdjuster { - pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { - Self { pool, config } + pub async fn new( + pool: ConnectionPool, + config: BaseTokenAdjusterConfig, + ) -> anyhow::Result { + let base_token_adjuster = Self { + pool: pool.clone(), + config, + }; + let initial_ratio = base_token_adjuster.fetch_new_ratio().await?; + base_token_adjuster + .persist_ratio(&initial_ratio, &pool) + .await?; + Ok(base_token_adjuster) } /// Main loop for the base token adjuster. diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index 6d04a0477a5b..b22d5a88e361 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -41,7 +41,7 @@ impl WiringLayer for BaseTokenAdjusterLayer { let master_pool_resource = context.get_resource::>()?; let master_pool = master_pool_resource.get().await?; - let adjuster = BaseTokenAdjuster::new(master_pool, self.config); + let adjuster = BaseTokenAdjuster::new(master_pool, self.config).await?; context.add_task(adjuster); From 9647fc2154c8041edb9883d84aec67ae6c0aa520 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 2 Jul 2024 09:58:06 +0300 Subject: [PATCH 40/48] rely on db ratio wout retry. start adjuster layer not from l1 gas layer --- core/bin/zksync_server/src/node_builder.rs | 17 +++--- .../src/base_token_adjuster.rs | 15 +---- .../src/base_token_fetcher.rs | 59 ++++++------------- .../layers/base_token_adjuster.rs | 2 +- .../layers/base_token_fetcher.rs | 2 +- 5 files changed, 28 insertions(+), 67 deletions(-) diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index e163960be7a6..f7a99ceccc9f 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -145,8 +145,6 @@ impl MainNodeBuilder { fn add_sequencer_l1_gas_layer(mut self) -> anyhow::Result { // Ensure the BaseTokenFetcherResource is inserted if the base token is not ETH. if self.contracts_config.base_token_addr != SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { - let config = try_load_config!(self.configs.base_token_adjuster); - self.node.add_layer(BaseTokenAdjusterLayer::new(config)); self.node.add_layer(BaseTokenFetcherLayer {}); } @@ -465,12 +463,12 @@ impl MainNodeBuilder { Ok(self) } - // fn add_base_token_adjuster_layer(mut self) -> anyhow::Result { - // let config = try_load_config!(self.configs.base_token_adjuster); - // self.node.add_layer(BaseTokenAdjusterLayer::new(config)); - // - // Ok(self) - // } + fn add_base_token_adjuster_layer(mut self) -> anyhow::Result { + let config = try_load_config!(self.configs.base_token_adjuster); + self.node.add_layer(BaseTokenAdjusterLayer::new(config)); + + Ok(self) + } pub fn build(mut self, mut components: Vec) -> anyhow::Result { // Add "base" layers (resources and helper tasks). @@ -560,8 +558,7 @@ impl MainNodeBuilder { self = self.add_vm_runner_protective_reads_layer()?; } Component::BaseTokenAdjuster => { - // self = self.add_base_token_adjuster_layer()?; - // Do nothing, will be handled by the `SequencerL1Gas` component. + self = self.add_base_token_adjuster_layer()?; } } } diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index ef05ce2fb9cf..be8edf0ef261 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -14,19 +14,8 @@ pub struct BaseTokenAdjuster { } impl BaseTokenAdjuster { - pub async fn new( - pool: ConnectionPool, - config: BaseTokenAdjusterConfig, - ) -> anyhow::Result { - let base_token_adjuster = Self { - pool: pool.clone(), - config, - }; - let initial_ratio = base_token_adjuster.fetch_new_ratio().await?; - base_token_adjuster - .persist_ratio(&initial_ratio, &pool) - .await?; - Ok(base_token_adjuster) + pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { + Self { pool, config } } /// Main loop for the base token adjuster. diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_fetcher.rs index 9bc9541340c7..963158b1b190 100644 --- a/core/node/base_token_adjuster/src/base_token_fetcher.rs +++ b/core/node/base_token_adjuster/src/base_token_fetcher.rs @@ -2,7 +2,7 @@ use std::{fmt::Debug, num::NonZeroU64, time::Duration}; use anyhow::Context; use async_trait::async_trait; -use tokio::{sync::watch, time::sleep}; +use tokio::sync::watch; use zksync_dal::{ConnectionPool, Core, CoreDal, DalError}; use zksync_types::{base_token_ratio::BaseTokenRatio, fee_model::BaseTokenConversionRatio}; @@ -21,13 +21,24 @@ pub struct DBBaseTokenFetcher { impl DBBaseTokenFetcher { pub async fn new(pool: ConnectionPool) -> anyhow::Result { - let latest_storage_ratio = retry_get_latest_price(pool.clone()).await?; + let latest_ratio = match get_latest_price(pool.clone()).await { + Ok(Some(latest_storage_price)) => BaseTokenConversionRatio { + numerator: latest_storage_price.numerator, + denominator: latest_storage_price.denominator, + }, + Ok(None) => { + // TODO(PE-136): Insert initial ratio from genesis. + // Though the DB should be populated very soon after the server starts, it is possible + // to have no ratios in the DB right after genesis. Having initial ratios in the DB + // from the genesis stage will eliminate this possibility. + tracing::error!("No latest price found in the database. Using default ratio."); + BaseTokenConversionRatio::default() + } + Err(err) => anyhow::bail!("Failed to get latest base token ratio: {:?}", err), + }; // TODO(PE-129): Implement latest ratio usability logic. - let latest_ratio = BaseTokenConversionRatio { - numerator: latest_storage_ratio.numerator, - denominator: latest_storage_ratio.denominator, - }; + tracing::debug!( "Starting the base token fetcher with conversion ratio: {:?}", latest_ratio @@ -70,42 +81,6 @@ async fn get_latest_price(pool: ConnectionPool) -> anyhow::Result) -> anyhow::Result { - let sleep_duration = Duration::from_secs(1); - let max_retries = 6; // should be enough time to allow fetching from external APIs & updating the DB upon init - let mut attempts = 1; - - loop { - match get_latest_price(pool.clone()).await { - Ok(Some(last_storage_price)) => { - return Ok(last_storage_price); - } - Ok(None) if attempts <= max_retries => { - tracing::warn!( - "Attempt {}/{} found no latest base token ratio. Retrying in {} seconds...", - attempts, - max_retries, - sleep_duration.as_secs() - ); - sleep(sleep_duration).await; - attempts += 1; - } - Ok(None) => { - anyhow::bail!( - "No latest base token ratio found after {} attempts", - max_retries - ); - } - Err(err) => { - anyhow::bail!( - "Failed to get latest base token ratio with DAL error: {:?}", - err - ); - } - } - } -} - #[async_trait] impl BaseTokenFetcher for DBBaseTokenFetcher { fn get_conversion_ratio(&self) -> BaseTokenConversionRatio { diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs index b22d5a88e361..6d04a0477a5b 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs @@ -41,7 +41,7 @@ impl WiringLayer for BaseTokenAdjusterLayer { let master_pool_resource = context.get_resource::>()?; let master_pool = master_pool_resource.get().await?; - let adjuster = BaseTokenAdjuster::new(master_pool, self.config).await?; + let adjuster = BaseTokenAdjuster::new(master_pool, self.config); context.add_task(adjuster); diff --git a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs index 63cece18ee5a..0b8df4aedd4e 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs @@ -23,7 +23,7 @@ use crate::{ /// ## Requests resources /// -/// - `PoolResource` +/// - `PoolResource` /// /// ## Adds tasks /// From 7a9614d5fbd82ede0a72864ef112b868fe0a1fca Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 2 Jul 2024 11:12:53 +0300 Subject: [PATCH 41/48] move match into get_latest_price --- .../src/base_token_fetcher.rs | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_fetcher.rs index 963158b1b190..d94a95a4124c 100644 --- a/core/node/base_token_adjuster/src/base_token_fetcher.rs +++ b/core/node/base_token_adjuster/src/base_token_fetcher.rs @@ -3,8 +3,8 @@ use std::{fmt::Debug, num::NonZeroU64, time::Duration}; use anyhow::Context; use async_trait::async_trait; use tokio::sync::watch; -use zksync_dal::{ConnectionPool, Core, CoreDal, DalError}; -use zksync_types::{base_token_ratio::BaseTokenRatio, fee_model::BaseTokenConversionRatio}; +use zksync_dal::{ConnectionPool, Core, CoreDal}; +use zksync_types::fee_model::BaseTokenConversionRatio; const CACHE_UPDATE_INTERVAL: Duration = Duration::from_millis(500); @@ -21,21 +21,7 @@ pub struct DBBaseTokenFetcher { impl DBBaseTokenFetcher { pub async fn new(pool: ConnectionPool) -> anyhow::Result { - let latest_ratio = match get_latest_price(pool.clone()).await { - Ok(Some(latest_storage_price)) => BaseTokenConversionRatio { - numerator: latest_storage_price.numerator, - denominator: latest_storage_price.denominator, - }, - Ok(None) => { - // TODO(PE-136): Insert initial ratio from genesis. - // Though the DB should be populated very soon after the server starts, it is possible - // to have no ratios in the DB right after genesis. Having initial ratios in the DB - // from the genesis stage will eliminate this possibility. - tracing::error!("No latest price found in the database. Using default ratio."); - BaseTokenConversionRatio::default() - } - Err(err) => anyhow::bail!("Failed to get latest base token ratio: {:?}", err), - }; + let latest_ratio = get_latest_price(pool.clone()).await?; // TODO(PE-129): Implement latest ratio usability logic. @@ -55,9 +41,7 @@ impl DBBaseTokenFetcher { _ = stop_receiver.changed() => break, } - let latest_storage_ratio = get_latest_price(self.pool.clone()) - .await? - .expect("No latest base token ratio found"); + let latest_storage_ratio = get_latest_price(self.pool.clone()).await?; // TODO(PE-129): Implement latest ratio usability logic. self.latest_ratio = BaseTokenConversionRatio { @@ -71,14 +55,30 @@ impl DBBaseTokenFetcher { } } -async fn get_latest_price(pool: ConnectionPool) -> anyhow::Result> { - pool.connection_tagged("db_base_token_fetcher") +async fn get_latest_price(pool: ConnectionPool) -> anyhow::Result { + let latest_storage_ratio = pool + .connection_tagged("db_base_token_fetcher") .await .context("Failed to obtain connection to the database")? .base_token_dal() .get_latest_ratio() - .await - .map_err(DalError::generalize) + .await; + + match latest_storage_ratio { + Ok(Some(latest_storage_price)) => Ok(BaseTokenConversionRatio { + numerator: latest_storage_price.numerator, + denominator: latest_storage_price.denominator, + }), + Ok(None) => { + // TODO(PE-136): Insert initial ratio from genesis. + // Though the DB should be populated very soon after the server starts, it is possible + // to have no ratios in the DB right after genesis. Having initial ratios in the DB + // from the genesis stage will eliminate this possibility. + tracing::error!("No latest price found in the database. Using default ratio."); + Ok(BaseTokenConversionRatio::default()) + } + Err(err) => anyhow::bail!("Failed to get latest base token ratio: {:?}", err), + } } #[async_trait] From 9344f9bb3934d92e25c3ca717ceceab5254c1a91 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 2 Jul 2024 14:31:26 +0300 Subject: [PATCH 42/48] PR review --- Cargo.lock | 1 - Cargo.toml | 1 + core/bin/external_node/src/config/mod.rs | 2 +- core/bin/zksync_server/src/node_builder.rs | 2 +- .../config/src/configs/base_token_adjuster.rs | 20 ++++--- core/lib/config/src/configs/contracts.rs | 4 +- .../src/models/storage_base_token_ratio.rs | 2 +- core/lib/env_config/src/contracts.rs | 2 +- core/lib/external_price_api/Cargo.toml | 3 +- .../src/base_token_adjuster.rs | 6 +- core/lib/protobuf_config/src/contracts.rs | 7 ++- core/lib/types/src/base_token_ratio.rs | 4 +- core/lib/types/src/fee_model.rs | 9 +-- .../web3/backend_jsonrpsee/namespaces/zks.rs | 4 +- .../api_server/src/web3/namespaces/zks.rs | 11 ++-- core/node/api_server/src/web3/state.rs | 2 +- core/node/base_token_adjuster/README.md | 10 +++- .../src/base_token_adjuster.rs | 5 +- .../src/base_token_fetcher.rs | 59 ++++++++++--------- .../src/l1_gas_price/main_node_fetcher.rs | 4 +- core/node/fee_model/src/lib.rs | 28 ++++----- 21 files changed, 100 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c959e6f697cd..6a21cef90754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8601,7 +8601,6 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "serde", "zksync_config", "zksync_types", ] diff --git a/Cargo.toml b/Cargo.toml index 87b8f3bec51f..37be55590de3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -245,6 +245,7 @@ zksync_types = { path = "core/lib/types" } zksync_utils = { path = "core/lib/utils" } zksync_web3_decl = { path = "core/lib/web3_decl" } zksync_crypto_primitives = { path = "core/lib/crypto_primitives" } +zksync_external_price_api = { path = "core/lib/external_price_api" } # Framework and components zksync_node_framework = { path = "core/node/node_framework" } diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index a8288b1f5183..9c4e9657084f 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -1349,7 +1349,7 @@ impl From<&ExternalNodeConfig> for InternalApiConfig { l2_testnet_paymaster_addr: config.remote.l2_testnet_paymaster_addr, req_entities_limit: config.optional.req_entities_limit, fee_history_limit: config.optional.fee_history_limit, - base_token_address: config.remote.base_token_addr, + base_token_address: Some(config.remote.base_token_addr), filters_disabled: config.optional.filters_disabled, dummy_verifier: config.remote.dummy_verifier, l1_batch_commit_data_generator_mode: config.remote.l1_batch_commit_data_generator_mode, diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index f7a99ceccc9f..bc7a9e9e0518 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -144,7 +144,7 @@ impl MainNodeBuilder { fn add_sequencer_l1_gas_layer(mut self) -> anyhow::Result { // Ensure the BaseTokenFetcherResource is inserted if the base token is not ETH. - if self.contracts_config.base_token_addr != SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { + if self.contracts_config.base_token_addr != Some(SHARED_BRIDGE_ETHER_TOKEN_ADDRESS) { self.node.add_layer(BaseTokenFetcherLayer {}); } diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 4b61ef3dc163..64f83cba610d 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use serde::Deserialize; // By default external APIs shall be polled every 30 seconds for a new price. @@ -8,14 +6,20 @@ pub const DEFAULT_INTERVAL_MS: u64 = 30_000; #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct BaseTokenAdjusterConfig { /// How often to fetch external APIs for a new ETH<->Base-Token price. - pub price_polling_interval_ms: Option, + #[serde(default = "BaseTokenAdjusterConfig::default_interval")] + pub price_polling_interval_ms: u64, } -impl BaseTokenAdjusterConfig { - pub fn price_polling_interval(&self) -> Duration { - match self.price_polling_interval_ms { - Some(interval) => Duration::from_millis(interval), - None => Duration::from_millis(DEFAULT_INTERVAL_MS), +impl Default for BaseTokenAdjusterConfig { + fn default() -> Self { + Self { + price_polling_interval_ms: Self::default_interval(), } } } + +impl BaseTokenAdjusterConfig { + pub fn default_interval() -> u64 { + DEFAULT_INTERVAL_MS + } +} diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index f763081ae8b8..a8226259e79a 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -39,7 +39,7 @@ pub struct ContractsConfig { pub ecosystem_contracts: Option, // Used by the RPC API and by the node builder in wiring the BaseTokenFetcher layer. If not set, // ETH is the chain's base token and the "ETH address" is SHARED_BRIDGE_ETHER_TOKEN_ADDRESS. - pub base_token_addr: Address, + pub base_token_addr: Option
, } impl ContractsConfig { @@ -58,7 +58,7 @@ impl ContractsConfig { l2_testnet_paymaster_addr: Some(Address::repeat_byte(0x11)), l1_multicall3_addr: Address::repeat_byte(0x12), governance_addr: Address::repeat_byte(0x13), - base_token_addr: Address::repeat_byte(0x14), + base_token_addr: Some(Address::repeat_byte(0x14)), ecosystem_contracts: Some(EcosystemContracts::for_tests()), } } diff --git a/core/lib/dal/src/models/storage_base_token_ratio.rs b/core/lib/dal/src/models/storage_base_token_ratio.rs index 996eae999588..f486aefd4085 100644 --- a/core/lib/dal/src/models/storage_base_token_ratio.rs +++ b/core/lib/dal/src/models/storage_base_token_ratio.rs @@ -19,7 +19,7 @@ pub struct StorageBaseTokenRatio { impl From for BaseTokenRatio { fn from(row: StorageBaseTokenRatio) -> BaseTokenRatio { BaseTokenRatio { - id: row.id, + id: row.id as u32, ratio_timestamp: row.ratio_timestamp.and_utc(), numerator: NonZeroU64::new(row.numerator.to_u64().expect("numerator is not u64")) .unwrap(), diff --git a/core/lib/env_config/src/contracts.rs b/core/lib/env_config/src/contracts.rs index c56f32017d22..ae5eb6f30c92 100644 --- a/core/lib/env_config/src/contracts.rs +++ b/core/lib/env_config/src/contracts.rs @@ -70,7 +70,7 @@ mod tests { state_transition_proxy_addr: addr("0xd90f1c081c6117241624e97cb6147257c3cb2097"), transparent_proxy_admin_addr: addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5"), }), - base_token_addr: SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, + base_token_addr: Some(SHARED_BRIDGE_ETHER_TOKEN_ADDRESS), } } diff --git a/core/lib/external_price_api/Cargo.toml b/core/lib/external_price_api/Cargo.toml index e1c4c99676c7..c75ff5851d75 100644 --- a/core/lib/external_price_api/Cargo.toml +++ b/core/lib/external_price_api/Cargo.toml @@ -10,8 +10,7 @@ keywords.workspace = true categories.workspace = true [dependencies] -serde = { workspace = true, features = ["derive"] } -async-trait = "0.1.74" +async-trait.workspace = true anyhow.workspace = true zksync_config.workspace = true diff --git a/core/lib/protobuf_config/src/base_token_adjuster.rs b/core/lib/protobuf_config/src/base_token_adjuster.rs index f19f36a84a4d..d8dea17daec0 100644 --- a/core/lib/protobuf_config/src/base_token_adjuster.rs +++ b/core/lib/protobuf_config/src/base_token_adjuster.rs @@ -8,13 +8,15 @@ impl ProtoRepr for proto::BaseTokenAdjuster { fn read(&self) -> anyhow::Result { Ok(configs::base_token_adjuster::BaseTokenAdjusterConfig { - price_polling_interval_ms: self.price_polling_interval_ms, + price_polling_interval_ms: self + .price_polling_interval_ms + .expect("price_polling_interval_ms"), }) } fn build(this: &Self::Type) -> Self { Self { - price_polling_interval_ms: this.price_polling_interval_ms, + price_polling_interval_ms: Some(this.price_polling_interval_ms), } } } diff --git a/core/lib/protobuf_config/src/contracts.rs b/core/lib/protobuf_config/src/contracts.rs index d891ba329004..4358aa4075a8 100644 --- a/core/lib/protobuf_config/src/contracts.rs +++ b/core/lib/protobuf_config/src/contracts.rs @@ -95,8 +95,11 @@ impl ProtoRepr for proto::Contracts { l1_multicall3_addr: required(&l1.multicall3_addr) .and_then(|x| parse_h160(x)) .context("l1_multicall3_addr")?, - base_token_addr: required(&l1.base_token_addr) - .and_then(|x| parse_h160(x)) + base_token_addr: l1 + .base_token_addr + .as_ref() + .map(|x| parse_h160(x)) + .transpose() .context("base_token_addr")?, }) } diff --git a/core/lib/types/src/base_token_ratio.rs b/core/lib/types/src/base_token_ratio.rs index f584522ee4d7..0782e67ab4b0 100644 --- a/core/lib/types/src/base_token_ratio.rs +++ b/core/lib/types/src/base_token_ratio.rs @@ -5,7 +5,7 @@ use chrono::{DateTime, Utc}; /// Represents the base token to ETH conversion ratio at a given point in time. #[derive(Debug, Clone)] pub struct BaseTokenRatio { - pub id: i64, + pub id: u32, pub ratio_timestamp: DateTime, pub numerator: NonZeroU64, pub denominator: NonZeroU64, @@ -17,6 +17,6 @@ pub struct BaseTokenRatio { pub struct BaseTokenAPIRatio { pub numerator: NonZeroU64, pub denominator: NonZeroU64, - // Either the timestamp of the quote or the timestamp of the request. + /// Either the timestamp of the quote or the timestamp of the request. pub ratio_timestamp: DateTime, } diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 85ae7ed2b3d3..38d785113e5f 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -294,10 +294,10 @@ impl FeeParamsV2 { converted_price_bd ); } else { - tracing::error!( - "Conversion to base token price failed: converted price is not a valid u64: {}. Using u64::MAX instead.", - converted_price_bd - ); + panic!( + "Conversion to base token price failed: converted price is not a valid u64: {}", + converted_price_bd + ); } u64::MAX } @@ -305,6 +305,7 @@ impl FeeParamsV2 { } } +/// The struct that represents the BaseToken<->ETH conversion ratio. #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct BaseTokenConversionRatio { pub numerator: NonZeroU64, diff --git a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs index 46dae003be70..45cb312dde6e 100644 --- a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs +++ b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs @@ -151,9 +151,7 @@ impl ZksNamespaceServer for ZksNamespace { } async fn get_fee_params(&self) -> RpcResult { - self.get_fee_params_impl() - .await - .map_err(|err| self.current_method().map_err(err)) + Ok(self.get_fee_params_impl()) } async fn get_batch_fee_input(&self) -> RpcResult { diff --git a/core/node/api_server/src/web3/namespaces/zks.rs b/core/node/api_server/src/web3/namespaces/zks.rs index 5db1f9320695..2b3fbbcd55ca 100644 --- a/core/node/api_server/src/web3/namespaces/zks.rs +++ b/core/node/api_server/src/web3/namespaces/zks.rs @@ -450,14 +450,12 @@ impl ZksNamespace { } #[tracing::instrument(skip(self))] - pub async fn get_fee_params_impl(&self) -> Result { - Ok(self - .state + pub fn get_fee_params_impl(&self) -> FeeParams { + self.state .tx_sender .0 .batch_fee_input_provider .get_fee_model_params() - .await?) } pub async fn get_protocol_version_impl( @@ -543,7 +541,10 @@ impl ZksNamespace { } pub fn get_base_token_l1_address_impl(&self) -> Result { - Ok(self.state.api_config.base_token_address) + self.state + .api_config + .base_token_address + .ok_or(Web3Error::MethodNotImplemented) } #[tracing::instrument(skip(self))] diff --git a/core/node/api_server/src/web3/state.rs b/core/node/api_server/src/web3/state.rs index f7a964f3cad1..276e0b6755e7 100644 --- a/core/node/api_server/src/web3/state.rs +++ b/core/node/api_server/src/web3/state.rs @@ -108,7 +108,7 @@ pub struct InternalApiConfig { pub l2_testnet_paymaster_addr: Option
, pub req_entities_limit: usize, pub fee_history_limit: u64, - pub base_token_address: Address, + pub base_token_address: Option
, pub filters_disabled: bool, pub dummy_verifier: bool, pub l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, diff --git a/core/node/base_token_adjuster/README.md b/core/node/base_token_adjuster/README.md index 3444f06906c5..3d139288792e 100644 --- a/core/node/base_token_adjuster/README.md +++ b/core/node/base_token_adjuster/README.md @@ -7,7 +7,11 @@ the fees to be denominated in the chain's base token. The Base Token Adjuster: -- Connect with external APIs to get the current price of the base token. -- Persist the price of the base token in the database. -- Upon request, adjust the fees to be denominated in the base token. +- Connects with external APIs to get the current price of the base token and of ETH. +- Persists the ETH<->BaseToken ratio in the database. - Upon certain configured threshold, update the L1 ETH<->BaseToken conversion ratio. + +The Base Token Fetcher: + +- Periodically fetches from the DB and caches the latest ETH<->BaseToken conversion ratio. +- Exposes this ratio upon request. diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_adjuster.rs index be8edf0ef261..de40a4340478 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_adjuster.rs @@ -1,4 +1,4 @@ -use std::{fmt::Debug, num::NonZero}; +use std::{fmt::Debug, num::NonZero, time::Duration}; use anyhow::Context as _; use chrono::Utc; @@ -21,7 +21,8 @@ impl BaseTokenAdjuster { /// Main loop for the base token adjuster. /// Orchestrates fetching a new ratio, persisting it, and conditionally updating the L1 with it. pub async fn run(&mut self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { - let mut timer = tokio::time::interval(self.config.price_polling_interval()); + let mut timer = + tokio::time::interval(Duration::from_millis(self.config.price_polling_interval_ms)); let pool = self.pool.clone(); while !*stop_receiver.borrow_and_update() { diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_fetcher.rs index d94a95a4124c..15eb379bb504 100644 --- a/core/node/base_token_adjuster/src/base_token_fetcher.rs +++ b/core/node/base_token_adjuster/src/base_token_fetcher.rs @@ -21,15 +21,19 @@ pub struct DBBaseTokenFetcher { impl DBBaseTokenFetcher { pub async fn new(pool: ConnectionPool) -> anyhow::Result { - let latest_ratio = get_latest_price(pool.clone()).await?; + let mut fetcher = Self { + pool, + latest_ratio: BaseTokenConversionRatio::default(), + }; + fetcher.latest_ratio = fetcher.get_latest_price().await?; // TODO(PE-129): Implement latest ratio usability logic. tracing::debug!( "Starting the base token fetcher with conversion ratio: {:?}", - latest_ratio + fetcher.latest_ratio ); - Ok(Self { pool, latest_ratio }) + Ok(fetcher) } pub async fn run(&mut self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { @@ -41,7 +45,7 @@ impl DBBaseTokenFetcher { _ = stop_receiver.changed() => break, } - let latest_storage_ratio = get_latest_price(self.pool.clone()).await?; + let latest_storage_ratio = self.get_latest_price().await?; // TODO(PE-129): Implement latest ratio usability logic. self.latest_ratio = BaseTokenConversionRatio { @@ -53,31 +57,32 @@ impl DBBaseTokenFetcher { tracing::info!("Stop signal received, base_token_fetcher is shutting down"); Ok(()) } -} -async fn get_latest_price(pool: ConnectionPool) -> anyhow::Result { - let latest_storage_ratio = pool - .connection_tagged("db_base_token_fetcher") - .await - .context("Failed to obtain connection to the database")? - .base_token_dal() - .get_latest_ratio() - .await; - - match latest_storage_ratio { - Ok(Some(latest_storage_price)) => Ok(BaseTokenConversionRatio { - numerator: latest_storage_price.numerator, - denominator: latest_storage_price.denominator, - }), - Ok(None) => { - // TODO(PE-136): Insert initial ratio from genesis. - // Though the DB should be populated very soon after the server starts, it is possible - // to have no ratios in the DB right after genesis. Having initial ratios in the DB - // from the genesis stage will eliminate this possibility. - tracing::error!("No latest price found in the database. Using default ratio."); - Ok(BaseTokenConversionRatio::default()) + async fn get_latest_price(&self) -> anyhow::Result { + let latest_storage_ratio = self + .pool + .connection_tagged("db_base_token_fetcher") + .await + .context("Failed to obtain connection to the database")? + .base_token_dal() + .get_latest_ratio() + .await; + + match latest_storage_ratio { + Ok(Some(latest_storage_price)) => Ok(BaseTokenConversionRatio { + numerator: latest_storage_price.numerator, + denominator: latest_storage_price.denominator, + }), + Ok(None) => { + // TODO(PE-136): Insert initial ratio from genesis. + // Though the DB should be populated very soon after the server starts, it is possible + // to have no ratios in the DB right after genesis. Having initial ratios in the DB + // from the genesis stage will eliminate this possibility. + tracing::error!("No latest price found in the database. Using default ratio."); + Ok(BaseTokenConversionRatio::default()) + } + Err(err) => anyhow::bail!("Failed to get latest base token ratio: {:?}", err), } - Err(err) => anyhow::bail!("Failed to get latest base token ratio: {:?}", err), } } diff --git a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs index dd659e984d41..df93b0a7aa26 100644 --- a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs +++ b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs @@ -73,7 +73,7 @@ impl MainNodeFeeParamsFetcher { #[async_trait::async_trait] impl BatchFeeModelInputProvider for MainNodeFeeParamsFetcher { - async fn get_fee_model_params(&self) -> anyhow::Result { - Ok(*self.main_node_fee_params.read().unwrap()) + fn get_fee_model_params(&self) -> FeeParams { + *self.main_node_fee_params.read().unwrap() } } diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 6b1af113adc0..0dcc99a312d0 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -27,7 +27,7 @@ pub trait BatchFeeModelInputProvider: fmt::Debug + 'static + Send + Sync { l1_gas_price_scale_factor: f64, l1_pubdata_price_scale_factor: f64, ) -> anyhow::Result { - let params = self.get_fee_model_params().await?; + let params = self.get_fee_model_params(); Ok(match params { FeeParams::V1(params) => BatchFeeInput::L1Pegged(compute_batch_fee_model_input_v1( @@ -45,7 +45,7 @@ pub trait BatchFeeModelInputProvider: fmt::Debug + 'static + Send + Sync { } /// Returns the fee model parameters using the denomination of the base token used (WEI for ETH). - async fn get_fee_model_params(&self) -> anyhow::Result; + fn get_fee_model_params(&self) -> FeeParams; } impl dyn BatchFeeModelInputProvider { @@ -67,18 +67,18 @@ pub struct MainNodeFeeInputProvider { #[async_trait] impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { - async fn get_fee_model_params(&self) -> anyhow::Result { + fn get_fee_model_params(&self) -> FeeParams { match self.config { - FeeModelConfig::V1(config) => Ok(FeeParams::V1(FeeParamsV1 { + FeeModelConfig::V1(config) => FeeParams::V1(FeeParamsV1 { config, l1_gas_price: self.provider.estimate_effective_gas_price(), - })), - FeeModelConfig::V2(config) => Ok(FeeParams::V2(FeeParamsV2::new( + }), + FeeModelConfig::V2(config) => FeeParams::V2(FeeParamsV2::new( config, self.provider.estimate_effective_gas_price(), self.provider.estimate_effective_pubdata_price(), self.base_token_fetcher.get_conversion_ratio(), - ))), + )), } } } @@ -143,9 +143,8 @@ impl BatchFeeModelInputProvider for ApiFeeInputProvider { } /// Returns the fee model parameters. - async fn get_fee_model_params(&self) -> anyhow::Result { - self.inner.get_fee_model_params().await - // TODO(PE-137): impl APIBaseTokenAdjuster and use it in the ApiFeeInputProvider. + fn get_fee_model_params(&self) -> FeeParams { + self.inner.get_fee_model_params() } } @@ -241,8 +240,8 @@ impl Default for MockBatchFeeParamsProvider { #[async_trait] impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { - async fn get_fee_model_params(&self) -> anyhow::Result { - Ok(self.0) + fn get_fee_model_params(&self) -> FeeParams { + self.0 } } @@ -619,10 +618,7 @@ mod tests { config, ); - let fee_params = fee_provider - .get_fee_model_params() - .await - .expect("Failed to get fee model params"); + let fee_params = fee_provider.get_fee_model_params(); if let FeeParams::V2(params) = fee_params { assert_eq!( From 8f45299d4d81c6acdd72fbcf15cd50edeb5fc70a Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 2 Jul 2024 15:35:46 +0300 Subject: [PATCH 43/48] changed names --- .github/workflows/ci-core-reusable.yml | 4 +-- core/bin/zksync_server/src/main.rs | 2 +- core/bin/zksync_server/src/node_builder.rs | 17 ++++++----- core/lib/config/src/configs/contracts.rs | 2 +- core/lib/zksync_core_leftovers/src/lib.rs | 8 +++-- core/node/base_token_adjuster/README.md | 11 ++++--- ...uster.rs => base_token_ratio_persister.rs} | 10 +++---- ...etcher.rs => base_token_ratio_provider.rs} | 24 +++++++-------- core/node/base_token_adjuster/src/lib.rs | 10 ++++--- core/node/fee_model/src/lib.rs | 16 +++++----- ...uster.rs => base_token_ratio_persister.rs} | 22 +++++++------- ...etcher.rs => base_token_ratio_provider.rs} | 30 ++++++++++--------- .../src/implementations/layers/l1_gas.rs | 7 +++-- .../src/implementations/layers/mod.rs | 4 +-- .../resources/base_token_fetcher.rs | 21 ------------- .../resources/base_token_ratio_provider.rs | 21 +++++++++++++ .../src/implementations/resources/mod.rs | 2 +- core/node/state_keeper/src/io/tests/tester.rs | 6 ++-- 18 files changed, 114 insertions(+), 103 deletions(-) rename core/node/base_token_adjuster/src/{base_token_adjuster.rs => base_token_ratio_persister.rs} (89%) rename core/node/base_token_adjuster/src/{base_token_fetcher.rs => base_token_ratio_provider.rs} (84%) rename core/node/node_framework/src/implementations/layers/{base_token_adjuster.rs => base_token_ratio_persister.rs} (71%) rename core/node/node_framework/src/implementations/layers/{base_token_fetcher.rs => base_token_ratio_provider.rs} (63%) delete mode 100644 core/node/node_framework/src/implementations/resources/base_token_fetcher.rs create mode 100644 core/node/node_framework/src/implementations/resources/base_token_ratio_provider.rs diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index 6b0ced4a1b31..02c907126f59 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -135,7 +135,7 @@ jobs: base_token: ["Eth", "Custom"] deployment_mode: ["Rollup", "Validium"] env: - SERVER_COMPONENTS: "api,tree,eth,state_keeper,base_token_adjuster,housekeeper,commitment_generator,vm_runner_protective_reads${{ matrix.consensus && ',consensus' || '' }}" + SERVER_COMPONENTS: "api,tree,eth,state_keeper,base_token_ratio_persister,housekeeper,commitment_generator,vm_runner_protective_reads${{ matrix.consensus && ',consensus' || '' }}" runs-on: [matterlabs-ci-runner] steps: @@ -305,7 +305,7 @@ jobs: runs-on: [matterlabs-ci-runner] env: - SERVER_COMPONENTS: "api,tree,eth,state_keeper,base_token_adjuster,housekeeper,commitment_generator,vm_runner_protective_reads${{ matrix.consensus && ',consensus' || '' }}" + SERVER_COMPONENTS: "api,tree,eth,state_keeper,base_token_ratio_persister,housekeeper,commitment_generator,vm_runner_protective_reads${{ matrix.consensus && ',consensus' || '' }}" EXT_NODE_FLAGS: "${{ matrix.consensus && '-- --enable-consensus' || '' }}" steps: diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index d4f7f0194546..53e7f3e2e825 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -48,7 +48,7 @@ struct Cli { /// Comma-separated list of components to launch. #[arg( long, - default_value = "api,tree,eth,state_keeper,housekeeper,tee_verifier_input_producer,commitment_generator,base_token_adjuster" + default_value = "api,tree,eth,state_keeper,housekeeper,tee_verifier_input_producer,commitment_generator,base_token_ratio_persister" )] components: ComponentsToRun, /// Path to the yaml config. If set, it will be used instead of env vars. diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index bc7a9e9e0518..a5d272797511 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -14,8 +14,8 @@ use zksync_node_api_server::{ }; use zksync_node_framework::{ implementations::layers::{ - base_token_adjuster::BaseTokenAdjusterLayer, - base_token_fetcher::BaseTokenFetcherLayer, + base_token_ratio_persister::BaseTokenRatioPersisterLayer, + base_token_ratio_provider::BaseTokenRatioProviderLayer, circuit_breaker_checker::CircuitBreakerCheckerLayer, commitment_generator::CommitmentGeneratorLayer, consensus::{ConsensusLayer, Mode as ConsensusMode}, @@ -143,9 +143,9 @@ impl MainNodeBuilder { } fn add_sequencer_l1_gas_layer(mut self) -> anyhow::Result { - // Ensure the BaseTokenFetcherResource is inserted if the base token is not ETH. + // Ensure the BaseTokenRatioProviderResource is inserted if the base token is not ETH. if self.contracts_config.base_token_addr != Some(SHARED_BRIDGE_ETHER_TOKEN_ADDRESS) { - self.node.add_layer(BaseTokenFetcherLayer {}); + self.node.add_layer(BaseTokenRatioProviderLayer {}); } let gas_adjuster_config = try_load_config!(self.configs.eth) @@ -463,9 +463,10 @@ impl MainNodeBuilder { Ok(self) } - fn add_base_token_adjuster_layer(mut self) -> anyhow::Result { + fn add_base_token_ratio_persister_layer(mut self) -> anyhow::Result { let config = try_load_config!(self.configs.base_token_adjuster); - self.node.add_layer(BaseTokenAdjusterLayer::new(config)); + self.node + .add_layer(BaseTokenRatioPersisterLayer::new(config)); Ok(self) } @@ -557,8 +558,8 @@ impl MainNodeBuilder { Component::VmRunnerProtectiveReads => { self = self.add_vm_runner_protective_reads_layer()?; } - Component::BaseTokenAdjuster => { - self = self.add_base_token_adjuster_layer()?; + Component::BaseTokenRatioPersister => { + self = self.add_base_token_ratio_persister_layer()?; } } } diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index a8226259e79a..4dabac07b004 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -37,7 +37,7 @@ pub struct ContractsConfig { pub l2_testnet_paymaster_addr: Option
, pub l1_multicall3_addr: Address, pub ecosystem_contracts: Option, - // Used by the RPC API and by the node builder in wiring the BaseTokenFetcher layer. If not set, + // Used by the RPC API and by the node builder in wiring the BaseTokenRatioProvider layer. If not set, // ETH is the chain's base token and the "ETH address" is SHARED_BRIDGE_ETHER_TOKEN_ADDRESS. pub base_token_addr: Option
, } diff --git a/core/lib/zksync_core_leftovers/src/lib.rs b/core/lib/zksync_core_leftovers/src/lib.rs index b086a45a9e1f..2968a1a3588f 100644 --- a/core/lib/zksync_core_leftovers/src/lib.rs +++ b/core/lib/zksync_core_leftovers/src/lib.rs @@ -88,8 +88,8 @@ pub enum Component { CommitmentGenerator, /// VM runner-based component that saves protective reads to Postgres. VmRunnerProtectiveReads, - /// A component to handle anything related to a chain's custom base token. - BaseTokenAdjuster, + /// A component to fetch and persist ETH<->BaseToken conversion ratios for chains with custom base tokens. + BaseTokenRatioPersister, } #[derive(Debug)] @@ -129,7 +129,9 @@ impl FromStr for Components { "vm_runner_protective_reads" => { Ok(Components(vec![Component::VmRunnerProtectiveReads])) } - "base_token_adjuster" => Ok(Components(vec![Component::BaseTokenAdjuster])), + "base_token_ratio_persister" => { + Ok(Components(vec![Component::BaseTokenRatioPersister])) + } other => Err(format!("{} is not a valid component name", other)), } } diff --git a/core/node/base_token_adjuster/README.md b/core/node/base_token_adjuster/README.md index 3d139288792e..66c90b7fd84f 100644 --- a/core/node/base_token_adjuster/README.md +++ b/core/node/base_token_adjuster/README.md @@ -1,17 +1,20 @@ # Base Token Adjuster -This crate contains all the logic to handle ZK Chain with custom base tokens. It is used by other node layers to adjust -the fees to be denominated in the chain's base token. +This crate contains all the logic to handle ZK Chain with custom base tokens. ## Overview -The Base Token Adjuster: +### The Base Token Ratio Persister + +Contains the building block for the `BaseTokenRatioPersisterLayer`. - Connects with external APIs to get the current price of the base token and of ETH. - Persists the ETH<->BaseToken ratio in the database. - Upon certain configured threshold, update the L1 ETH<->BaseToken conversion ratio. -The Base Token Fetcher: +### The Base Token Ratio Provider + +Contains the building block for the `BaseTokenRatioProviderLayer`. - Periodically fetches from the DB and caches the latest ETH<->BaseToken conversion ratio. - Exposes this ratio upon request. diff --git a/core/node/base_token_adjuster/src/base_token_adjuster.rs b/core/node/base_token_adjuster/src/base_token_ratio_persister.rs similarity index 89% rename from core/node/base_token_adjuster/src/base_token_adjuster.rs rename to core/node/base_token_adjuster/src/base_token_ratio_persister.rs index de40a4340478..15dab582892f 100644 --- a/core/node/base_token_adjuster/src/base_token_adjuster.rs +++ b/core/node/base_token_adjuster/src/base_token_ratio_persister.rs @@ -8,17 +8,17 @@ use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::base_token_ratio::BaseTokenAPIRatio; #[derive(Debug, Clone)] -pub struct BaseTokenAdjuster { +pub struct BaseTokenRatioPersister { pool: ConnectionPool, config: BaseTokenAdjusterConfig, } -impl BaseTokenAdjuster { +impl BaseTokenRatioPersister { pub fn new(pool: ConnectionPool, config: BaseTokenAdjusterConfig) -> Self { Self { pool, config } } - /// Main loop for the base token adjuster. + /// Main loop for the base token ratio persister. /// Orchestrates fetching a new ratio, persisting it, and conditionally updating the L1 with it. pub async fn run(&mut self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { let mut timer = @@ -36,7 +36,7 @@ impl BaseTokenAdjuster { // TODO(PE-128): Update L1 ratio } - tracing::info!("Stop signal received, base_token_adjuster is shutting down"); + tracing::info!("Stop signal received, base_token_ratio_persister is shutting down"); Ok(()) } @@ -58,7 +58,7 @@ impl BaseTokenAdjuster { pool: &ConnectionPool, ) -> anyhow::Result { let mut conn = pool - .connection_tagged("base_token_adjuster") + .connection_tagged("base_token_ratio_persister") .await .context("Failed to obtain connection to the database")?; diff --git a/core/node/base_token_adjuster/src/base_token_fetcher.rs b/core/node/base_token_adjuster/src/base_token_ratio_provider.rs similarity index 84% rename from core/node/base_token_adjuster/src/base_token_fetcher.rs rename to core/node/base_token_adjuster/src/base_token_ratio_provider.rs index 15eb379bb504..39a96556f8de 100644 --- a/core/node/base_token_adjuster/src/base_token_fetcher.rs +++ b/core/node/base_token_adjuster/src/base_token_ratio_provider.rs @@ -9,17 +9,17 @@ use zksync_types::fee_model::BaseTokenConversionRatio; const CACHE_UPDATE_INTERVAL: Duration = Duration::from_millis(500); #[async_trait] -pub trait BaseTokenFetcher: Debug + Send + Sync { +pub trait BaseTokenRatioProvider: Debug + Send + Sync { fn get_conversion_ratio(&self) -> BaseTokenConversionRatio; } #[derive(Debug, Clone)] -pub struct DBBaseTokenFetcher { +pub struct DBBaseTokenRatioProvider { pub pool: ConnectionPool, pub latest_ratio: BaseTokenConversionRatio, } -impl DBBaseTokenFetcher { +impl DBBaseTokenRatioProvider { pub async fn new(pool: ConnectionPool) -> anyhow::Result { let mut fetcher = Self { pool, @@ -30,7 +30,7 @@ impl DBBaseTokenFetcher { // TODO(PE-129): Implement latest ratio usability logic. tracing::debug!( - "Starting the base token fetcher with conversion ratio: {:?}", + "Starting the base token ratio provider with conversion ratio: {:?}", fetcher.latest_ratio ); Ok(fetcher) @@ -54,14 +54,14 @@ impl DBBaseTokenFetcher { }; } - tracing::info!("Stop signal received, base_token_fetcher is shutting down"); + tracing::info!("Stop signal received, base_token_ratio_provider is shutting down"); Ok(()) } async fn get_latest_price(&self) -> anyhow::Result { let latest_storage_ratio = self .pool - .connection_tagged("db_base_token_fetcher") + .connection_tagged("db_base_token_ratio_provider") .await .context("Failed to obtain connection to the database")? .base_token_dal() @@ -87,25 +87,25 @@ impl DBBaseTokenFetcher { } #[async_trait] -impl BaseTokenFetcher for DBBaseTokenFetcher { +impl BaseTokenRatioProvider for DBBaseTokenRatioProvider { fn get_conversion_ratio(&self) -> BaseTokenConversionRatio { self.latest_ratio } } -// Struct for a no-op BaseTokenFetcher (conversion ratio is either always 1:1 or a forced ratio). +// Struct for a no-op BaseTokenRatioProvider (conversion ratio is either always 1:1 or a forced ratio). #[derive(Debug, Clone)] -pub struct NoOpFetcher { +pub struct NoOpRatioProvider { pub latest_ratio: BaseTokenConversionRatio, } -impl NoOpFetcher { +impl NoOpRatioProvider { pub fn new(latest_ratio: BaseTokenConversionRatio) -> Self { Self { latest_ratio } } } -impl Default for NoOpFetcher { +impl Default for NoOpRatioProvider { fn default() -> Self { Self { latest_ratio: BaseTokenConversionRatio { @@ -117,7 +117,7 @@ impl Default for NoOpFetcher { } #[async_trait] -impl BaseTokenFetcher for NoOpFetcher { +impl BaseTokenRatioProvider for NoOpRatioProvider { fn get_conversion_ratio(&self) -> BaseTokenConversionRatio { self.latest_ratio } diff --git a/core/node/base_token_adjuster/src/lib.rs b/core/node/base_token_adjuster/src/lib.rs index 902893fa9708..2340ca56c2a7 100644 --- a/core/node/base_token_adjuster/src/lib.rs +++ b/core/node/base_token_adjuster/src/lib.rs @@ -1,7 +1,9 @@ pub use self::{ - base_token_adjuster::BaseTokenAdjuster, - base_token_fetcher::{BaseTokenFetcher, DBBaseTokenFetcher, NoOpFetcher}, + base_token_ratio_persister::BaseTokenRatioPersister, + base_token_ratio_provider::{ + BaseTokenRatioProvider, DBBaseTokenRatioProvider, NoOpRatioProvider, + }, }; -mod base_token_adjuster; -mod base_token_fetcher; +mod base_token_ratio_persister; +mod base_token_ratio_provider; diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 0dcc99a312d0..00d804de6c81 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -2,7 +2,7 @@ use std::{fmt, sync::Arc}; use anyhow::Context as _; use async_trait::async_trait; -use zksync_base_token_adjuster::BaseTokenFetcher; +use zksync_base_token_adjuster::BaseTokenRatioProvider; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::{ fee_model::{ @@ -61,7 +61,7 @@ impl dyn BatchFeeModelInputProvider { #[derive(Debug)] pub struct MainNodeFeeInputProvider { provider: Arc, - base_token_fetcher: Arc, + base_token_ratio_provider: Arc, config: FeeModelConfig, } @@ -77,7 +77,7 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { config, self.provider.estimate_effective_gas_price(), self.provider.estimate_effective_pubdata_price(), - self.base_token_fetcher.get_conversion_ratio(), + self.base_token_ratio_provider.get_conversion_ratio(), )), } } @@ -86,12 +86,12 @@ impl BatchFeeModelInputProvider for MainNodeFeeInputProvider { impl MainNodeFeeInputProvider { pub fn new( provider: Arc, - base_token_fetcher: Arc, + base_token_ratio_provider: Arc, config: FeeModelConfig, ) -> Self { Self { provider, - base_token_fetcher, + base_token_ratio_provider, config, } } @@ -249,7 +249,7 @@ impl BatchFeeModelInputProvider for MockBatchFeeParamsProvider { mod tests { use std::num::NonZeroU64; - use zksync_base_token_adjuster::NoOpFetcher; + use zksync_base_token_adjuster::NoOpRatioProvider; use zksync_config::{configs::eth_sender::PubdataSendingMode, GasAdjusterConfig}; use zksync_eth_client::{clients::MockEthereum, BaseFees}; use zksync_types::{commitment::L1BatchCommitmentMode, fee_model::BaseTokenConversionRatio}; @@ -601,7 +601,7 @@ mod tests { let gas_adjuster = setup_gas_adjuster(case.input_l1_gas_price, case.input_l1_pubdata_price).await; - let base_token_fetcher = NoOpFetcher::new(case.conversion_ratio); + let base_token_ratio_provider = NoOpRatioProvider::new(case.conversion_ratio); let config = FeeModelConfig::V2(FeeModelConfigV2 { minimal_l2_gas_price: case.input_minimal_l2_gas_price, @@ -614,7 +614,7 @@ mod tests { let fee_provider = MainNodeFeeInputProvider::new( Arc::new(gas_adjuster), - Arc::new(base_token_fetcher), + Arc::new(base_token_ratio_provider), config, ); diff --git a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs b/core/node/node_framework/src/implementations/layers/base_token_ratio_persister.rs similarity index 71% rename from core/node/node_framework/src/implementations/layers/base_token_adjuster.rs rename to core/node/node_framework/src/implementations/layers/base_token_ratio_persister.rs index 6d04a0477a5b..c9a6ef8d8b66 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_adjuster.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_ratio_persister.rs @@ -1,4 +1,4 @@ -use zksync_base_token_adjuster::BaseTokenAdjuster; +use zksync_base_token_adjuster::BaseTokenRatioPersister; use zksync_config::configs::base_token_adjuster::BaseTokenAdjusterConfig; use crate::{ @@ -8,7 +8,7 @@ use crate::{ wiring_layer::{WiringError, WiringLayer}, }; -/// Wiring layer for `BaseTokenAdjuster` +/// Wiring layer for `BaseTokenRatioPersister` /// /// Responsible for orchestrating communications with external API feeds to get ETH<->BaseToken /// conversion ratios and persisting them both in the DB and in the L1. @@ -19,40 +19,40 @@ use crate::{ /// /// ## Adds tasks /// -/// - `BaseTokenAdjuster` +/// - `BaseTokenRatioPersister` #[derive(Debug)] -pub struct BaseTokenAdjusterLayer { +pub struct BaseTokenRatioPersisterLayer { config: BaseTokenAdjusterConfig, } -impl BaseTokenAdjusterLayer { +impl BaseTokenRatioPersisterLayer { pub fn new(config: BaseTokenAdjusterConfig) -> Self { Self { config } } } #[async_trait::async_trait] -impl WiringLayer for BaseTokenAdjusterLayer { +impl WiringLayer for BaseTokenRatioPersisterLayer { fn layer_name(&self) -> &'static str { - "base_token_adjuster" + "base_token_ratio_persister" } async fn wire(self: Box, mut context: ServiceContext<'_>) -> Result<(), WiringError> { let master_pool_resource = context.get_resource::>()?; let master_pool = master_pool_resource.get().await?; - let adjuster = BaseTokenAdjuster::new(master_pool, self.config); + let persister = BaseTokenRatioPersister::new(master_pool, self.config); - context.add_task(adjuster); + context.add_task(persister); Ok(()) } } #[async_trait::async_trait] -impl Task for BaseTokenAdjuster { +impl Task for BaseTokenRatioPersister { fn id(&self) -> TaskId { - "base_token_adjuster".into() + "base_token_ratio_persister".into() } async fn run(mut self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { diff --git a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs b/core/node/node_framework/src/implementations/layers/base_token_ratio_provider.rs similarity index 63% rename from core/node/node_framework/src/implementations/layers/base_token_fetcher.rs rename to core/node/node_framework/src/implementations/layers/base_token_ratio_provider.rs index 0b8df4aedd4e..b80de5c3acb2 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_fetcher.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_ratio_provider.rs @@ -1,10 +1,10 @@ use std::sync::Arc; -use zksync_base_token_adjuster::DBBaseTokenFetcher; +use zksync_base_token_adjuster::DBBaseTokenRatioProvider; use crate::{ implementations::resources::{ - base_token_fetcher::BaseTokenFetcherResource, + base_token_ratio_provider::BaseTokenRatioProviderResource, pools::{PoolResource, ReplicaPool}, }, service::{ServiceContext, StopReceiver}, @@ -12,13 +12,13 @@ use crate::{ wiring_layer::{WiringError, WiringLayer}, }; -/// Wiring layer for `BaseTokenFetcher` +/// Wiring layer for `BaseTokenRatioProvider` /// /// Responsible for serving the latest ETH<->BaseToken conversion ratio. This layer is only wired if -/// the base token is not ETH. If wired, this layer inserts the BaseTokenFetcherResource and kicks +/// the base token is not ETH. If wired, this layer inserts the BaseTokenRatioProviderResource and kicks /// off a task to poll the DB for the latest ratio and cache it. /// -/// If the base token is ETH, a default, no-op impl of the BaseTokenFetcherResource is used by other +/// If the base token is ETH, a default, no-op impl of the BaseTokenRatioProviderResource is used by other /// layers to always return a conversion ratio of 1. /// ## Requests resources @@ -27,33 +27,35 @@ use crate::{ /// /// ## Adds tasks /// -/// - `BaseTokenFetcher` +/// - `BaseTokenRatioProvider` #[derive(Debug)] -pub struct BaseTokenFetcherLayer; +pub struct BaseTokenRatioProviderLayer; #[async_trait::async_trait] -impl WiringLayer for BaseTokenFetcherLayer { +impl WiringLayer for BaseTokenRatioProviderLayer { fn layer_name(&self) -> &'static str { - "base_token_fetcher" + "base_token_ratio_provider" } async fn wire(self: Box, mut context: ServiceContext<'_>) -> Result<(), WiringError> { let replica_pool_resource = context.get_resource::>()?; let replica_pool = replica_pool_resource.get().await.unwrap(); - let fetcher = DBBaseTokenFetcher::new(replica_pool).await?; + let ratio_provider = DBBaseTokenRatioProvider::new(replica_pool).await?; - context.insert_resource(BaseTokenFetcherResource(Arc::new(fetcher.clone())))?; - context.add_task(fetcher); + context.insert_resource(BaseTokenRatioProviderResource(Arc::new( + ratio_provider.clone(), + )))?; + context.add_task(ratio_provider); Ok(()) } } #[async_trait::async_trait] -impl Task for DBBaseTokenFetcher { +impl Task for DBBaseTokenRatioProvider { fn id(&self) -> TaskId { - "base_token_fetcher".into() + "base_token_ratio_provider".into() } async fn run(mut self: Box, stop_receiver: StopReceiver) -> anyhow::Result<()> { diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index 4f4af39aa997..676a92fd17ac 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -10,8 +10,9 @@ use zksync_types::fee_model::FeeModelConfig; use crate::{ implementations::resources::{ - base_token_fetcher::BaseTokenFetcherResource, eth_interface::EthInterfaceResource, - fee_input::FeeInputResource, l1_tx_params::L1TxParamsResource, + base_token_ratio_provider::BaseTokenRatioProviderResource, + eth_interface::EthInterfaceResource, fee_input::FeeInputResource, + l1_tx_params::L1TxParamsResource, }, service::{ServiceContext, StopReceiver}, task::{Task, TaskId}, @@ -75,7 +76,7 @@ impl WiringLayer for SequencerL1GasLayer { .context("GasAdjuster::new()")?; let gas_adjuster = Arc::new(adjuster); - let fetcher = context.get_resource_or_default::(); + let fetcher = context.get_resource_or_default::(); let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( gas_adjuster.clone(), diff --git a/core/node/node_framework/src/implementations/layers/mod.rs b/core/node/node_framework/src/implementations/layers/mod.rs index fd3772538c86..7e544ca74ada 100644 --- a/core/node/node_framework/src/implementations/layers/mod.rs +++ b/core/node/node_framework/src/implementations/layers/mod.rs @@ -1,5 +1,5 @@ -pub mod base_token_adjuster; -pub mod base_token_fetcher; +pub mod base_token_ratio_persister; +pub mod base_token_ratio_provider; pub mod batch_status_updater; pub mod circuit_breaker_checker; pub mod commitment_generator; diff --git a/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs b/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs deleted file mode 100644 index be8079eda739..000000000000 --- a/core/node/node_framework/src/implementations/resources/base_token_fetcher.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::sync::Arc; - -use zksync_base_token_adjuster::{BaseTokenFetcher, NoOpFetcher}; - -use crate::resource::Resource; - -/// A resource that provides [`BaseTokenFetcher`] implementation to the service. -#[derive(Clone)] -pub struct BaseTokenFetcherResource(pub Arc); - -impl Default for BaseTokenFetcherResource { - fn default() -> Self { - Self(Arc::new(NoOpFetcher::default())) - } -} - -impl Resource for BaseTokenFetcherResource { - fn name() -> String { - "common/base_token_fetcher".into() - } -} diff --git a/core/node/node_framework/src/implementations/resources/base_token_ratio_provider.rs b/core/node/node_framework/src/implementations/resources/base_token_ratio_provider.rs new file mode 100644 index 000000000000..9cb43870f76c --- /dev/null +++ b/core/node/node_framework/src/implementations/resources/base_token_ratio_provider.rs @@ -0,0 +1,21 @@ +use std::sync::Arc; + +use zksync_base_token_adjuster::{BaseTokenRatioProvider, NoOpRatioProvider}; + +use crate::resource::Resource; + +/// A resource that provides [`BaseTokenRatioProvider`] implementation to the service. +#[derive(Clone)] +pub struct BaseTokenRatioProviderResource(pub Arc); + +impl Default for BaseTokenRatioProviderResource { + fn default() -> Self { + Self(Arc::new(NoOpRatioProvider::default())) + } +} + +impl Resource for BaseTokenRatioProviderResource { + fn name() -> String { + "common/base_token_ratio_provider".into() + } +} diff --git a/core/node/node_framework/src/implementations/resources/mod.rs b/core/node/node_framework/src/implementations/resources/mod.rs index 60eecb4b9e93..87942e0fbaf3 100644 --- a/core/node/node_framework/src/implementations/resources/mod.rs +++ b/core/node/node_framework/src/implementations/resources/mod.rs @@ -1,5 +1,5 @@ pub mod action_queue; -pub mod base_token_fetcher; +pub mod base_token_ratio_provider; pub mod circuit_breakers; pub mod eth_interface; pub mod fee_input; diff --git a/core/node/state_keeper/src/io/tests/tester.rs b/core/node/state_keeper/src/io/tests/tester.rs index d291e1935b7d..c056191736f2 100644 --- a/core/node/state_keeper/src/io/tests/tester.rs +++ b/core/node/state_keeper/src/io/tests/tester.rs @@ -2,7 +2,7 @@ use std::{slice, sync::Arc, time::Duration}; -use zksync_base_token_adjuster::NoOpFetcher; +use zksync_base_token_adjuster::NoOpRatioProvider; use zksync_config::{ configs::{chain::StateKeeperConfig, eth_sender::PubdataSendingMode, wallets::Wallets}, GasAdjusterConfig, @@ -88,7 +88,7 @@ impl Tester { MainNodeFeeInputProvider::new( gas_adjuster, - Arc::new(NoOpFetcher::default()), + Arc::new(NoOpRatioProvider::default()), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), @@ -107,7 +107,7 @@ impl Tester { let gas_adjuster = Arc::new(self.create_gas_adjuster().await); let batch_fee_input_provider = MainNodeFeeInputProvider::new( gas_adjuster, - Arc::new(NoOpFetcher::default()), + Arc::new(NoOpRatioProvider::default()), FeeModelConfig::V1(FeeModelConfigV1 { minimal_l2_gas_price: self.minimal_l2_gas_price(), }), From 2b5d92cfe62fc1208d1380f9a77629cc6eadec8e Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 2 Jul 2024 15:59:19 +0300 Subject: [PATCH 44/48] precision and scale to NUMERIC --- .../20240611121747_add_base_token_ratio_table.up.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.up.sql b/core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.up.sql index ce52209e073f..f4853e352802 100644 --- a/core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.up.sql +++ b/core/lib/dal/migrations/20240611121747_add_base_token_ratio_table.up.sql @@ -4,8 +4,8 @@ CREATE TABLE base_token_ratios ( updated_at TIMESTAMP NOT NULL, ratio_timestamp TIMESTAMP NOT NULL, - numerator NUMERIC NOT NULL, - denominator NUMERIC NOT NULL, + numerator NUMERIC(20,0) NOT NULL, + denominator NUMERIC(20,0) NOT NULL, used_in_l1 BOOLEAN NOT NULL DEFAULT FALSE ); From 91a5a6013ff98af9289dd754b9a160b7c6d2b4c2 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 2 Jul 2024 18:09:06 +0300 Subject: [PATCH 45/48] remove contracts change from bad main merge --- contracts | 2 +- core/lib/protobuf_config/src/contracts.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts b/contracts index db9387690502..8172969672cc 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit db9387690502937de081a959b164db5a5262ce0a +Subproject commit 8172969672cc6a38542cd8f5578c74b7e30cd3b4 diff --git a/core/lib/protobuf_config/src/contracts.rs b/core/lib/protobuf_config/src/contracts.rs index 4358aa4075a8..ac1864b7a0bd 100644 --- a/core/lib/protobuf_config/src/contracts.rs +++ b/core/lib/protobuf_config/src/contracts.rs @@ -131,7 +131,7 @@ impl ProtoRepr for proto::Contracts { validator_timelock_addr: Some(format!("{:?}", this.validator_timelock_addr)), default_upgrade_addr: Some(format!("{:?}", this.default_upgrade_addr)), multicall3_addr: Some(format!("{:?}", this.l1_multicall3_addr)), - base_token_addr: Some(format!("{:?}", this.base_token_addr)), + base_token_addr: this.base_token_addr.map(|a| format!("{:?}", a)), }), l2: Some(proto::L2 { testnet_paymaster_addr: this.l2_testnet_paymaster_addr.map(|a| format!("{:?}", a)), From cfdeb10f0fa8518e74790ec7099923f7546fa2a4 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Tue, 2 Jul 2024 18:38:47 +0300 Subject: [PATCH 46/48] pass ci --- core/lib/config/src/configs/base_token_adjuster.rs | 4 ++-- core/node/base_token_adjuster/README.md | 4 ++-- core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs | 1 - core/node/node_framework/src/implementations/layers/l1_gas.rs | 4 ++-- zk_toolbox/Cargo.lock | 2 ++ 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 64f83cba610d..e1221be6f2d0 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -1,11 +1,11 @@ use serde::Deserialize; -// By default external APIs shall be polled every 30 seconds for a new price. +/// By default the ratio persister will run every 30 seconds. pub const DEFAULT_INTERVAL_MS: u64 = 30_000; #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct BaseTokenAdjusterConfig { - /// How often to fetch external APIs for a new ETH<->Base-Token price. + /// How often to spark a new cycle of the ratio persister to fetch external prices and persis ratios. #[serde(default = "BaseTokenAdjusterConfig::default_interval")] pub price_polling_interval_ms: u64, } diff --git a/core/node/base_token_adjuster/README.md b/core/node/base_token_adjuster/README.md index 66c90b7fd84f..c5b6dec2b17c 100644 --- a/core/node/base_token_adjuster/README.md +++ b/core/node/base_token_adjuster/README.md @@ -6,7 +6,7 @@ This crate contains all the logic to handle ZK Chain with custom base tokens. ### The Base Token Ratio Persister -Contains the building block for the `BaseTokenRatioPersisterLayer`. +Contains the building blockss for the `BaseTokenRatioPersisterLayer`. - Connects with external APIs to get the current price of the base token and of ETH. - Persists the ETH<->BaseToken ratio in the database. @@ -14,7 +14,7 @@ Contains the building block for the `BaseTokenRatioPersisterLayer`. ### The Base Token Ratio Provider -Contains the building block for the `BaseTokenRatioProviderLayer`. +Contains the building blocks for the `BaseTokenRatioProviderLayer`. - Periodically fetches from the DB and caches the latest ETH<->BaseToken conversion ratio. - Exposes this ratio upon request. diff --git a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs index df93b0a7aa26..259a5e3e3fed 100644 --- a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs +++ b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs @@ -71,7 +71,6 @@ impl MainNodeFeeParamsFetcher { } } -#[async_trait::async_trait] impl BatchFeeModelInputProvider for MainNodeFeeParamsFetcher { fn get_fee_model_params(&self) -> FeeParams { *self.main_node_fee_params.read().unwrap() diff --git a/core/node/node_framework/src/implementations/layers/l1_gas.rs b/core/node/node_framework/src/implementations/layers/l1_gas.rs index 676a92fd17ac..2276e73e857f 100644 --- a/core/node/node_framework/src/implementations/layers/l1_gas.rs +++ b/core/node/node_framework/src/implementations/layers/l1_gas.rs @@ -76,11 +76,11 @@ impl WiringLayer for SequencerL1GasLayer { .context("GasAdjuster::new()")?; let gas_adjuster = Arc::new(adjuster); - let fetcher = context.get_resource_or_default::(); + let ratio_provider = context.get_resource_or_default::(); let batch_fee_input_provider = Arc::new(MainNodeFeeInputProvider::new( gas_adjuster.clone(), - fetcher.0.clone(), + ratio_provider.0.clone(), FeeModelConfig::from_state_keeper_config(&self.state_keeper_config), )); context.insert_resource(FeeInputResource(batch_fee_input_provider))?; diff --git a/zk_toolbox/Cargo.lock b/zk_toolbox/Cargo.lock index e6f82da3ad75..62501a944bb9 100644 --- a/zk_toolbox/Cargo.lock +++ b/zk_toolbox/Cargo.lock @@ -6508,6 +6508,7 @@ name = "zksync_types" version = "0.1.0" dependencies = [ "anyhow", + "bigdecimal", "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono", "derive_more 1.0.0-beta.6", @@ -6523,6 +6524,7 @@ dependencies = [ "serde_json", "strum 0.24.1", "thiserror", + "tracing", "zksync_basic_types", "zksync_config", "zksync_contracts", From c35a0356f6fd8e4fe9e7d77f3a085620db84159d Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 3 Jul 2024 13:49:57 +0300 Subject: [PATCH 47/48] default_interval not public and comments --- core/lib/config/src/configs/base_token_adjuster.rs | 2 +- core/lib/config/src/configs/contracts.rs | 3 +-- .../src/implementations/layers/base_token_ratio_provider.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index e1221be6f2d0..92a18473f6db 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -19,7 +19,7 @@ impl Default for BaseTokenAdjusterConfig { } impl BaseTokenAdjusterConfig { - pub fn default_interval() -> u64 { + fn default_interval() -> u64 { DEFAULT_INTERVAL_MS } } diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index 4dabac07b004..1ab032869e37 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -37,8 +37,7 @@ pub struct ContractsConfig { pub l2_testnet_paymaster_addr: Option
, pub l1_multicall3_addr: Address, pub ecosystem_contracts: Option, - // Used by the RPC API and by the node builder in wiring the BaseTokenRatioProvider layer. If not set, - // ETH is the chain's base token and the "ETH address" is SHARED_BRIDGE_ETHER_TOKEN_ADDRESS. + // Used by the RPC API and by the node builder in wiring the BaseTokenRatioProvider layer. pub base_token_addr: Option
, } diff --git a/core/node/node_framework/src/implementations/layers/base_token_ratio_provider.rs b/core/node/node_framework/src/implementations/layers/base_token_ratio_provider.rs index b80de5c3acb2..d213ac68c79b 100644 --- a/core/node/node_framework/src/implementations/layers/base_token_ratio_provider.rs +++ b/core/node/node_framework/src/implementations/layers/base_token_ratio_provider.rs @@ -20,7 +20,7 @@ use crate::{ /// /// If the base token is ETH, a default, no-op impl of the BaseTokenRatioProviderResource is used by other /// layers to always return a conversion ratio of 1. - +/// /// ## Requests resources /// /// - `PoolResource` From 11b9fedd39d8f16fda0bc3a91c2d567640012dd1 Mon Sep 17 00:00:00 2001 From: Shahar Kaminsky Date: Wed, 3 Jul 2024 14:16:21 +0300 Subject: [PATCH 48/48] nits --- core/lib/config/src/configs/base_token_adjuster.rs | 6 ++++++ core/lib/dal/src/base_token_dal.rs | 2 -- .../base_token_adjuster/src/base_token_ratio_persister.rs | 5 ++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/lib/config/src/configs/base_token_adjuster.rs b/core/lib/config/src/configs/base_token_adjuster.rs index 92a18473f6db..11d669429e05 100644 --- a/core/lib/config/src/configs/base_token_adjuster.rs +++ b/core/lib/config/src/configs/base_token_adjuster.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use serde::Deserialize; /// By default the ratio persister will run every 30 seconds. @@ -22,4 +24,8 @@ impl BaseTokenAdjusterConfig { fn default_interval() -> u64 { DEFAULT_INTERVAL_MS } + + pub fn price_polling_interval(&self) -> Duration { + Duration::from_millis(self.price_polling_interval_ms) + } } diff --git a/core/lib/dal/src/base_token_dal.rs b/core/lib/dal/src/base_token_dal.rs index d5a9c00dc120..a8bf51d0c603 100644 --- a/core/lib/dal/src/base_token_dal.rs +++ b/core/lib/dal/src/base_token_dal.rs @@ -38,8 +38,6 @@ impl BaseTokenDal<'_, '_> { Ok(row.id as usize) } - // TODO (PE-128): pub async fn mark_l1_update() - pub async fn get_latest_ratio(&mut self) -> DalResult> { let row = sqlx::query_as!( StorageBaseTokenRatio, diff --git a/core/node/base_token_adjuster/src/base_token_ratio_persister.rs b/core/node/base_token_adjuster/src/base_token_ratio_persister.rs index 15dab582892f..b730737b992f 100644 --- a/core/node/base_token_adjuster/src/base_token_ratio_persister.rs +++ b/core/node/base_token_adjuster/src/base_token_ratio_persister.rs @@ -1,4 +1,4 @@ -use std::{fmt::Debug, num::NonZero, time::Duration}; +use std::{fmt::Debug, num::NonZero}; use anyhow::Context as _; use chrono::Utc; @@ -21,8 +21,7 @@ impl BaseTokenRatioPersister { /// Main loop for the base token ratio persister. /// Orchestrates fetching a new ratio, persisting it, and conditionally updating the L1 with it. pub async fn run(&mut self, mut stop_receiver: watch::Receiver) -> anyhow::Result<()> { - let mut timer = - tokio::time::interval(Duration::from_millis(self.config.price_polling_interval_ms)); + let mut timer = tokio::time::interval(self.config.price_polling_interval()); let pool = self.pool.clone(); while !*stop_receiver.borrow_and_update() {