Skip to content

Conversation

@msooseth
Copy link
Collaborator

@msooseth msooseth commented Sep 10, 2025

Description

This PR changes the way we do RPC. Basically, we have a mutable cache that is inside Session. We also have a mutable latestBlockNum. The latestBlockNum is updated only ONCE, from Nothing to Just, the first time something with Latest is fetched. This way, Latest will become a fixed block, and from then on, we use that instead of Latest. The cache is such that we keep everything there that we ever fetched.

Cache: due to threads asking for the same data, it is possible that two threads both fetch the same data. However, since the blockchain is immutable, they'd get the same data back, so they'd store the same thing in the cache. So there is no hazard of concurrency, worst case we fetch the same data twice.

The only issue that can arise is that two threads could potentially ask for the Latest, and they get different values back. However, for test, the first thing we do normally is to: rpcDat = Fetch.mkRpcInfo blockUrlInfo mockData before launching any symbolic execution. So at that point, the Latest will be replaced with a value. For run there is no issue, as it's not multi-threaded. For symbolic, first thing we do is Just url -> liftIO $ Fetch.fetchBlockWithSession conf sess block url so that's also OK. For equivalence, block number does not matter.

Old cache in EVM.hs

This as the old cache in EVM.hs:

data Cache = Cache
  { fetched :: Map Addr Contract
  , path    :: Map (CodeLocation, Int) Bool
  } deriving (Show, Generic)

But that cache was PER VM:

data VM (t :: VMType) s = VM
  { result         :: Maybe (VMResult t s)
  , state          :: FrameState t s
  , frames         :: [Frame t s]
[...]
  , cache          :: Cache

So each thread would inherit their parent's cache:

  • A -> (B,C) fork A into B and C. B and C inherit A's cache. Now:
  • B->(D,F) Another fork. B into D, F. Now, if C fetched some data, D would have no idea, and would re-fetch it. And F would re-fetch it. Actually, also B would re-fetch it. etc. So it's a huge mess, and tons of re-fetches.

Another annoyance was that this "cache" was abused to store the paths visited, which were then used to detect loops:

maxIterationsReached vm (Just maxIter) =
  let codelocation = getCodeLocation vm
      (iters, _) = view (at codelocation % non (0, [])) vm.iterations
  in if unsafeInto maxIter <= iters
     then Map.lookup (codelocation, iters - 1) vm.cache.path

This had nothing to do with a cache. It was simply tracking where we have been. This cache.path has now been renamed pathsVisited.

Small Changes

  • Fetch is also now a module, and not everything is exported out.
  • We now also only open one HTTP channel with the RPC host, and use that throughout, as part of sess :: NetSession.Session inside Session. This reduces latency and pressure on the RPC host.
  • Contracts less than equal to address 0xdeadbeef are treated as if they didn't exist, i.e. the system automatically treats them as if the RPC call finished and returned nonexistent. This should be fine.
  • The warning for fetching zero address has been removed. It doesn't make sense, zero address will get fetched when we try to run e.g. a precompile. So instead, we deal with it as per above, i.e. return as if the RPC happened and returned that nothing is deployed there.

Overall effect

Overall, this PR should make working with RPC a lot faster. It should also make sure we don't overwhelm the RPC host and get data faster.

Fixes #528

Checklist

  • tested locally
  • added automated tests
  • updated the docs
  • updated the changelog

@msooseth msooseth changed the title [DRAFT] Adding session use for RPC [DRAFT] Adding session use for RPC, along with shared cache and fixed Latest Block Sep 10, 2025
@msooseth msooseth force-pushed the use-rpc-session branch 2 times, most recently from 98ed1f7 to fece723 Compare September 11, 2025 10:58
Update cabal too

Updating Latest correctly
@msooseth msooseth changed the title [DRAFT] Adding session use for RPC, along with shared cache and fixed Latest Block Adding session use for RPC, along with shared cache and fixed Latest Block Sep 15, 2025
@msooseth msooseth marked this pull request as ready for review September 15, 2025 10:59
@msooseth msooseth requested a review from blishko September 15, 2025 10:59
Copy link
Collaborator

@blishko blishko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

I am just wondering if we can push the creation of the session a bit down the pipeline.
It seems we need to create the session in too many places right now.

Copy link
Collaborator

@blishko blishko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@msooseth msooseth merged commit f3d8672 into main Sep 16, 2025
3 of 7 checks passed
@msooseth msooseth deleted the use-rpc-session branch September 16, 2025 12:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Confusion about "Latest" BlockNumber when Symbolic executing a contract

3 participants