Skip to content

Commit

Permalink
feat: Allow tracking l2 fees for L2-based chains (#2563)
Browse files Browse the repository at this point in the history
## What ❔

In this PR we add changes to the GasAdjuster needed to track the fees on
L2-based chains. The PR aims to preserve type-safety for places that
should work when L1 interface is available only, so `DynClient<L1>` is
left in a lot of places and generally `L1` is the default network to be
used. However, some methods were extended to work with any network.

Overall what was added:

- A new resource for accessing L2-specific endpoints.
- A new field is returned from `fee_history`. Clients should not rely on
this field. However, we added it so we could test things.
- Some refactoring
- While some new new config fields are added into the Rust code, they
are not added to the config so that in case something has to be changed,
we do not introduce legacy (esp. in protobuf)
- Into protobuf we only added `RelayedL2Calldata` pubdata type, which
would symbolize that the calldata is being relayed to L1 through another
L2. I am pretty confident that this type will be needed anyway.

## Why ❔ 

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [ ] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [ ] Tests for the changes have been added / updated.
- [ ] Documentation comments have been added / updated.
- [ ] Code has been formatted via `zk fmt` and `zk lint`.
  • Loading branch information
StanislavBreadless authored Aug 9, 2024
1 parent d40ff5f commit e3f7804
Show file tree
Hide file tree
Showing 63 changed files with 959 additions and 413 deletions.
4 changes: 2 additions & 2 deletions core/bin/block_reverter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use tokio::{
};
use zksync_block_reverter::{
eth_client::{
clients::{Client, PKSigningClient},
clients::{Client, PKSigningClient, L1},
EthInterface,
},
BlockReverter, BlockReverterEthConfig, NodeRole,
Expand Down Expand Up @@ -251,7 +251,7 @@ async fn main() -> anyhow::Result<()> {
json,
operator_address,
} => {
let eth_client = Client::http(l1_secrets.l1_rpc_url.clone())
let eth_client = Client::<L1>::http(l1_secrets.l1_rpc_url.clone())
.context("Ethereum client")?
.build();

Expand Down
2 changes: 2 additions & 0 deletions core/bin/external_node/src/node_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ impl ExternalNodeBuilder {
let query_eth_client_layer = QueryEthClientLayer::new(
self.config.required.settlement_layer_id(),
self.config.required.eth_client_url.clone(),
// TODO(EVM-676): add this config for external node
Default::default(),
);
self.node.add_layer(query_eth_client_layer);
Ok(self)
Expand Down
8 changes: 7 additions & 1 deletion core/bin/external_node/src/tests/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,13 @@ impl WiringLayer for MockL1ClientLayer {

fn layer_name(&self) -> &'static str {
// We don't care about values, we just want to hijack the layer name.
QueryEthClientLayer::new(SLChainId(1), "https://example.com".parse().unwrap()).layer_name()
// TODO(EVM-676): configure the `settlement_mode` here
QueryEthClientLayer::new(
SLChainId(1),
"https://example.com".parse().unwrap(),
Default::default(),
)
.layer_name()
}

async fn wire(self, _: Self::Input) -> Result<Self::Output, WiringError> {
Expand Down
4 changes: 2 additions & 2 deletions core/bin/external_node/src/tests/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use tempfile::TempDir;
use zksync_dal::CoreDal;
use zksync_db_connection::connection_pool::TestTemplate;
use zksync_eth_client::clients::MockEthereum;
use zksync_eth_client::clients::MockSettlementLayer;
use zksync_node_genesis::{insert_genesis_batch, GenesisBatchParams, GenesisParams};
use zksync_types::{
api, block::L2BlockHeader, ethabi, Address, L2BlockNumber, ProtocolVersionId, H256,
Expand Down Expand Up @@ -119,7 +119,7 @@ pub(super) fn expected_health_components(components: &ComponentsToRun) -> Vec<&'
}

pub(super) fn mock_eth_client(diamond_proxy_addr: Address) -> MockClient<L1> {
let mock = MockEthereum::builder().with_call_handler(move |call, _| {
let mock = MockSettlementLayer::builder().with_call_handler(move |call, _| {
tracing::info!("L1 call: {call:?}");
if call.to == Some(diamond_proxy_addr) {
let packed_semver = ProtocolVersionId::latest().into_packed_semver_with_patch(0);
Expand Down
13 changes: 10 additions & 3 deletions core/bin/zksync_server/src/node_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ use zksync_node_framework::{
},
service::{ZkStackService, ZkStackServiceBuilder},
};
use zksync_types::SHARED_BRIDGE_ETHER_TOKEN_ADDRESS;
use zksync_types::{settlement::SettlementMode, SHARED_BRIDGE_ETHER_TOKEN_ADDRESS};
use zksync_vlog::prometheus::PrometheusExporterConfig;

/// Macro that looks into a path to fetch an optional config,
Expand Down Expand Up @@ -153,8 +153,15 @@ impl MainNodeBuilder {
fn add_query_eth_client_layer(mut self) -> anyhow::Result<Self> {
let genesis = self.genesis_config.clone();
let eth_config = try_load_config!(self.secrets.l1);
let query_eth_client_layer =
QueryEthClientLayer::new(genesis.settlement_layer_id(), eth_config.l1_rpc_url);
let query_eth_client_layer = QueryEthClientLayer::new(
genesis.settlement_layer_id(),
eth_config.l1_rpc_url,
self.configs
.eth
.as_ref()
.and_then(|x| Some(x.gas_adjuster?.settlement_mode))
.unwrap_or(SettlementMode::SettlesToL1),
);
self.node.add_layer(query_eth_client_layer);
Ok(self)
}
Expand Down
1 change: 1 addition & 0 deletions core/lib/basic_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub mod commitment;
pub mod network;
pub mod protocol_version;
pub mod prover_dal;
pub mod settlement;
pub mod tee_types;
pub mod url;
pub mod vm_version;
Expand Down
16 changes: 16 additions & 0 deletions core/lib/basic_types/src/settlement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use serde::{Deserialize, Serialize};

/// An enum which is used to describe whether a zkSync network settles to L1 or to the gateway.
/// Gateway is an Ethereum-compatible L2 and so it requires different treatment with regards to DA handling.
#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum SettlementMode {
#[default]
SettlesToL1,
Gateway,
}

impl SettlementMode {
pub fn is_gateway(self) -> bool {
matches!(self, Self::Gateway)
}
}
8 changes: 7 additions & 1 deletion core/lib/config/src/configs/eth_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::time::Duration;

use anyhow::Context as _;
use serde::Deserialize;
use zksync_basic_types::H256;
use zksync_basic_types::{settlement::SettlementMode, H256};
use zksync_crypto_primitives::K256PrivateKey;

use crate::EthWatchConfig;
Expand Down Expand Up @@ -54,6 +54,7 @@ impl EthConfig {
num_samples_for_blob_base_fee_estimate: 10,
internal_pubdata_pricing_multiplier: 1.0,
max_blob_base_fee: None,
settlement_mode: Default::default(),
}),
watcher: Some(EthWatchConfig {
confirmations_for_eth_event: None,
Expand Down Expand Up @@ -82,6 +83,7 @@ pub enum PubdataSendingMode {
Calldata,
Blobs,
Custom,
RelayedL2Calldata,
}

#[derive(Debug, Deserialize, Clone, PartialEq)]
Expand Down Expand Up @@ -181,6 +183,10 @@ pub struct GasAdjusterConfig {
pub internal_pubdata_pricing_multiplier: f64,
/// Max blob base fee that is allowed to be used.
pub max_blob_base_fee: Option<u64>,
/// Whether the gas adjuster should require that the L2 node is used as a settlement layer.
/// It offers a runtime check for correctly provided values.
#[serde(default)]
pub settlement_mode: SettlementMode,
}

impl GasAdjusterConfig {
Expand Down
2 changes: 2 additions & 0 deletions core/lib/config/src/testonly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ impl Distribution<configs::eth_sender::GasAdjusterConfig> for EncodeDist {
num_samples_for_blob_base_fee_estimate: self.sample(rng),
internal_pubdata_pricing_multiplier: self.sample(rng),
max_blob_base_fee: self.sample(rng),
// TODO(EVM-676): generate it randomly once this value is used
settlement_mode: Default::default(),
}
}
}
Expand Down

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

This file was deleted.

32 changes: 27 additions & 5 deletions core/lib/dal/src/blocks_web3_dal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use zksync_db_connection::{
use zksync_system_constants::EMPTY_UNCLES_HASH;
use zksync_types::{
api,
fee_model::BatchFeeInput,
l2_to_l1_log::L2ToL1Log,
vm_trace::Call,
web3::{BlockHeader, Bytes},
Expand Down Expand Up @@ -564,17 +565,21 @@ impl BlocksWeb3Dal<'_, '_> {
.collect())
}

/// Returns `base_fee_per_gas` for L2 block range [min(newest_block - block_count + 1, 0), newest_block]
/// Returns `base_fee_per_gas` and `fair_pubdata_price` for L2 block range [min(newest_block - block_count + 1, 0), newest_block]
/// in descending order of L2 block numbers.
pub async fn get_fee_history(
&mut self,
newest_block: L2BlockNumber,
block_count: u64,
) -> DalResult<Vec<U256>> {
) -> DalResult<(Vec<U256>, Vec<U256>)> {
let result: Vec<_> = sqlx::query!(
r#"
SELECT
base_fee_per_gas
base_fee_per_gas,
l2_fair_gas_price,
fair_pubdata_price,
protocol_version,
l1_gas_price
FROM
miniblocks
WHERE
Expand All @@ -593,10 +598,27 @@ impl BlocksWeb3Dal<'_, '_> {
.fetch_all(self.storage)
.await?
.into_iter()
.map(|row| bigdecimal_to_u256(row.base_fee_per_gas))
.map(|row| {
let fee_input = BatchFeeInput::for_protocol_version(
row.protocol_version
.map(|x| (x as u16).try_into().unwrap())
.unwrap_or_else(ProtocolVersionId::last_potentially_undefined),
row.l2_fair_gas_price as u64,
row.fair_pubdata_price.map(|x| x as u64),
row.l1_gas_price as u64,
);

(
bigdecimal_to_u256(row.base_fee_per_gas),
U256::from(fee_input.fair_pubdata_price()),
)
})
.collect();

Ok(result)
let (base_fee_per_gas, effective_pubdata_price): (Vec<U256>, Vec<U256>) =
result.into_iter().unzip();

Ok((base_fee_per_gas, effective_pubdata_price))
}

pub async fn get_block_details(
Expand Down
1 change: 1 addition & 0 deletions core/lib/env_config/src/eth_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ mod tests {
num_samples_for_blob_base_fee_estimate: 10,
internal_pubdata_pricing_multiplier: 1.0,
max_blob_base_fee: None,
settlement_mode: Default::default(),
}),
watcher: Some(EthWatchConfig {
confirmations_for_eth_event: Some(0),
Expand Down
4 changes: 2 additions & 2 deletions core/lib/eth_client/src/clients/http/decl.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use jsonrpsee::proc_macros::rpc;
use zksync_types::{web3, Address, H256, U256, U64};
use zksync_web3_decl::client::{ForNetwork, L1};
use zksync_web3_decl::client::ForWeb3Network;

/// Subset of the L1 `eth` namespace used by the L1 client.
#[rpc(client, namespace = "eth", client_bounds(Self: ForNetwork<Net = L1>))]
#[rpc(client, namespace = "eth", client_bounds(Self: ForWeb3Network))]
pub(super) trait L1EthNamespace {
#[method(name = "chainId")]
async fn chain_id(&self) -> RpcResult<U256>;
Expand Down
1 change: 1 addition & 0 deletions core/lib/eth_client/src/clients/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum Method {
#[metrics(name = "sign_prepared_tx_for_addr")]
SignPreparedTx,
Allowance,
L2FeeHistory,
}

#[derive(Debug, Metrics)]
Expand Down
Loading

0 comments on commit e3f7804

Please sign in to comment.