Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add anvil_{set,remove}BlockTimestampInterval #442

Merged
merged 7 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions SUPPORTED_APIS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ The `status` options are:

| Namespace | API | <div style="width:130px">Status</div> | Description |
| --- | --- | --- | --- |
| `ANVIL` | `anvil_setBlockTimestampInterval` | `SUPPORTED` | Sets the block timestamp interval |
| `ANVIL` | `anvil_removeBlockTimestampInterval` | `SUPPORTED` | Removes the block timestamp interval |
| `ANVIL` | `anvil_setMinGasPrice` | `NOT IMPLEMENTED` | Set the minimum gas price for the node. Unsupported for ZKsync as it is only relevant for pre-EIP1559 chains |
| `ANVIL` | `anvil_setLoggingEnabled` | `SUPPORTED` | Enables or disables logging |
| `ANVIL` | `anvil_snapshot` | `SUPPORTED` | Snapshot the state of the blockchain at the current block |
Expand Down
41 changes: 41 additions & 0 deletions e2e-tests/test/anvil-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,47 @@ import * as fs from "node:fs";

const provider = getTestProvider();

describe("anvil_setBlockTimestampInterval & anvil_removeBlockTimestampInterval", function () {
it("Should control timestamp interval between blocks", async function () {
// Arrange
const interval = 42;
let expectedTimestamp: number = await provider.send("config_getCurrentTimestamp", []);
expectedTimestamp += interval;
const wallet = new Wallet(RichAccounts[0].PrivateKey, provider);
const userWallet = Wallet.createRandom().connect(provider);

// Set interval
await provider.send("anvil_setBlockTimestampInterval", [interval]);

const txResponse = await wallet.sendTransaction({
to: userWallet.address,
value: ethers.utils.parseEther("0.1"),
});
const txReceipt = await txResponse.wait();

// Assert new block is `interval` apart from start
const newBlockTimestamp = (await provider.getBlock(txReceipt.blockNumber)).timestamp;
expect(newBlockTimestamp).to.equal(expectedTimestamp);

// Accomodate for virtual block
expectedTimestamp += interval;

// Remove interval
const result: boolean = await provider.send("anvil_removeBlockTimestampInterval", []);
expect(result);

const txResponse2 = await wallet.sendTransaction({
to: userWallet.address,
value: ethers.utils.parseEther("0.1"),
});
const txReceipt2 = await txResponse2.wait();

// Assert new block is `1` apart from previous block
const newBlockTimestamp2 = (await provider.getBlock(txReceipt2.blockNumber)).timestamp;
expect(newBlockTimestamp2).to.equal(expectedTimestamp + 1);
});
});

