From e71f6f96bda08f8330c643a31df4ef9e82c9afc2 Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Sun, 2 Jun 2024 14:02:48 +0200 Subject: [PATCH] fix(api): correct default fee data in eth call (#2072) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Previously, if no `max_fee_per_gas` was provided, it would mean that we would use 0 as the base fee. This would also require the gas per pubdata to be 0. With this PR we will now use the current gas per pubdata. This PR also provides a large eth call gas limit to ensure that it will work fine even under very high L1 gas price ## Why ❔ ## Checklist - [ ] 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`. - [ ] Spellcheck has been run via `zk spellcheck`. --- core/lib/multivm/src/utils.rs | 31 +++++++++- .../src/versions/vm_latest/constants.rs | 4 +- core/lib/types/src/transaction_request.rs | 57 ++++++++++------- .../api_server/src/execution_sandbox/apply.rs | 6 +- .../src/execution_sandbox/execute.rs | 21 +++---- core/node/api_server/src/tx_sender/mod.rs | 20 +++++- .../api_server/src/web3/namespaces/debug.rs | 16 ++++- .../api_server/src/web3/namespaces/eth.rs | 21 ++++++- .../interfaces/ISystemContext.sol | 61 +++++++++++++++++++ core/tests/ts-integration/src/env.ts | 6 +- core/tests/ts-integration/src/helpers.ts | 2 + core/tests/ts-integration/tests/fees.test.ts | 12 ++++ .../tests/ts-integration/tests/system.test.ts | 17 +++++- .../crates/types/src/protocol_version.rs | 8 +-- 14 files changed, 228 insertions(+), 54 deletions(-) create mode 100644 core/tests/ts-integration/contracts/custom-account/interfaces/ISystemContext.sol diff --git a/core/lib/multivm/src/utils.rs b/core/lib/multivm/src/utils.rs index 1f4d55ea66ae..a15fdba6b703 100644 --- a/core/lib/multivm/src/utils.rs +++ b/core/lib/multivm/src/utils.rs @@ -441,8 +441,35 @@ pub fn get_max_batch_gas_limit(version: VmVersion) -> u64 { } VmVersion::Vm1_4_1 => crate::vm_1_4_1::constants::BLOCK_GAS_LIMIT as u64, VmVersion::Vm1_4_2 => crate::vm_1_4_2::constants::BLOCK_GAS_LIMIT as u64, - VmVersion::Vm1_5_0SmallBootloaderMemory => crate::vm_latest::constants::BATCH_GAS_LIMIT, - VmVersion::Vm1_5_0IncreasedBootloaderMemory => crate::vm_latest::constants::BATCH_GAS_LIMIT, + VmVersion::Vm1_5_0SmallBootloaderMemory | VmVersion::Vm1_5_0IncreasedBootloaderMemory => { + crate::vm_latest::constants::BATCH_GAS_LIMIT + } + } +} + +pub fn get_eth_call_gas_limit(version: VmVersion) -> u64 { + match version { + VmVersion::M5WithRefunds | VmVersion::M5WithoutRefunds => { + crate::vm_m5::utils::ETH_CALL_GAS_LIMIT as u64 + } + VmVersion::M6Initial | VmVersion::M6BugWithCompressionFixed => { + crate::vm_m6::utils::ETH_CALL_GAS_LIMIT as u64 + } + VmVersion::Vm1_3_2 => crate::vm_1_3_2::utils::ETH_CALL_GAS_LIMIT as u64, + VmVersion::VmVirtualBlocks => { + crate::vm_virtual_blocks::constants::ETH_CALL_GAS_LIMIT as u64 + } + VmVersion::VmVirtualBlocksRefundsEnhancement => { + crate::vm_refunds_enhancement::constants::ETH_CALL_GAS_LIMIT as u64 + } + VmVersion::VmBoojumIntegration => { + crate::vm_boojum_integration::constants::ETH_CALL_GAS_LIMIT as u64 + } + VmVersion::Vm1_4_1 => crate::vm_1_4_1::constants::ETH_CALL_GAS_LIMIT as u64, + VmVersion::Vm1_4_2 => crate::vm_1_4_2::constants::ETH_CALL_GAS_LIMIT as u64, + VmVersion::Vm1_5_0SmallBootloaderMemory | VmVersion::Vm1_5_0IncreasedBootloaderMemory => { + crate::vm_latest::constants::ETH_CALL_GAS_LIMIT + } } } diff --git a/core/lib/multivm/src/versions/vm_latest/constants.rs b/core/lib/multivm/src/versions/vm_latest/constants.rs index 1f02162f7348..01f697ec91a2 100644 --- a/core/lib/multivm/src/versions/vm_latest/constants.rs +++ b/core/lib/multivm/src/versions/vm_latest/constants.rs @@ -3,7 +3,7 @@ use zk_evm_1_5_0::aux_structures::MemoryPage; pub use zk_evm_1_5_0::zkevm_opcode_defs::system_params::{ ERGS_PER_CIRCUIT, INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; -use zksync_system_constants::{MAX_L2_TX_GAS_LIMIT, MAX_NEW_FACTORY_DEPS}; +use zksync_system_constants::MAX_NEW_FACTORY_DEPS; use super::vm::MultiVMSubversion; use crate::vm_latest::old_vm::utils::heap_page_from_base; @@ -160,7 +160,7 @@ pub const BATCH_COMPUTATIONAL_GAS_LIMIT: u32 = pub const BATCH_GAS_LIMIT: u64 = 1 << 50; /// How many gas is allowed to spend on a single transaction in eth_call method -pub const ETH_CALL_GAS_LIMIT: u32 = MAX_L2_TX_GAS_LIMIT as u32; +pub const ETH_CALL_GAS_LIMIT: u64 = BATCH_GAS_LIMIT; /// ID of the transaction from L1 pub const L1_TX_TYPE: u8 = 255; diff --git a/core/lib/types/src/transaction_request.rs b/core/lib/types/src/transaction_request.rs index 72551d762d18..c2526cc3ed6f 100644 --- a/core/lib/types/src/transaction_request.rs +++ b/core/lib/types/src/transaction_request.rs @@ -66,11 +66,32 @@ pub struct CallRequest { pub eip712_meta: Option, } +/// While some default parameters are usually provided for the `eth_call` methods, +/// sometimes users may want to override those. +pub struct CallOverrides { + pub enforced_base_fee: Option, +} + impl CallRequest { /// Function to return a builder for a Call Request pub fn builder() -> CallRequestBuilder { CallRequestBuilder::default() } + + pub fn get_call_overrides(&self) -> Result { + let provided_gas_price = self.max_fee_per_gas.or(self.gas_price); + let enforced_base_fee = if let Some(provided_gas_price) = provided_gas_price { + Some( + provided_gas_price + .try_into() + .map_err(|_| SerializationTransactionError::MaxFeePerGasNotU64)?, + ) + } else { + None + }; + + Ok(CallOverrides { enforced_base_fee }) + } } /// Call Request Builder @@ -183,10 +204,16 @@ pub enum SerializationTransactionError { AccessListsNotSupported, #[error("nonce has max value")] TooBigNonce, - /// Sanity check error to avoid extremely big numbers specified + + /// Sanity checks to avoid extremely big numbers specified /// to gas and pubdata price. - #[error("{0}")] - TooHighGas(String), + #[error("max fee per gas higher than 2^64-1")] + MaxFeePerGasNotU64, + #[error("max fee per pubdata byte higher than 2^64-1")] + MaxFeePerPubdataByteNotU64, + #[error("max priority fee per gas higher than 2^64-1")] + MaxPriorityFeePerGasNotU64, + /// OversizedData is returned if the raw tx size is greater /// than some meaningful limit a user might use. This is not a consensus error /// making the transaction invalid, rather a DOS protection. @@ -736,16 +763,12 @@ impl TransactionRequest { fn get_fee_data_checked(&self) -> Result { if self.gas_price > u64::MAX.into() { - return Err(SerializationTransactionError::TooHighGas( - "max fee per gas higher than 2^64-1".to_string(), - )); + return Err(SerializationTransactionError::MaxFeePerGasNotU64); } let gas_per_pubdata_limit = if let Some(meta) = &self.eip712_meta { if meta.gas_per_pubdata > u64::MAX.into() { - return Err(SerializationTransactionError::TooHighGas( - "max fee per pubdata byte higher than 2^64-1".to_string(), - )); + return Err(SerializationTransactionError::MaxFeePerPubdataByteNotU64); } else if meta.gas_per_pubdata == U256::zero() { return Err(SerializationTransactionError::GasPerPubDataLimitZero); } @@ -757,9 +780,7 @@ impl TransactionRequest { let max_priority_fee_per_gas = self.max_priority_fee_per_gas.unwrap_or(self.gas_price); if max_priority_fee_per_gas > u64::MAX.into() { - return Err(SerializationTransactionError::TooHighGas( - "max priority fee per gas higher than 2^64-1".to_string(), - )); + return Err(SerializationTransactionError::MaxPriorityFeePerGasNotU64); } Ok(Fee { @@ -1316,9 +1337,7 @@ mod tests { L2Tx::from_request(tx1, usize::MAX); assert_eq!( execute_tx1.unwrap_err(), - SerializationTransactionError::TooHighGas( - "max fee per gas higher than 2^64-1".to_string() - ) + SerializationTransactionError::MaxFeePerGasNotU64 ); let tx2 = TransactionRequest { @@ -1332,9 +1351,7 @@ mod tests { L2Tx::from_request(tx2, usize::MAX); assert_eq!( execute_tx2.unwrap_err(), - SerializationTransactionError::TooHighGas( - "max priority fee per gas higher than 2^64-1".to_string() - ) + SerializationTransactionError::MaxPriorityFeePerGasNotU64 ); let tx3 = TransactionRequest { @@ -1352,9 +1369,7 @@ mod tests { L2Tx::from_request(tx3, usize::MAX); assert_eq!( execute_tx3.unwrap_err(), - SerializationTransactionError::TooHighGas( - "max fee per pubdata byte higher than 2^64-1".to_string() - ) + SerializationTransactionError::MaxFeePerPubdataByteNotU64 ); } diff --git a/core/node/api_server/src/execution_sandbox/apply.rs b/core/node/api_server/src/execution_sandbox/apply.rs index d3af1a5c9dd4..dc8b56f41967 100644 --- a/core/node/api_server/src/execution_sandbox/apply.rs +++ b/core/node/api_server/src/execution_sandbox/apply.rs @@ -403,12 +403,12 @@ impl StoredL2BlockInfo { } #[derive(Debug)] -struct ResolvedBlockInfo { +pub(crate) struct ResolvedBlockInfo { state_l2_block_number: L2BlockNumber, state_l2_block_hash: H256, vm_l1_batch_number: L1BatchNumber, l1_batch_timestamp: u64, - protocol_version: ProtocolVersionId, + pub(crate) protocol_version: ProtocolVersionId, historical_fee_input: Option, } @@ -429,7 +429,7 @@ impl BlockArgs { ) } - async fn resolve_block_info( + pub(crate) async fn resolve_block_info( &self, connection: &mut Connection<'_, Core>, ) -> anyhow::Result { diff --git a/core/node/api_server/src/execution_sandbox/execute.rs b/core/node/api_server/src/execution_sandbox/execute.rs index 2fd5b376acb3..72c94e2a428c 100644 --- a/core/node/api_server/src/execution_sandbox/execute.rs +++ b/core/node/api_server/src/execution_sandbox/execute.rs @@ -4,14 +4,13 @@ use anyhow::Context as _; use multivm::{ interface::{TxExecutionMode, VmExecutionResultAndLogs, VmInterface}, tracers::StorageInvocations, - vm_latest::constants::ETH_CALL_GAS_LIMIT, MultiVMTracer, }; use tracing::{span, Level}; use zksync_dal::{ConnectionPool, Core}; use zksync_types::{ - fee::TransactionExecutionMetrics, l2::L2Tx, ExecuteTransactionCommon, Nonce, - PackedEthSignature, Transaction, U256, + fee::TransactionExecutionMetrics, l2::L2Tx, transaction_request::CallOverrides, + ExecuteTransactionCommon, Nonce, PackedEthSignature, Transaction, U256, }; use super::{ @@ -40,7 +39,7 @@ impl TxExecutionArgs { } fn for_eth_call( - enforced_base_fee: u64, + enforced_base_fee: Option, vm_execution_cache_misses_limit: Option, ) -> Self { let missed_storage_invocation_limit = vm_execution_cache_misses_limit.unwrap_or(usize::MAX); @@ -48,7 +47,7 @@ impl TxExecutionArgs { execution_mode: TxExecutionMode::EthCall, enforced_nonce: None, added_balance: U256::zero(), - enforced_base_fee: Some(enforced_base_fee), + enforced_base_fee, missed_storage_invocation_limit, } } @@ -170,23 +169,21 @@ impl TransactionExecutor { vm_permit: VmPermit, shared_args: TxSharedArgs, connection_pool: ConnectionPool, + call_overrides: CallOverrides, mut tx: L2Tx, block_args: BlockArgs, vm_execution_cache_misses_limit: Option, custom_tracers: Vec, ) -> anyhow::Result { - let enforced_base_fee = tx.common_data.fee.max_fee_per_gas.as_u64(); - let execution_args = - TxExecutionArgs::for_eth_call(enforced_base_fee, vm_execution_cache_misses_limit); + let execution_args = TxExecutionArgs::for_eth_call( + call_overrides.enforced_base_fee, + vm_execution_cache_misses_limit, + ); if tx.common_data.signature.is_empty() { tx.common_data.signature = PackedEthSignature::default().serialize_packed().into(); } - // Protection against infinite-loop eth_calls and alike: - // limiting the amount of gas the call can use. - // We can't use `BLOCK_ERGS_LIMIT` here since the VM itself has some overhead. - tx.common_data.fee.gas_limit = ETH_CALL_GAS_LIMIT.into(); let output = self .execute_tx_in_sandbox( vm_permit, diff --git a/core/node/api_server/src/tx_sender/mod.rs b/core/node/api_server/src/tx_sender/mod.rs index 9e6bd86415f9..1b13e50b410f 100644 --- a/core/node/api_server/src/tx_sender/mod.rs +++ b/core/node/api_server/src/tx_sender/mod.rs @@ -7,7 +7,7 @@ use multivm::{ interface::VmExecutionResultAndLogs, utils::{ adjust_pubdata_price_for_tx, derive_base_fee_and_gas_per_pubdata, derive_overhead, - get_max_batch_gas_limit, + get_eth_call_gas_limit, get_max_batch_gas_limit, }, vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, }; @@ -28,6 +28,7 @@ use zksync_types::{ fee_model::BatchFeeInput, get_code_key, get_intrinsic_constants, l2::{error::TxCheckError::TxDuplication, L2Tx}, + transaction_request::CallOverrides, utils::storage_key_for_eth_balance, AccountTreeId, Address, ExecuteTransactionCommon, L2ChainId, Nonce, PackedEthSignature, ProtocolVersionId, Transaction, VmVersion, H160, H256, MAX_L2_TX_GAS_LIMIT, @@ -965,6 +966,7 @@ impl TxSender { pub(super) async fn eth_call( &self, block_args: BlockArgs, + call_overrides: CallOverrides, tx: L2Tx, ) -> Result, SubmitTxError> { let vm_permit = self.0.vm_concurrency_limiter.acquire().await; @@ -977,6 +979,7 @@ impl TxSender { vm_permit, self.shared_args().await?, self.0.replica_connection_pool.clone(), + call_overrides, tx, block_args, vm_execution_cache_misses_limit, @@ -1036,4 +1039,19 @@ impl TxSender { } Ok(()) } + + pub(crate) async fn get_default_eth_call_gas( + &self, + block_args: BlockArgs, + ) -> anyhow::Result { + let mut connection = self.acquire_replica_connection().await?; + + let protocol_version = block_args + .resolve_block_info(&mut connection) + .await + .context("failed to resolve block info")? + .protocol_version; + + Ok(get_eth_call_gas_limit(protocol_version.into())) + } } diff --git a/core/node/api_server/src/web3/namespaces/debug.rs b/core/node/api_server/src/web3/namespaces/debug.rs index 4b998adcfeb8..400711de8593 100644 --- a/core/node/api_server/src/web3/namespaces/debug.rs +++ b/core/node/api_server/src/web3/namespaces/debug.rs @@ -125,7 +125,7 @@ impl DebugNamespace { pub async fn debug_trace_call_impl( &self, - request: CallRequest, + mut request: CallRequest, block_id: Option, options: Option, ) -> Result { @@ -148,6 +148,19 @@ impl DebugNamespace { .last_sealed_l2_block .diff_with_block_args(&block_args), ); + + if request.gas.is_none() { + request.gas = Some( + self.state + .tx_sender + .get_default_eth_call_gas(block_args) + .await + .map_err(Web3Error::InternalError)? + .into(), + ) + } + + let call_overrides = request.get_call_overrides()?; let tx = L2Tx::from_request(request.into(), MAX_ENCODED_TX_SIZE)?; let shared_args = self.shared_args().await; @@ -173,6 +186,7 @@ impl DebugNamespace { vm_permit, shared_args, self.state.connection_pool.clone(), + call_overrides, tx.clone(), block_args, self.sender_config().vm_execution_cache_misses_limit, diff --git a/core/node/api_server/src/web3/namespaces/eth.rs b/core/node/api_server/src/web3/namespaces/eth.rs index ff2403051de0..b1541f7261bf 100644 --- a/core/node/api_server/src/web3/namespaces/eth.rs +++ b/core/node/api_server/src/web3/namespaces/eth.rs @@ -52,7 +52,7 @@ impl EthNamespace { pub async fn call_impl( &self, - request: CallRequest, + mut request: CallRequest, block_id: Option, ) -> Result { let block_id = block_id.unwrap_or(BlockId::Number(BlockNumber::Pending)); @@ -70,8 +70,25 @@ impl EthNamespace { ); drop(connection); + if request.gas.is_none() { + request.gas = Some( + self.state + .tx_sender + .get_default_eth_call_gas(block_args) + .await + .map_err(Web3Error::InternalError)? + .into(), + ) + } + let call_overrides = request.get_call_overrides()?; let tx = L2Tx::from_request(request.into(), self.state.api_config.max_tx_size)?; - let call_result = self.state.tx_sender.eth_call(block_args, tx).await?; + + // It is assumed that the previous checks has already enforced that the `max_fee_per_gas` is at most u64. + let call_result: Vec = self + .state + .tx_sender + .eth_call(block_args, call_overrides, tx) + .await?; Ok(call_result.into()) } diff --git a/core/tests/ts-integration/contracts/custom-account/interfaces/ISystemContext.sol b/core/tests/ts-integration/contracts/custom-account/interfaces/ISystemContext.sol new file mode 100644 index 000000000000..6b83f6d6ada4 --- /dev/null +++ b/core/tests/ts-integration/contracts/custom-account/interfaces/ISystemContext.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @author Matter Labs + * @custom:security-contact security@matterlabs.dev + * @notice Contract that stores some of the context variables, that may be either + * block-scoped, tx-scoped or system-wide. + */ +interface ISystemContext { + struct BlockInfo { + uint128 timestamp; + uint128 number; + } + + /// @notice A structure representing the timeline for the upgrade from the batch numbers to the L2 block numbers. + /// @dev It will be used for the L1 batch -> L2 block migration in Q3 2023 only. + struct VirtualBlockUpgradeInfo { + /// @notice In order to maintain consistent results for `blockhash` requests, we'll + /// have to remember the number of the batch when the upgrade to the virtual blocks has been done. + /// The hashes for virtual blocks before the upgrade are identical to the hashes of the corresponding batches. + uint128 virtualBlockStartBatch; + /// @notice L2 block when the virtual blocks have caught up with the L2 blocks. Starting from this block, + /// all the information returned to users for block.timestamp/number, etc should be the information about the L2 blocks and + /// not virtual blocks. + uint128 virtualBlockFinishL2Block; + } + + function chainId() external view returns (uint256); + + function origin() external view returns (address); + + function gasPrice() external view returns (uint256); + + function blockGasLimit() external view returns (uint256); + + function coinbase() external view returns (address); + + function difficulty() external view returns (uint256); + + function baseFee() external view returns (uint256); + + function txNumberInBlock() external view returns (uint16); + + function getBlockHashEVM(uint256 _block) external view returns (bytes32); + + function getBatchHash(uint256 _batchNumber) external view returns (bytes32 hash); + + function getBlockNumber() external view returns (uint128); + + function getBlockTimestamp() external view returns (uint128); + + function getBatchNumberAndTimestamp() external view returns (uint128 blockNumber, uint128 blockTimestamp); + + function getL2BlockNumberAndTimestamp() external view returns (uint128 blockNumber, uint128 blockTimestamp); + + function gasPerPubdataByte() external view returns (uint256 gasPerPubdataByte); + + function getCurrentPubdataSpent() external view returns (uint256 currentPubdataSpent); +} diff --git a/core/tests/ts-integration/src/env.ts b/core/tests/ts-integration/src/env.ts index ada8a695e0aa..ddbb8227dc60 100644 --- a/core/tests/ts-integration/src/env.ts +++ b/core/tests/ts-integration/src/env.ts @@ -222,8 +222,10 @@ export async function loadTestEnvironmentFromEnv(): Promise { const baseTokenAddressL2 = L2_BASE_TOKEN_ADDRESS; const l2ChainId = parseInt(process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID!); - const l1BatchCommitDataGeneratorMode = process.env - .CHAIN_STATE_KEEPER_L1_BATCH_COMMIT_DATA_GENERATOR_MODE! as DataAvailabityMode; + // If the `CHAIN_STATE_KEEPER_L1_BATCH_COMMIT_DATA_GENERATOR_MODE` is not set, the default value is `Rollup`. + const l1BatchCommitDataGeneratorMode = (process.env.CHAIN_STATE_KEEPER_L1_BATCH_COMMIT_DATA_GENERATOR_MODE || + process.env.EN_L1_BATCH_COMMIT_DATA_GENERATOR_MODE || + 'Rollup') as DataAvailabityMode; let minimalL2GasPrice; if (process.env.CHAIN_STATE_KEEPER_MINIMAL_L2_GAS_PRICE !== undefined) { minimalL2GasPrice = ethers.BigNumber.from(process.env.CHAIN_STATE_KEEPER_MINIMAL_L2_GAS_PRICE!); diff --git a/core/tests/ts-integration/src/helpers.ts b/core/tests/ts-integration/src/helpers.ts index 966a77b3fb8e..d3464bc84bdd 100644 --- a/core/tests/ts-integration/src/helpers.ts +++ b/core/tests/ts-integration/src/helpers.ts @@ -4,6 +4,8 @@ import * as ethers from 'ethers'; import * as hre from 'hardhat'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-solc/dist/src/types'; +export const SYSTEM_CONTEXT_ADDRESS = '0x000000000000000000000000000000000000800b'; + /** * Loads the test contract * diff --git a/core/tests/ts-integration/tests/fees.test.ts b/core/tests/ts-integration/tests/fees.test.ts index a2a72cfa5be3..699b9e5e886b 100644 --- a/core/tests/ts-integration/tests/fees.test.ts +++ b/core/tests/ts-integration/tests/fees.test.ts @@ -16,8 +16,11 @@ import { TestMaster } from '../src/index'; import * as zksync from 'zksync-ethers'; import { BigNumber, ethers } from 'ethers'; import { DataAvailabityMode, Token } from '../src/types'; +import { keccak256 } from 'ethers/lib/utils'; +import { SYSTEM_CONTEXT_ADDRESS, getTestContract } from '../src/helpers'; const UINT32_MAX = BigNumber.from(2).pow(32).sub(1); +const MAX_GAS_PER_PUBDATA = 50_000; const logs = fs.createWriteStream('fees.log', { flags: 'a' }); @@ -168,6 +171,15 @@ testFees('Test fees', () => { const receipt = await tx.wait(); expect(receipt.gasUsed.gt(UINT32_MAX)).toBeTruthy(); + // Let's also check that the same transaction would work as eth_call + const systemContextArtifact = getTestContract('ISystemContext'); + const systemContext = new ethers.Contract(SYSTEM_CONTEXT_ADDRESS, systemContextArtifact.abi, alice.provider); + const systemContextGasPerPubdataByte = await systemContext.gasPerPubdataByte(); + expect(systemContextGasPerPubdataByte.toNumber()).toEqual(MAX_GAS_PER_PUBDATA); + + const dataHash = await l1Messenger.callStatic.sendToL1(largeData, { type: 0 }); + expect(dataHash).toEqual(keccak256(largeData)); + // Secondly, let's test an unsuccessful transaction with large refund. // The size of the data has increased, so the previous gas limit is not enough. diff --git a/core/tests/ts-integration/tests/system.test.ts b/core/tests/ts-integration/tests/system.test.ts index c46916c4ec67..2934226eed8f 100644 --- a/core/tests/ts-integration/tests/system.test.ts +++ b/core/tests/ts-integration/tests/system.test.ts @@ -13,7 +13,8 @@ import * as zksync from 'zksync-ethers'; import * as ethers from 'ethers'; import { BigNumberish, BytesLike } from 'ethers'; import { hashBytecode, serialize } from 'zksync-ethers/build/utils'; -import { getTestContract } from '../src/helpers'; +import { SYSTEM_CONTEXT_ADDRESS, getTestContract } from '../src/helpers'; +import { DataAvailabityMode } from '../src/types'; const contracts = { counter: getTestContract('Counter'), @@ -311,6 +312,20 @@ describe('System behavior checks', () => { ).toBeAccepted(); }); + test('Gas per pubdata byte getter should work', async () => { + const systemContextArtifact = getTestContract('ISystemContext'); + const systemContext = new ethers.Contract(SYSTEM_CONTEXT_ADDRESS, systemContextArtifact.abi, alice.provider); + const currentGasPerPubdata = await systemContext.gasPerPubdataByte(); + + // The current gas per pubdata depends on a lot of factors, so it wouldn't be sustainable to check the exact value. + // We'll just check that it is greater than zero. + if (testMaster.environment().l1BatchCommitDataGeneratorMode === DataAvailabityMode.Rollup) { + expect(currentGasPerPubdata.toNumber()).toBeGreaterThan(0); + } else { + expect(currentGasPerPubdata.toNumber()).toEqual(0); + } + }); + it('should reject transaction with huge gas limit', async () => { await expect( alice.sendTransaction({ to: alice.address, gasLimit: ethers.BigNumber.from(2).pow(51) }) diff --git a/zk_toolbox/crates/types/src/protocol_version.rs b/zk_toolbox/crates/types/src/protocol_version.rs index 35ac74d3b5f8..5b619c883a3e 100644 --- a/zk_toolbox/crates/types/src/protocol_version.rs +++ b/zk_toolbox/crates/types/src/protocol_version.rs @@ -25,13 +25,7 @@ impl ProtocolSemanticVersion { impl fmt::Display for ProtocolSemanticVersion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}.{}.{}", - Self::MAJOR_VERSION, - self.minor as u16, - self.patch - ) + write!(f, "{}.{}.{}", Self::MAJOR_VERSION, self.minor, self.patch) } }