Skip to content

Commit 8f3251f

Browse files
KillariDevjochem-brouwerryanschneiders1naMicah Zoltu
authored
add eth_simulateV1 (#484)
This adds eth_simulateV1, a method that executes a sequence of EVM calls on top of a block, outputting the result as a kind of block. The method has a lot of options that influence the execution behavior. Co-authored-by: Jochem Brouwer <[email protected]> Co-authored-by: Ryan Schneider <[email protected]> Co-authored-by: Sina Mahmoodi <[email protected]> Co-authored-by: Micah Zoltu <[email protected]>
1 parent 884fb32 commit 8f3251f

File tree

4 files changed

+639
-0
lines changed

4 files changed

+639
-0
lines changed

docs/ethsimulatev1-notes.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# eth_simulate
2+
This document contains some extra information that couldn't be fit to the specification document directly.
3+
4+
## Default block values
5+
Unlike `eth_call`, `eth_simulateV1`'s calls are conducted inside blocks. We don't require user to define all the fields of the blocks so here are the defaults that are assumed for blocks parameters:
6+
7+
| parameter name | default value |
8+
-----------------|-----------------------
9+
| prevRandao | `0x0000000000000000000000000000000000000000000000000000000000000000` |
10+
| feeRecipient | `0x0000000000000000000000000000000000000000` |
11+
| mixHash | `0x0000000000000000000000000000000000000000000000000000000000000000` |
12+
| nonce | `0x0` |
13+
| extraData | `0x0000000000000000000000000000000000000000000000000000000000000000` |
14+
| difficulty | The same as the base block defined as the second parameter in the call |
15+
| gasLimit | The same as the base block defined as the second parameter in the call |
16+
| hash | Calculated normally |
17+
| parentHash | Previous blocks hash |
18+
| timestamp | The timestamp of previous block + `network block time` (12s on Ethereum Mainnet) |
19+
| baseFeePerGas | When validation mode is true, baseFeePerGas is calculated on what it should be according to Ethereum's spec. When validation mode is false, the baseFeePerGas is set to zero |
20+
| sha3Uncles | Empty trie root |
21+
| withdrawals | Empty array |
22+
| uncles | Empty array |
23+
| blobBaseFee | When validation mode is true, blobBaseFee is calculated on what it should be according to EIP-4844 spec. When validation mode is false, the blobBaseFee is set to zero |
24+
| number | Previous block number + 1 |
25+
| logsBloom | Calculated normally. ETH logs are not part of the calculation |
26+
| receiptsRoot | Calculated normally |
27+
| transactionsRoot | Calculated normally |
28+
| size | Calculated normally |
29+
| withdrawalsRoot | Calculated normally |
30+
| gasUsed | Calculated normally |
31+
| stateRoot | Calculated normally |
32+
33+
## Default values for transactions
34+
As eth_simulate is an extension to `eth_call` we want to enable the nice user experience that the user does not need to provide all required values for a transaction. We are assuming following defaults if the variable is not provided by the user:
35+
| parameter name | description |
36+
-----------------|-----------------------
37+
| type | `0x2` |
38+
| nonce | Take the correct nonce for the account prior eth_simulate and increment by one for each transaction by the account |
39+
| to | `null` |
40+
| from | `0x0000000000000000000000000000000000000000` |
41+
| gasLimit | blockGasLimit - soFarUsedGasInBlock |
42+
| value | `0x0` |
43+
| input | no data |
44+
| gasPrice | `0x0` |
45+
| maxPriorityFeePerGas | `0x0` |
46+
| maxFeePerGas | `0x0` |
47+
| accessList | empty array |
48+
| blobVersionedHashes | empty array |
49+
| chainId | The chain id of the current chain |
50+
| r | `0x0` |
51+
| s | `0x0` |
52+
| yParity | even |
53+
| v | `0x0` |
54+
55+
## Overriding default values
56+
The default values of blocks and transactions can be overriden. For Transactions we allow overriding of variables `type`, `nonce`, `to`, `from`, `gas limit`, `value`, `input`, `gasPrice`, `maxPriorityFeePerGas`, `maxFeePerGas`, `accessList`, and for blocks we allow modifications of `number`, `time`, `gasLimit`, `feeRecipient`, `prevRandao`, `baseFeePerGas` and `blobBaseFee`:
57+
```json
58+
"blockOverrides": {
59+
"number": "0x14",
60+
"time": "0xc8",
61+
"gasLimit": "0x2e631",
62+
"feeRecipient": "0xc100000000000000000000000000000000000000",
63+
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000001234",
64+
"baseFeePerGas": "0x14",
65+
"blobBaseFee": "0x15"
66+
},
67+
```
68+
All the other fields are computed automatically (eg, `stateRoot` and `gasUsed`) or kept as their default values (eg. `uncles` or `withdrawals`). When overriding `number` and `time` variables for blocks, we automatically check that the block numbers and time fields are strictly increasing (we don't allow decreasing, or duplicated block numbers or times). If the block number is increased more than `1` compared to the previous block, new empty blocks are generated in between.
69+
70+
An interesting note here is that an user can specify block numbers and times of some blocks, but not for others. When block numbers of times are left unspecified, the default values will be used. After the blocks have been constructed, and default values are calculated, the blocks are checked that their block numbers and times are still valid.
71+
72+
## ETH transfer logs
73+
When `traceTransfers` setting is enabled on `eth_simulateV1`, eth_simulate will return logs for ethereum transfers along with the normal logs sent by contracts. The ETH transfers are identical to ERC20 transfers, except the "sending contract" is address `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`.
74+
75+
For example, here's a query that will simply send ether from one address to another (with a state override that gives us the ETH initially):
76+
```json
77+
{
78+
"jsonrpc": "2.0",
79+
"id": 1,
80+
"method": "eth_simulateV1",
81+
"params": [
82+
{
83+
"blockStateCalls": [
84+
{
85+
"stateOverrides": {
86+
"0xc000000000000000000000000000000000000000": {
87+
"balance": "0x7d0"
88+
}
89+
},
90+
"calls": [
91+
{
92+
"from": "0xc000000000000000000000000000000000000000",
93+
"to": "0xc100000000000000000000000000000000000000",
94+
"value": "0x3e8"
95+
}
96+
]
97+
}
98+
],
99+
"traceTransfers": true
100+
},
101+
"latest"
102+
]
103+
}
104+
```
105+
106+
The output of this query is:
107+
```json
108+
{
109+
"jsonrpc": "2.0",
110+
"id": 1,
111+
"result": [
112+
{
113+
"number": "0x4",
114+
"hash": "0x859c932c5cf0dabf8d12eb2518e063966ac1a25e2fc49f1f02574a37f358d0b5",
115+
"timestamp": "0x1f",
116+
"gasLimit": "0x4c4b40",
117+
"gasUsed": "0x5208",
118+
"feeRecipient": "0x0000000000000000000000000000000000000000",
119+
"baseFeePerGas": "0x2310a91d",
120+
"prevRandao": "0x0000000000000000000000000000000000000000000000000000000000000000",
121+
"calls": [
122+
{
123+
"returnData": "0x",
124+
"logs": [
125+
{
126+
"address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
127+
"topics": [
128+
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
129+
"0x000000000000000000000000c000000000000000000000000000000000000000",
130+
"0x000000000000000000000000c100000000000000000000000000000000000000"
131+
],
132+
"data": "0x00000000000000000000000000000000000000000000000000000000000003e8",
133+
"blockNumber": "0x4",
134+
"transactionHash": "0xa4d41019e71335f8567e17746b708ddda8b975a9a61f909bd3df55f4866cc913",
135+
"transactionIndex": "0x0",
136+
"blockHash": "0x859c932c5cf0dabf8d12eb2518e063966ac1a25e2fc49f1f02574a37f358d0b5",
137+
"logIndex": "0x0",
138+
"removed": false
139+
}
140+
],
141+
"gasUsed": "0x5208",
142+
"status": "0x1"
143+
}
144+
]
145+
}
146+
]
147+
}
148+
```
149+
150+
Here the interesting part is:
151+
```json
152+
"address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
153+
"topics": [
154+
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
155+
"0x000000000000000000000000c000000000000000000000000000000000000000",
156+
"0x000000000000000000000000c100000000000000000000000000000000000000"
157+
],
158+
"data": "0x00000000000000000000000000000000000000000000000000000000000003e8",
159+
```
160+
In the observed event, the sender address is denoted as the `0xee...` address. The first topic (`0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef`) aligns with the event signature `Transfer(address,address,uint256)`, while the second topic (`0x000000000000000000000000c000000000000000000000000000000000000000`) corresponds to the sending address, and the third topic (`0x000000000000000000000000c100000000000000000000000000000000000000`) represents the receiving address. The quantity of ETH transacted is stored in the data field.
161+
162+
The ETH logs will contain following types of ETH transfers:
163+
- Transfering ETH from EOA
164+
- Transfering ETH via contract
165+
- Selfdestructing contract sending ETH
166+
167+
But not following ones:
168+
- Gas fees
169+
- eth_simulates eth balance override
170+
171+
ETH logs are not part of the calculation for logs bloom filter. Also, similar to normal logs, if the transaction sends ETH but the execution reverts, no log gets issued.
172+
173+
## Validation Mode
174+
The `eth_simulate` includes a feature to enable or disable validation using the `Validation` setting. By default, validation is off, and `eth_simulate` behaves similarly to `eth_call`. When validation is enabled, the simulation closely approximates real EVM block creation, except it skips transaction signature checks and allows direct transactions from contracts.
175+
176+
### Zero Base Fee
177+
When validation mode is disabled, the block's `baseFee` is set to zero. This affects the BASEFEE opcode and the `baseFee` returned by `eth_simulate`. Additionally, the base fee does not auto-adjust based on the parent block, even if the user overrides the previous block's `baseFee`.
178+
179+
This behavior is intentional to enable free transactions in `eth_simulate`, allowing users to avoid adding boilerplate code to give themselves ETH using a balance override. While similar results could be achieved by skipping balance validation, it would be inconsistent with EVM operations.
180+
181+
## Failures
182+
It is possible that user defines a transaction that cannot be included in the Ethereum block as it breaks the rules of EVM. For example, if transactions nonce is too high or low, baseFeePerGas is too low etc. In these situations the execution of eth_simulate ends and an error is returned.
183+
184+
## Version number
185+
The method name for eth_simulate `eth_simulateV1` the intention is that after release of eth_simulate, if new features are wanted the `eth_simulateV1` is kept as it is, and instead `eth_simulateV2` is published with the new wanted features.
186+
187+
## Clients can set their own limits
188+
Clients may introduce their own limits to prevent DOS attacks using the method. We have thought of three such standard limits
189+
- How many blocks can be defined in `BlockStateCalls`. The suggested default for this is 256 blocks
190+
- A global gas limit (similar to the same limit for `eth_call`). The eth_simulate cannot exceed the global gas limit over its lifespan
191+
- The clients can set their own limit on how big the input JSON payload can be. A suggested default for this is 30mb
192+
193+
## Rationale
194+
195+
### Pre-computed calls
196+
197+
When it comes to contract override behavior, specifically precompile override, there were two approaches:
198+
199+
1. As specified above to allow replacement of precompiles by EVM code and to allow those same precompiles to be relocated to another access for fallback behavior.
200+
2. Allow users to pass in a set of pre-computed calls for an address, i.e. direct mapping of input to output.
201+
202+
The second approach has better UX for simple use-cases such as faking a signature via ecrecover. It also allows for getting the same gas usage as a real precompile execution. The simpler UX comes at cost of flexibility. Overriding with EVM code is general. It also imposes changes to the EVM interpreter code, which has been otherwise avoided, without enabling new features. Hence the spec proposes the first alternative.

src/eth/execute.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,19 @@
9595
storageKeys:
9696
- '0x0000000000000000000000000000000000000000000000000000000000000081'
9797
gasUsed: '0x125f8'
98+
- name: eth_simulateV1
99+
summary: Executes a sequence of message calls building on each other's state without creating transactions on the block chain, optionally overriding block and state data
100+
params:
101+
- name: Payload
102+
required: true
103+
schema:
104+
$ref: '#/components/schemas/EthSimulatePayload'
105+
- name: Block tag
106+
required: false
107+
description: "default: 'latest'"
108+
schema:
109+
$ref: '#/components/schemas/BlockNumberOrTagOrHash'
110+
result:
111+
name: Result of calls
112+
schema:
113+
$ref: '#/components/schemas/EthSimulateResult'

0 commit comments

Comments
 (0)