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(node): add anvil node #1037

Merged
merged 310 commits into from
May 4, 2022
Merged

feat(node): add anvil node #1037

merged 310 commits into from
May 4, 2022

Conversation

mattsse
Copy link
Member

@mattsse mattsse commented Mar 24, 2022

Motivation

Add support for forge node

Solution

  • general RPC support
  • Eth API bindings
  • axum HTTP server
  • axum WS server
    • ws specific handler
  • axum RPC handlers
  • EthApi interface
    • EthRequest dispatch
    • send(raw)transaction
    • estimate gas
  • Node revm Database Backend
  • revm call transaction
    • recover sender
    • Execute transactions
    • Make and store Block
    • add trie based Database for revm
  • Transaction Pool
  • Transaction validation
  • Mining strategies
  • ethereum types and encoding
  • forge node command
    • full node config and builder
  • forked backend

@onbjerg onbjerg added the T-feature Type: feature label Mar 25, 2022
@mattsse mattsse marked this pull request as draft March 26, 2022 14:35
@onbjerg onbjerg linked an issue Mar 27, 2022 that may be closed by this pull request
@gakonst
Copy link
Member

gakonst commented Mar 27, 2022

should we rebase this to master so that it doesn't have the revm commits?

@mattsse
Copy link
Member Author

mattsse commented Mar 27, 2022

yeah will fixup all revm related commits

@sambacha
Copy link
Contributor

I see a tracing service mentioned in the commits. Fwiw Besu has support for open telemetry if your looking for a reference that is useful.

@mattsse mattsse force-pushed the matt/node branch 5 times, most recently from b84b857 to d369b1e Compare March 29, 2022 22:18
@brockelmore
Copy link
Member

brockelmore commented Mar 29, 2022

Random mind dump from my accumulation of building this in cevm back in the day (see here):

  1. We should not match eth nodes 1:1 in capabilities, we should have additional nonstandard features such as:
    • forge_setBlock: used for setting reported block number
    • forge_setRpcUrl: used for forking - forward requests to addresses that we dont have to mainnet
    • forge_mining: bool to set either time based block number increment
    • forge_setTimestamp: set block timestamp
    • forge_enableTraces: turn on call traces for transactions that are returned to the user when they execute a transaction (instead of just txhash/receipt)
    • eth_sendUnsignedTransaction: execute a transaction regardless of signature status
  2. ideally this is used for dry runs of a solidity scripting system (the backbone that will be behind forge deploy, that will allow for arbitrary scripting capabilities, and testing of the transaction system)

@mattsse
Copy link
Member Author

mattsse commented Mar 29, 2022

Random mind dump from my accumulation of building this in cevm back in the day (see here):

  1. We should not match eth nodes 1:1 in capabilities, we should have additional nonstandard features such as:

    • forge_setBlock: used for setting reported block number
    • forge_setRpcUrl: used for forking - forward requests to addresses that we dont have to mainnet
    • forge_mining: bool to set either time based block number increment
    • forge_setTimestamp: set block timestamp
    • forge_enableTraces: turn on call traces for transactions that are returned to the user when they execute a transaction (instead of just txhash/receipt)
    • eth_sendUnsignedTransaction: execute a transaction regardless of signature status
  2. ideally this is used for dry runs of a solidity scripting system (the backbone that will be behind forge deploy, that will allow for arbitrary scripting capabilities, and testing of the transaction system)

thanks for this.
will add them to the EthApi type to track this and keep this in mind moving forward with the impl.

@brockelmore brockelmore mentioned this pull request Mar 30, 2022
7 tasks
@gakonst
Copy link
Member

gakonst commented Mar 30, 2022

Not explicitly required, but we should probably strive for compatibility with trace_replayTransaction etc for logging our traces. A separate thing would be to allow our traces to print state diffs, which is probably something we'd PR into the Tracing inspector

@tynes
Copy link
Contributor

tynes commented Mar 31, 2022

Random mind dump from my accumulation of building this in cevm back in the day (see here):

  1. We should not match eth nodes 1:1 in capabilities, we should have additional nonstandard features such as:

    • forge_setBlock: used for setting reported block number
    • forge_setRpcUrl: used for forking - forward requests to addresses that we dont have to mainnet
    • forge_mining: bool to set either time based block number increment
    • forge_setTimestamp: set block timestamp
    • forge_enableTraces: turn on call traces for transactions that are returned to the user when they execute a transaction (instead of just txhash/receipt)
    • eth_sendUnsignedTransaction: execute a transaction regardless of signature status
  2. ideally this is used for dry runs of a solidity scripting system (the backbone that will be behind forge deploy, that will allow for arbitrary scripting capabilities, and testing of the transaction system)

Perhaps its a good idea to work with the hardhat team to standardize some RPCs for these kinds of actions so that tests can be written in a portable way. Right now hardhat has a hardhat RPC namespace that includes similar methods

@Rjected
Copy link
Contributor

Rjected commented Apr 3, 2022

re: some comments on one of my ethers changes I've been diving into this PR, I wanted to see how much we could share with ethers, and comment on what's missing w.r.t transaction types.

Based on some messing around with types (link to a rough PoC on top of this PR), I think the node presents a need for a signed request type, as opposed to the existing Transaction and TransactionRequest types in ethers.

