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(anvil): support state caching for forked historical blocks #1759

Open
Tracked by #8269
asafyish opened this issue May 29, 2022 · 10 comments
Open
Tracked by #8269

feat(anvil): support state caching for forked historical blocks #1759

asafyish opened this issue May 29, 2022 · 10 comments
Labels
C-anvil Command: anvil P-normal Priority: normal T-feature Type: feature

Comments

@asafyish
Copy link

Component

Anvil

Describe the feature you would like

I am running anvil in fork mode (connected to archive node), like so:

anvil --fork-url "https://eth-mainnet.alchemyapi.io/v2/XXXXXXXX" --fork-block-number 14577209

Then I am trying to read historical blocks data, which works, but only caches block 14577209, even though I am reading from the other blocks. The same scenario does create cache in hardhat and ganache. Since anvil is so much faster in the initial reading, I prefer using it.

This is how I am reading historical data:

const ethers = require("ethers");

const daiAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
const daiAbi = ["function name() view returns (string)"];

const provider = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545");
const daiContract = new ethers.Contract(daiAddress, daiAbi, provider);

for (let blockTag = 14577100; blockTag < 14577209; blockTag++) {
  const name = await daiContract.name({
    blockTag,
  });
  console.log(name);
}

Running

forge cache ls

returns:
-️ mainnet (2.1 kB)
-️ Block 14577209 (2.1 kB)

Additional context

forge 0.2.0 (6ca977f 2022-05-29T02:10:50.584077Z)

@asafyish asafyish added the T-feature Type: feature label May 29, 2022
@onbjerg onbjerg added this to Foundry May 29, 2022
@onbjerg onbjerg moved this to Todo in Foundry May 29, 2022
@mattsse
Copy link
Member

mattsse commented May 29, 2022

currently we only store the state of the forked block, and cache is blockwise.

if the block predates the fork we simply delegate the eth_call.
in order to support state caching for these blocks we'd need to execute these calls locally and instead let the evm translate this to storage requests so we can cache single storage slots.

which would be doable but would require some work.

I'm not sure how hardhat/ganache store that eth_call, are they storing state map or eth_call/response pairs?

@asafyish
Copy link
Author

I am not familiar with hardhat implementation, but looking into the cache folder, it seems they are creating a "request-{transaction-hash}.json" file, with the contents of the response inside. I don't see any block number reference, so I can only guess that part of the hash contains the block number, something like sha(blockNumber + transaction-data).

@mds1
Copy link
Collaborator

mds1 commented May 29, 2022

I looked into hardhat's caching approach a little while back (see #698 (comment)). The answer is yes, they store call/response pairs:

  • cache/hardhat-network-fork/network-{chainId}/ is the path containing cached responses for a given chain ID
  • Within that folder, each request is stored as a separate JSON file called request-{key}.json, where the key is a hash of {chain ID} {rpcMethod} {stringifiedParams}. You can see the implementation here.
  • The contents of each JSON file is simply the response of the request
  • They also don't cache blocks that are too recent and at risk of being reorged, and that threshold varies by network

Most (all?) of their implementation can be found here: https://github.com/nomiclabs/hardhat/blob/4f108b51fc7f87bcf7f173a4301b5973918b4903/packages/hardhat-core/src/internal/hardhat-network/jsonrpc/client.ts#L224-L475

@tynes
Copy link
Contributor

tynes commented May 30, 2022

Needing to implement chain specific logic for safe depth could be implemented in a more generic way if the corresponding blockhash is stored locally and always checked against the remote before using the cache. If they don't match, then flush the cache. That adds an extra http request but it can be amortized over many possible state lookups

@mds1
Copy link
Collaborator

mds1 commented May 30, 2022

Ah yes good point, did not mean to imply we should use hardhat's approach of a chain-specific reorg threshold—I agree the blockhash approach is better and forgot to mention it

@onbjerg onbjerg added the C-anvil Command: anvil label May 30, 2022
@onbjerg
Copy link
Member

onbjerg commented Jul 1, 2022

Is this still the case? If so, is this expected/is it fixable? cc @mattsse

@julien-devatom
Copy link

which would be doable but would require some work.

Is it still considered to have a call cache that can be dumped into a file?
My point is when you are running 1M times the same calls every day by using anvil. You can only leverage the in-memory caching of the storage. So when restarting anvil, you're restarting from scratch.

That would be an awesome feature.

@zerosnacks zerosnacks added this to the v1.0.0 milestone Jul 26, 2024
@zerosnacks zerosnacks changed the title Partial cache in forking mode feat(anvil): support state caching for forked historical blocks Aug 1, 2024
@zerosnacks zerosnacks added the P-normal Priority: normal label Sep 11, 2024
@grandizzy
Copy link
Collaborator

we now have

      --dump-state <PATH>
          Dump the state and block environment of chain on exit to the given file.
          If the value is a directory, the state will be written to `<VALUE>/state.json`.
      --load-state <PATH>
          Initialize the chain from a previously saved state snapshot

flags which could be used (we're fixing the issue reported in #8493 though)

Would these new options work for mentioned scenario? thank you

CC @julien-devatom @asafyish

@Maliksb11
Copy link

OK

@grandizzy
Copy link
Collaborator

ping @julien-devatom @asafyish could you pls check comment above? thanks!

@grandizzy grandizzy removed this from the v1.0.0 milestone Nov 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-anvil Command: anvil P-normal Priority: normal T-feature Type: feature
Projects
Status: Todo
Development

No branches or pull requests

9 participants