From cee400d308d6390db70c24cf8ae7dd72dc72b482 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:59:41 +0530 Subject: [PATCH] feat(`anvil`): support mining with same block.timestamp (#9160) * feat(`anvil`): support mining with same block.timestamp * fix timestamp tests * fix --- crates/anvil/src/eth/backend/time.rs | 6 +- crates/anvil/tests/it/anvil_api.rs | 127 ++++++++++++++++++++++++++- crates/anvil/tests/it/fork.rs | 2 +- 3 files changed, 128 insertions(+), 7 deletions(-) diff --git a/crates/anvil/src/eth/backend/time.rs b/crates/anvil/src/eth/backend/time.rs index 0822baa04716..3ae9524f0e5e 100644 --- a/crates/anvil/src/eth/backend/time.rs +++ b/crates/anvil/src/eth/backend/time.rs @@ -70,9 +70,9 @@ impl TimeManager { /// Fails if it's before (or at the same time) the last timestamp pub fn set_next_block_timestamp(&self, timestamp: u64) -> Result<(), BlockchainError> { trace!(target: "time", "override next timestamp {}", timestamp); - if timestamp <= *self.last_timestamp.read() { + if timestamp < *self.last_timestamp.read() { return Err(BlockchainError::TimestampError(format!( - "{timestamp} is lower than or equal to previous block's timestamp" + "{timestamp} is lower than previous block's timestamp" ))) } self.next_exact_timestamp.write().replace(timestamp); @@ -112,7 +112,7 @@ impl TimeManager { (current.saturating_add(self.offset()) as u64, false) }; // Ensures that the timestamp is always increasing - if next_timestamp <= last_timestamp { + if next_timestamp < last_timestamp { next_timestamp = last_timestamp + 1; } let next_offset = update_offset.then_some((next_timestamp as i128) - current); diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index a6e36929cf25..da5372c7edb3 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -315,7 +315,7 @@ async fn test_set_next_timestamp() { let next = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); assert_eq!(next.header.number, 2); - assert!(next.header.timestamp > block.header.timestamp); + assert!(next.header.timestamp >= block.header.timestamp); } #[tokio::test(flavor = "multi_thread")] @@ -339,7 +339,7 @@ async fn test_evm_set_time() { api.evm_mine(None).await.unwrap(); let next = provider.get_block(BlockId::default(), false.into()).await.unwrap().unwrap(); - assert!(next.header.timestamp > block.header.timestamp); + assert!(next.header.timestamp >= block.header.timestamp); } #[tokio::test(flavor = "multi_thread")] @@ -608,7 +608,7 @@ async fn test_fork_revert_next_block_timestamp() { api.mine_one().await; let block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert!(block.header.timestamp > latest_block.header.timestamp); + assert!(block.header.timestamp >= latest_block.header.timestamp); } // test that after a snapshot revert, the env block is reset @@ -888,3 +888,124 @@ async fn test_arb_get_block() { assert_eq!(block.header.number, 1); } + +// Set next_block_timestamp same as previous block +// api.evm_set_next_block_timestamp(0).unwrap(); +#[tokio::test(flavor = "multi_thread")] +async fn test_mine_blk_with_prev_timestamp() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + + let init_blk = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); + + let init_number = init_blk.header.number; + let init_timestamp = init_blk.header.timestamp; + + // mock timestamp + api.evm_set_next_block_timestamp(init_timestamp).unwrap(); + + api.mine_one().await; + + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); + + let next_blk_num = block.header.number; + let next_blk_timestamp = block.header.timestamp; + + assert_eq!(next_blk_num, init_number + 1); + assert_eq!(next_blk_timestamp, init_timestamp); + + // Sleep for 1 second + tokio::time::sleep(Duration::from_secs(1)).await; + + // Subsequent block should have a greater timestamp than previous block + api.mine_one().await; + + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); + + let third_blk_num = block.header.number; + let third_blk_timestmap = block.header.timestamp; + + assert_eq!(third_blk_num, init_number + 2); + assert_ne!(third_blk_timestmap, next_blk_timestamp); + assert!(third_blk_timestmap > next_blk_timestamp); +} + +// increase time by 0 seconds i.e next_block_timestamp = prev_block_timestamp +// api.evm_increase_time(0).unwrap(); +#[tokio::test(flavor = "multi_thread")] +async fn test_increase_time_by_zero() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + + let init_blk = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); + + let init_number = init_blk.header.number; + let init_timestamp = init_blk.header.timestamp; + + let _ = api.evm_increase_time(U256::ZERO).await; + + api.mine_one().await; + + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); + + let next_blk_num = block.header.number; + let next_blk_timestamp = block.header.timestamp; + + assert_eq!(next_blk_num, init_number + 1); + assert_eq!(next_blk_timestamp, init_timestamp); +} + +// evm_mine(MineOptions::Timestamp(prev_block_timestamp)) +#[tokio::test(flavor = "multi_thread")] +async fn evm_mine_blk_with_same_timestamp() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + + let init_blk = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); + + let init_number = init_blk.header.number; + let init_timestamp = init_blk.header.timestamp; + + api.evm_mine(Some(MineOptions::Timestamp(Some(init_timestamp)))).await.unwrap(); + + let block = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); + + let next_blk_num = block.header.number; + let next_blk_timestamp = block.header.timestamp; + + assert_eq!(next_blk_num, init_number + 1); + assert_eq!(next_blk_timestamp, init_timestamp); +} + +// mine 4 blocks instantly. +#[tokio::test(flavor = "multi_thread")] +async fn test_mine_blks_with_same_timestamp() { + let (api, handle) = spawn(NodeConfig::test()).await; + let provider = handle.http_provider(); + + let init_blk = provider.get_block(BlockId::latest(), false.into()).await.unwrap().unwrap(); + + let init_number = init_blk.header.number; + let init_timestamp = init_blk.header.timestamp; + + // Mine 4 blocks instantly + let _ = api.anvil_mine(Some(U256::from(4)), None).await; + + let latest_blk_num = api.block_number().unwrap().to::(); + + assert_eq!(latest_blk_num, init_number + 4); + + let mut blk_futs = vec![]; + for i in 1..=4 { + blk_futs.push(provider.get_block(i.into(), false.into())); + } + + let blks = futures::future::join_all(blk_futs) + .await + .into_iter() + .map(|blk| blk.unwrap().unwrap().header.timestamp) + .collect::>(); + + // timestamps should be equal + assert_eq!(blks, vec![init_timestamp; 4]); +} diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index d8df936b1e0a..17e5f9bd4f92 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -897,7 +897,7 @@ async fn test_fork_block_timestamp() { api.anvil_mine(Some(U256::from(1)), None).await.unwrap(); let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert!(initial_block.header.timestamp < latest_block.header.timestamp); + assert!(initial_block.header.timestamp <= latest_block.header.timestamp); } #[tokio::test(flavor = "multi_thread")]