Quick primer on the differences between these types, and what we need

The ethers Transaction type:

  • Represents a response from a provider for APIs such as eth_getTransactionByHash.
  • Contains a signature via r, s, v fields, although not a Signature.
  • Has fields like transaction_index, block_hash, block_number in case the tx is in a block.
  • Is not an enum type like the ethers TypedTransaction, instead using a transaction_type field.
  • Must contain a hash, also includes a method to compute it.

The ethers TypedTransaction type:

  • Represents a transaction request and is used in some part as input to the following APIs:
    • eth_sendTransaction
    • eth_call
    • eth_estimateGas
    • eth_createAccessList
    • eth_createAccessList
    • trace_call
    • trace_callMany
    • If we want, the discussed eth_sendUnsignedTransaction
  • Is used as input to signers
  • Implements transaction types by being an enum
  • Does not include a signature

In the node, we need to work with signed transaction requests in a ton of places, and TypedTransaction wouldn't work because it's unsigned. While the Transaction type would probably work, I would prefer something more like this (use in the node can be seen here):

use ethers_core::types::{
    transaction::eip2718::TypedTransaction,
    Signature,
};


/// Represents a signed transaction request
pub struct SignedTransaction {
    pub tx: EthersTypedTransaction,
    pub signature: Signature,
}

We could introduce this type into ethers and move the decoding changes in ethers-rs#1096 onto that type.
The methods required for the node to work are implemented in the PoC, with notes and questions for implementation.

The PoC removes most of the transaction types introduced in the node.
If we decide to introduce a type like SignedTransaction we could remove the rest of the transaction types.

What do you all think about adding something like this in ethers so we can simplify things in the node?
I can help implement and integrate into the node ofc!

Copy link
Member

@gakonst gakonst left a comment

Choose a reason for hiding this comment

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

approved - modulo my comments on integration tests against uniswap v3-core not passing + how should we be thinking about the waffle patch required in v3-periphery

@mattsse
Copy link
Member Author

mattsse commented May 4, 2022

approved - modulo my comments on integration tests against uniswap v3-core not passing + how should we be thinking about the waffle patch required in v3-periphery

The only tests failing for uni v3 are
a) tests that expect a tx to consume a certain amount of gas
b) tests that expect a specific revert message

currently not sure why a) happens, could just be differences in how revm/hh measure.
since these all test against snapshot files, they could also be automatically migrated.

b) happens because waffle has hardhat decoding baked in https://github.com/TrueFiEng/Waffle/blob/master/waffle-chai/src/matchers/revertedWith.ts#L80
I think we could just get this fixed in there as well

rn the hardhat-waffle plugin is only compatible with hardhat due to this
https://github.com/NomicFoundation/hardhat/blob/fd1f7f8cdb06d8e4ee7fb5fce15c4c48730ca45c/packages/hardhat-waffle/src/waffle-provider-adapter.ts#L16-L23

I made a drop-in replacement plugin here:

https://github.com/mattsse/hardhat-anvil this currently lacks proper anvil config

that could be massively simplified if the network name would not be enforced in hardhat-waffle

@gakonst
Copy link
Member

gakonst commented May 4, 2022

Let's get it 🔥🔥🔥🔥 BIG milestone

@@ -249,7 +256,10 @@ where
trace!(target: "backendhandler", "last sender dropped, ready to drop (&flush cache)");
return Poll::Ready(())
}
_ => break,
Poll::Pending => {
cx.waker().wake_by_ref();
Copy link
Contributor

Choose a reason for hiding this comment

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

i'm confused, what does it do?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure why, but in node fork mode, the task did not wake up after reaching this state, even if new messages were sent to the channel...

this explicitly tells the scheduler to poll it again

Copy link
Contributor

Choose a reason for hiding this comment

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

is there an easy way to reproduce it? there is definitely something fishy with spawning and hyper, but I processed ~8 months worth of blocks without this change.

just tested it and this single line cuts the throughput in half.

Copy link
Contributor

Choose a reason for hiding this comment

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

because of that (probably) hyper problem i'm using foundry-evm with Ws provider & most of the processing lives in futures::executor::ThreadPool::spawn_with_handle, so maybe perf degradation is not visible in foundry.

Copy link
Member Author

@mattsse mattsse May 5, 2022

Choose a reason for hiding this comment

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

yeh this a rather poor solution and turns this basically into a busy loop.
performance for foundry itself is not really an issue here, so because I wasn't able to track this down made this change.

is there an easy way to reproduce it?

I believe it was this test or any other fork test
https://github.com/foundry-rs/foundry/blob/master/anvil/tests/it/fork.rs#L34

just tested again and it hangs when the SharedBackend sends the request if I remove the wake, not sure why though

also tested with increased buffer size and UnboundedSender/Receiver, same result

Copy link
Member

Choose a reason for hiding this comment

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

@univerz realize I'm hijacking the thread but if you wanna join the foundry telegram group to have more synchronous conversations, it'd be great! https://t.me/foundry_rs

@Perelyn-sama
Copy link
Contributor

maybe forge create could deploy contracts to anvil by default?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-feature Type: feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

jsonrpc node over the evm instance used