Skip to content

Commit

Permalink
Merge branch 'main' into db/fix-and-improve-debugging-logs
Browse files Browse the repository at this point in the history
  • Loading branch information
dutterbutter authored Nov 26, 2024
2 parents 9b5ef7e + ede79a3 commit 903eaae
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 6 deletions.
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_snapshot` | `SUPPORTED` | Snapshot the state of the blockchain at the current block |
| `ANVIL` | `anvil_revert` | `SUPPORTED` | Revert the state of the blockchain to a previous snapshot |
| `ANVIL` | `anvil_setTime` | `SUPPORTED` | Sets the internal clock time to the given timestamp |
| `ANVIL` | `anvil_increaseTime` | `SUPPORTED` | Jump forward in time by the given amount of time, in seconds |
| `ANVIL` | `anvil_setNextBlockTimestamp` | `SUPPORTED` | Works like `anvil_increaseTime`, but takes the exact timestamp that you want in the next block, and increases the time accordingly |
Expand Down
37 changes: 37 additions & 0 deletions e2e-tests/test/anvil-apis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,43 @@ import { BigNumber } from "ethers";

const provider = getTestProvider();

describe("anvil_snapshot", function () {
it("Should return incrementing snapshot ids", async function () {
const wallet = new Wallet(RichAccounts[6].PrivateKey);
const deployer = new Deployer(hre, wallet);
const greeter = await deployContract(deployer, "Greeter", ["Hi"]);
expect(await greeter.greet()).to.eq("Hi");

// Act
const snapshotId1: string = await provider.send("anvil_snapshot", []);
const snapshotId2: string = await provider.send("anvil_snapshot", []);

// Assert
expect(await greeter.greet()).to.eq("Hi");
expect(BigNumber.from(snapshotId2).toString()).to.eq(BigNumber.from(snapshotId1).add(1).toString());
});
});

describe("anvil_revert", function () {
it("Should revert with correct snapshot id", async function () {
const wallet = new Wallet(RichAccounts[6].PrivateKey);
const deployer = new Deployer(hre, wallet);
const greeter = await deployContract(deployer, "Greeter", ["Hi"]);
expect(await greeter.greet()).to.eq("Hi");
const snapshotId = await provider.send("anvil_snapshot", []);
const setGreetingTx = await greeter.setGreeting("Hola, mundo!");
await setGreetingTx.wait();
expect(await greeter.greet()).to.equal("Hola, mundo!");

// Act
const reverted: boolean = await provider.send("anvil_revert", [snapshotId]);

// Assert
expect(await greeter.greet()).to.eq("Hi");
expect(reverted).to.be.true;
});
});

describe("anvil_increaseTime", function () {
it("Should increase current timestamp of the node", async function () {
// Arrange
Expand Down
10 changes: 10 additions & 0 deletions src/config/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,15 @@ pub struct Cli {
/// [default: m/44'/60'/0'/0/]
#[arg(long, help_heading = "Account Configuration")]
pub derivation_path: Option<String>,

/// Enables automatic impersonation on startup. This allows any transaction sender to be
/// simulated as different accounts, which is useful for testing contract behavior.
#[arg(
long,
visible_alias = "auto-unlock",
help_heading = "Account Configuration"
)]
pub auto_impersonate: bool,
}

#[derive(Debug, Subcommand, Clone)]
Expand Down Expand Up @@ -288,6 +297,7 @@ impl Cli {
.with_log_level(self.log)
.with_log_file_path(self.log_file_path.clone())
.with_account_generator(self.account_generator())
.with_auto_impersonate(self.auto_impersonate)
.with_genesis_balance(genesis_balance)
.with_cache_config(self.cache.map(|cache_type| {
match cache_type {
Expand Down
10 changes: 10 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ pub struct TestNodeConfig {
pub account_generator: Option<AccountGenerator>,
/// Signer accounts that can sign messages/transactions
pub signer_accounts: Vec<PrivateKeySigner>,
/// Enable auto impersonation of accounts on startup
pub enable_auto_impersonate: bool,
/// Whether the node operates in offline mode
pub offline: bool,
/// The host the server will listen on
Expand Down Expand Up @@ -147,6 +149,7 @@ impl Default for TestNodeConfig {
account_generator: None,
genesis_accounts: genesis_accounts.clone(),
signer_accounts: genesis_accounts,
enable_auto_impersonate: false,
// 100ETH default balance
genesis_balance: U256::from(100u128 * 10u128.pow(18)),

Expand Down Expand Up @@ -696,6 +699,13 @@ impl TestNodeConfig {
.with_genesis_accounts(accounts)
}

/// Sets whether to enable autoImpersonate
#[must_use]
pub fn with_auto_impersonate(mut self, enable_auto_impersonate: bool) -> Self {
self.enable_auto_impersonate = enable_auto_impersonate;
self
}

/// Set the offline mode
#[must_use]
pub fn with_offline(mut self, offline: Option<bool>) -> Self {
Expand Down
5 changes: 1 addition & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,7 @@ async fn main() -> anyhow::Result<()> {
let fork_details = match command {
Command::Run => {
if config.offline {
tracing::warn!(
"Running in offline mode: default fee parameters will be used. \
To override, specify values in `config.toml` and use the `--config` flag."
);
tracing::warn!("Running in offline mode: default fee parameters will be used.");
None
} else {
// Initialize the client to get the fee params
Expand Down
23 changes: 23 additions & 0 deletions src/namespaces/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@ use crate::utils::Numeric;

#[rpc]
pub trait AnvilNamespaceT {
/// Snapshot the state of the blockchain at the current block. Takes no parameters. Returns the id of the snapshot
/// that was created. A snapshot can only be reverted once. After a successful `anvil_revert`, the same snapshot id cannot
/// be used again. Consider creating a new snapshot after each `anvil_revert` if you need to revert to the same
/// point multiple times.
///
/// # Returns
/// The `U64` identifier for this snapshot.
#[rpc(name = "anvil_snapshot")]
fn snapshot(&self) -> RpcResult<U64>;

/// Revert the state of the blockchain to a previous snapshot. Takes a single parameter,
/// which is the snapshot id to revert to. This deletes the given snapshot, as well as any snapshots
/// taken after (e.g.: reverting to id 0x1 will delete snapshots with ids 0x1, 0x2, etc.)
///
/// # Arguments
///
/// * `id` - The snapshot id to revert
///
/// # Returns
/// `true` if a snapshot was reverted, otherwise `false`.
#[rpc(name = "anvil_revert")]
fn revert(&self, id: U64) -> RpcResult<bool>;

/// Set the current timestamp for the node.
/// Warning: This will allow you to move backwards in time, which may cause new blocks to appear to be
/// mined before old blocks. This will result in an invalid state.
Expand Down
18 changes: 18 additions & 0 deletions src/node/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ use crate::{
impl<S: ForkSource + std::fmt::Debug + Clone + Send + Sync + 'static> AnvilNamespaceT
for InMemoryNode<S>
{
fn snapshot(&self) -> RpcResult<U64> {
self.snapshot()
.map_err(|err| {
tracing::error!("failed creating snapshot: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn revert(&self, id: U64) -> RpcResult<bool> {
self.revert_snapshot(id)
.map_err(|err| {
tracing::error!("failed reverting snapshot: {:?}", err);
into_jsrpc_error(Web3Error::InternalError(err))
})
.into_boxed_future()
}

fn set_time(&self, timestamp: Numeric) -> RpcResult<i128> {
self.set_time(timestamp)
.map_err(|err| {
Expand Down
15 changes: 13 additions & 2 deletions src/node/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ impl<S: std::fmt::Debug + ForkSource> InMemoryNodeInner<S> {
f.estimate_gas_scale_factor,
)
};
let impersonation_manager = ImpersonationManager::default();
if config.enable_auto_impersonate {
// Enable auto impersonation if configured
impersonation_manager.set_auto_impersonation(true);
}

InMemoryNodeInner {
time: TimestampManager::new(f.block_timestamp),
Expand All @@ -286,7 +291,7 @@ impl<S: std::fmt::Debug + ForkSource> InMemoryNodeInner<S> {
&updated_config.system_contracts_options,
updated_config.use_evm_emulator,
),
impersonation: Default::default(),
impersonation: impersonation_manager,
rich_accounts: HashSet::new(),
previous_states: Default::default(),
observability,
Expand All @@ -299,6 +304,12 @@ impl<S: std::fmt::Debug + ForkSource> InMemoryNodeInner<S> {
blocks.insert(block_hash, create_genesis(NON_FORK_FIRST_BLOCK_TIMESTAMP));
let fee_input_provider = TestNodeFeeInputProvider::default();

let impersonation_manager = ImpersonationManager::default();
if config.enable_auto_impersonate {
// Enable auto impersonation if configured
impersonation_manager.set_auto_impersonation(true);
}

InMemoryNodeInner {
time: TimestampManager::new(NON_FORK_FIRST_BLOCK_TIMESTAMP),
current_batch: 0,
Expand All @@ -321,7 +332,7 @@ impl<S: std::fmt::Debug + ForkSource> InMemoryNodeInner<S> {
&config.system_contracts_options,
config.use_evm_emulator,
),
impersonation: Default::default(),
impersonation: impersonation_manager,
rich_accounts: HashSet::new(),
previous_states: Default::default(),
observability,
Expand Down

0 comments on commit 903eaae

Please sign in to comment.