describe("anvil_setLoggingEnabled", function () {
it("Should disable and enable logging", async function () {
// Arrange
Expand Down
17 changes: 17 additions & 0 deletions src/namespaces/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ use crate::utils::Numeric;

#[rpc]
pub trait AnvilNamespaceT {
/// Sets the block timestamp interval. All future blocks' timestamps will
/// have the provided amount of seconds in-between of them. Does not affect
/// the block production interval.
///
/// # Arguments
///
/// * `seconds` - The minimum gas price to be set
#[rpc(name = "anvil_setBlockTimestampInterval")]
fn set_block_timestamp_interval(&self, seconds: u64) -> RpcResult<()>;

/// Removes the block timestamp interval if it exists.
///
/// # Returns
/// `true` if an existing interval was removed, `false` otherwise
#[rpc(name = "anvil_removeBlockTimestampInterval")]
fn remove_block_timestamp_interval(&self) -> RpcResult<bool>;

/// Set the minimum gas price for the node. Unsupported for ZKsync as it is only relevant for
/// pre-EIP1559 chains.
///
Expand Down
9 changes: 9 additions & 0 deletions src/node/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ use crate::{
impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> AnvilNamespaceT
for InMemoryNode<S>
{
fn set_block_timestamp_interval(&self, seconds: u64) -> RpcResult<()> {
self.time.set_block_timestamp_interval(seconds);
Ok(()).into_boxed_future()
}

fn remove_block_timestamp_interval(&self) -> RpcResult<bool> {
Ok(self.time.remove_block_timestamp_interval()).into_boxed_future()
}

fn set_min_gas_price(&self, _gas: U256) -> RpcResult<()> {
tracing::info!("anvil_setMinGasPrice is unsupported as ZKsync is a post-EIP1559 chain");
Err(into_jsrpc_error(Web3Error::MethodNotImplemented)).into_boxed_future()
Expand Down
2 changes: 1 addition & 1 deletion src/node/block_producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl<S: ForkSource + Clone + fmt::Debug> Future for BlockProducer<S> {
.contracts(TxExecutionMode::VerifyExecute, impersonating)
.clone();
pin.node
.seal_block(txs, base_system_contracts)
.seal_block(&mut pin.node.time.lock(), txs, base_system_contracts)
.expect("block sealing failed");
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/node/config_api.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use zksync_web3_decl::error::Web3Error;

use crate::node::time::ReadTime;
use crate::{
config::show_details::{ShowCalls, ShowGasDetails, ShowStorageLogs, ShowVMDetails},
fork::ForkSource,
Expand Down Expand Up @@ -37,7 +38,7 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> Configurat
}

fn config_get_current_timestamp(&self) -> Result<u64> {
Ok(self.time.last_timestamp())
Ok(self.time.current_timestamp())
}

fn config_set_show_calls(&self, value: String) -> Result<String> {
Expand Down
4 changes: 3 additions & 1 deletion src/node/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> DebugNames
) -> RpcResult<DebugCall> {
let only_top = options.is_some_and(|o| o.tracer_config.only_top_call);
let inner = self.get_inner().clone();
let time = self.time.clone();
Box::pin(async move {
if block.is_some() && !matches!(block, Some(BlockId::Number(BlockNumber::Latest))) {
return Err(jsonrpc_core::Error::invalid_params(
Expand All @@ -165,7 +166,8 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> DebugNames
let storage = StorageView::new(&inner.fork_storage).into_rc_ptr();

// init vm
let (mut l1_batch_env, _block_context) = inner.create_l1_batch_env(storage.clone());
let (mut l1_batch_env, _block_context) =
inner.create_l1_batch_env(&time, storage.clone());

// update the enforced_base_fee within l1_batch_env to match the logic in zksync_core
l1_batch_env.enforced_base_fee = Some(l2_tx.common_data.fee.max_fee_per_gas.as_u64());
Expand Down
18 changes: 4 additions & 14 deletions src/node/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> EthNamespa
}
};

let result: jsonrpc_core::Result<Fee> = reader.estimate_gas_impl(req);
let result: jsonrpc_core::Result<Fee> = reader.estimate_gas_impl(&self.time, req);
match result {
Ok(fee) => Ok(fee.gas_limit).into_boxed_future(),
Err(err) => return futures::future::err(err).boxed(),
Expand Down Expand Up @@ -2841,7 +2841,7 @@ mod tests {
inner.current_batch = 1;
inner.current_miniblock = 1;
inner.current_miniblock_hash = H256::repeat_byte(0x1);
inner.time.set_last_timestamp_unchecked(1);
node.time.set_current_timestamp_unchecked(1);
inner
.filters
.add_block_filter()
Expand All @@ -2858,7 +2858,6 @@ mod tests {

let storage = inner.fork_storage.inner.read().unwrap();
let expected_snapshot = Snapshot {
current_timestamp: inner.time.last_timestamp(),
current_batch: inner.current_batch,
current_miniblock: inner.current_miniblock,
current_miniblock_hash: inner.current_miniblock_hash,
Expand All @@ -2876,10 +2875,6 @@ mod tests {
};
let actual_snapshot = inner.snapshot().expect("failed taking snapshot");

assert_eq!(
expected_snapshot.current_timestamp,
actual_snapshot.current_timestamp
);
assert_eq!(
expected_snapshot.current_batch,
actual_snapshot.current_batch
Expand Down Expand Up @@ -2947,7 +2942,7 @@ mod tests {
inner.current_batch = 1;
inner.current_miniblock = 1;
inner.current_miniblock_hash = H256::repeat_byte(0x1);
inner.time.set_last_timestamp_unchecked(1);
node.time.set_current_timestamp_unchecked(1);
inner
.filters
.add_block_filter()
Expand All @@ -2965,7 +2960,6 @@ mod tests {
let expected_snapshot = {
let storage = inner.fork_storage.inner.read().unwrap();
Snapshot {
current_timestamp: inner.time.last_timestamp(),
current_batch: inner.current_batch,
current_miniblock: inner.current_miniblock,
current_miniblock_hash: inner.current_miniblock_hash,
Expand Down Expand Up @@ -3000,7 +2994,7 @@ mod tests {
inner.current_batch = 2;
inner.current_miniblock = 2;
inner.current_miniblock_hash = H256::repeat_byte(0x2);
inner.time.set_last_timestamp_unchecked(2);
node.time.set_current_timestamp_unchecked(2);
inner
.filters
.add_pending_transaction_filter()
Expand All @@ -3021,10 +3015,6 @@ mod tests {
.expect("failed restoring snapshot");

let storage = inner.fork_storage.inner.read().unwrap();
assert_eq!(
expected_snapshot.current_timestamp,
inner.time.last_timestamp()
);
assert_eq!(expected_snapshot.current_batch, inner.current_batch);
assert_eq!(expected_snapshot.current_miniblock, inner.current_miniblock);
assert_eq!(
Expand Down
Loading