github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/cmd/evm/README.md (about) 1 # EVM tool 2 3 The EVM tool provides a few useful subcommands to facilitate testing at the EVM 4 layer. 5 6 * transition tool (`t8n`) : a stateless state transition utility 7 * transaction tool (`t9n`) : a transaction validation utility 8 * block builder tool (`b11r`): a block assembler utility 9 10 ## State transition tool (`t8n`) 11 12 13 The `evm t8n` tool is a stateless state transition utility. It is a utility 14 which can 15 16 1. Take a prestate, including 17 - Accounts, 18 - Block context information, 19 - Previous blockshashes (*optional) 20 2. Apply a set of transactions, 21 3. Apply a mining-reward (*optional), 22 4. And generate a post-state, including 23 - State root, transaction root, receipt root, 24 - Information about rejected transactions, 25 - Optionally: a full or partial post-state dump 26 27 ### Specification 28 29 The idea is to specify the behaviour of this binary very _strict_, so that other 30 node implementors can build replicas based on their own state-machines, and the 31 state generators can swap between a \`geth\`-based implementation and a \`parityvm\`-based 32 implementation. 33 34 #### Command line params 35 36 Command line params that need to be supported are 37 38 ``` 39 --input.alloc value (default: "alloc.json") 40 --input.env value (default: "env.json") 41 --input.txs value (default: "txs.json") 42 --output.alloc value (default: "alloc.json") 43 --output.basedir value 44 --output.body value 45 --output.result value (default: "result.json") 46 --state.chainid value (default: 1) 47 --state.fork value (default: "GrayGlacier") 48 --state.reward value (default: 0) 49 --trace.memory (default: false) 50 --trace.nomemory (default: true) 51 --trace.noreturndata (default: true) 52 --trace.nostack (default: false) 53 --trace.returndata (default: false) 54 ``` 55 #### Objects 56 57 The transition tool uses JSON objects to read and write data related to the transition operation. The 58 following object definitions are required. 59 60 ##### `alloc` 61 62 The `alloc` object defines the prestate that transition will begin with. 63 64 ```go 65 // Map of address to account definition. 66 type Alloc map[common.Address]Account 67 // Genesis account. Each field is optional. 68 type Account struct { 69 Code []byte `json:"code"` 70 Storage map[common.Hash]common.Hash `json:"storage"` 71 Balance *big.Int `json:"balance"` 72 Nonce uint64 `json:"nonce"` 73 SecretKey []byte `json:"secretKey"` 74 } 75 ``` 76 77 ##### `env` 78 79 The `env` object defines the environmental context in which the transition will 80 take place. 81 82 ```go 83 type Env struct { 84 // required 85 CurrentCoinbase common.Address `json:"currentCoinbase"` 86 CurrentGasLimit uint64 `json:"currentGasLimit"` 87 CurrentNumber uint64 `json:"currentNumber"` 88 CurrentTimestamp uint64 `json:"currentTimestamp"` 89 Withdrawals []*Withdrawal `json:"withdrawals"` 90 // optional 91 CurrentDifficulty *big.Int `json:"currentDifficuly"` 92 CurrentRandom *big.Int `json:"currentRandom"` 93 CurrentBaseFee *big.Int `json:"currentBaseFee"` 94 ParentDifficulty *big.Int `json:"parentDifficulty"` 95 ParentGasUsed uint64 `json:"parentGasUsed"` 96 ParentGasLimit uint64 `json:"parentGasLimit"` 97 ParentTimestamp uint64 `json:"parentTimestamp"` 98 BlockHashes map[uint64]common.Hash `json:"blockHashes"` 99 ParentUncleHash common.Hash `json:"parentUncleHash"` 100 Ommers []Ommer `json:"ommers"` 101 } 102 type Ommer struct { 103 Delta uint64 `json:"delta"` 104 Address common.Address `json:"address"` 105 } 106 type Withdrawal struct { 107 Index uint64 `json:"index"` 108 ValidatorIndex uint64 `json:"validatorIndex"` 109 Recipient common.Address `json:"recipient"` 110 Amount *big.Int `json:"amount"` 111 } 112 ``` 113 114 ##### `txs` 115 116 The `txs` object is an array of any of the transaction types: `LegacyTx`, 117 `AccessListTx`, or `DynamicFeeTx`. 118 119 ```go 120 type LegacyTx struct { 121 Nonce uint64 `json:"nonce"` 122 GasPrice *big.Int `json:"gasPrice"` 123 Gas uint64 `json:"gas"` 124 To *common.Address `json:"to"` 125 Value *big.Int `json:"value"` 126 Data []byte `json:"data"` 127 V *big.Int `json:"v"` 128 R *big.Int `json:"r"` 129 S *big.Int `json:"s"` 130 SecretKey *common.Hash `json:"secretKey"` 131 } 132 type AccessList []AccessTuple 133 type AccessTuple struct { 134 Address common.Address `json:"address" gencodec:"required"` 135 StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"` 136 } 137 type AccessListTx struct { 138 ChainID *big.Int `json:"chainId"` 139 Nonce uint64 `json:"nonce"` 140 GasPrice *big.Int `json:"gasPrice"` 141 Gas uint64 `json:"gas"` 142 To *common.Address `json:"to"` 143 Value *big.Int `json:"value"` 144 Data []byte `json:"data"` 145 AccessList AccessList `json:"accessList"` 146 V *big.Int `json:"v"` 147 R *big.Int `json:"r"` 148 S *big.Int `json:"s"` 149 SecretKey *common.Hash `json:"secretKey"` 150 } 151 type DynamicFeeTx struct { 152 ChainID *big.Int `json:"chainId"` 153 Nonce uint64 `json:"nonce"` 154 GasTipCap *big.Int `json:"maxPriorityFeePerGas"` 155 GasFeeCap *big.Int `json:"maxFeePerGas"` 156 Gas uint64 `json:"gas"` 157 To *common.Address `json:"to"` 158 Value *big.Int `json:"value"` 159 Data []byte `json:"data"` 160 AccessList AccessList `json:"accessList"` 161 V *big.Int `json:"v"` 162 R *big.Int `json:"r"` 163 S *big.Int `json:"s"` 164 SecretKey *common.Hash `json:"secretKey"` 165 } 166 ``` 167 168 ##### `result` 169 170 The `result` object is output after a transition is executed. It includes 171 information about the post-transition environment. 172 173 ```go 174 type ExecutionResult struct { 175 StateRoot common.Hash `json:"stateRoot"` 176 TxRoot common.Hash `json:"txRoot"` 177 ReceiptRoot common.Hash `json:"receiptsRoot"` 178 LogsHash common.Hash `json:"logsHash"` 179 Bloom types.Bloom `json:"logsBloom"` 180 Receipts types.Receipts `json:"receipts"` 181 Rejected []*rejectedTx `json:"rejected,omitempty"` 182 Difficulty *big.Int `json:"currentDifficulty"` 183 GasUsed uint64 `json:"gasUsed"` 184 BaseFee *big.Int `json:"currentBaseFee,omitempty"` 185 } 186 ``` 187 188 #### Error codes and output 189 190 All logging should happen against the `stderr`. 191 There are a few (not many) errors that can occur, those are defined below. 192 193 ##### EVM-based errors (`2` to `9`) 194 195 - Other EVM error. Exit code `2` 196 - Failed configuration: when a non-supported or invalid fork was specified. Exit code `3`. 197 - Block history is not supplied, but needed for a `BLOCKHASH` operation. If `BLOCKHASH` 198 is invoked targeting a block which history has not been provided for, the program will 199 exit with code `4`. 200 201 ##### IO errors (`10`-`20`) 202 203 - Invalid input json: the supplied data could not be marshalled. 204 The program will exit with code `10` 205 - IO problems: failure to load or save files, the program will exit with code `11` 206 207 ``` 208 # This should exit with 3 209 ./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --state.fork=Frontier+1346 2>/dev/null 210 exitcode:3 OK 211 ``` 212 #### Forks 213 ### Basic usage 214 215 The chain configuration to be used for a transition is specified via the 216 `--state.fork` CLI flag. A list of possible values and configurations can be 217 found in [`tests/init.go`](tests/init.go). 218 219 #### Examples 220 ##### Basic usage 221 222 Invoking it with the provided example files 223 ``` 224 ./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --state.fork=Berlin 225 ``` 226 Two resulting files: 227 228 `alloc.json`: 229 ```json 230 { 231 "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": { 232 "balance": "0xfeed1a9d", 233 "nonce": "0x1" 234 }, 235 "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { 236 "balance": "0x5ffd4878be161d74", 237 "nonce": "0xac" 238 }, 239 "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": { 240 "balance": "0xa410" 241 } 242 } 243 ``` 244 `result.json`: 245 ```json 246 { 247 "stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13", 248 "txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d", 249 "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", 250 "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 251 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 252 "receipts": [ 253 { 254 "root": "0x", 255 "status": "0x1", 256 "cumulativeGasUsed": "0x5208", 257 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 258 "logs": null, 259 "transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673", 260 "contractAddress": "0x0000000000000000000000000000000000000000", 261 "gasUsed": "0x5208", 262 "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 263 "transactionIndex": "0x0" 264 } 265 ], 266 "rejected": [ 267 { 268 "index": 1, 269 "error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" 270 } 271 ], 272 "currentDifficulty": "0x20000", 273 "gasUsed": "0x5208" 274 } 275 ``` 276 277 We can make them spit out the data to e.g. `stdout` like this: 278 ``` 279 ./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.result=stdout --output.alloc=stdout --state.fork=Berlin 280 ``` 281 Output: 282 ```json 283 { 284 "alloc": { 285 "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": { 286 "balance": "0xfeed1a9d", 287 "nonce": "0x1" 288 }, 289 "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { 290 "balance": "0x5ffd4878be161d74", 291 "nonce": "0xac" 292 }, 293 "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": { 294 "balance": "0xa410" 295 } 296 }, 297 "result": { 298 "stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13", 299 "txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d", 300 "receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", 301 "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 302 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 303 "receipts": [ 304 { 305 "root": "0x", 306 "status": "0x1", 307 "cumulativeGasUsed": "0x5208", 308 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 309 "logs": null, 310 "transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673", 311 "contractAddress": "0x0000000000000000000000000000000000000000", 312 "gasUsed": "0x5208", 313 "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 314 "transactionIndex": "0x0" 315 } 316 ], 317 "rejected": [ 318 { 319 "index": 1, 320 "error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" 321 } 322 ], 323 "currentDifficulty": "0x20000", 324 "gasUsed": "0x5208" 325 } 326 } 327 ``` 328 329 #### About Ommers 330 331 Mining rewards and ommer rewards might need to be added. This is how those are applied: 332 333 - `block_reward` is the block mining reward for the miner (`0xaa`), of a block at height `N`. 334 - For each ommer (mined by `0xbb`), with blocknumber `N-delta` 335 - (where `delta` is the difference between the current block and the ommer) 336 - The account `0xbb` (ommer miner) is awarded `(8-delta)/ 8 * block_reward` 337 - The account `0xaa` (block miner) is awarded `block_reward / 32` 338 339 To make `t8n` apply these, the following inputs are required: 340 341 - `--state.reward` 342 - For ethash, it is `5000000000000000000` `wei`, 343 - If this is not defined, mining rewards are not applied, 344 - A value of `0` is valid, and causes accounts to be 'touched'. 345 - For each ommer, the tool needs to be given an `addres\` and a `delta`. This 346 is done via the `ommers` field in `env`. 347 348 Note: the tool does not verify that e.g. the normal uncle rules apply, 349 and allows e.g two uncles at the same height, or the uncle-distance. This means that 350 the tool allows for negative uncle reward (distance > 8) 351 352 Example: 353 `./testdata/5/env.json`: 354 ```json 355 { 356 "currentCoinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 357 "currentDifficulty": "0x20000", 358 "currentGasLimit": "0x750a163df65e8a", 359 "currentNumber": "1", 360 "currentTimestamp": "1000", 361 "ommers": [ 362 {"delta": 1, "address": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" }, 363 {"delta": 2, "address": "0xcccccccccccccccccccccccccccccccccccccccc" } 364 ] 365 } 366 ``` 367 When applying this, using a reward of `0x08` 368 Output: 369 ```json 370 { 371 "alloc": { 372 "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": { 373 "balance": "0x88" 374 }, 375 "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": { 376 "balance": "0x70" 377 }, 378 "0xcccccccccccccccccccccccccccccccccccccccc": { 379 "balance": "0x60" 380 } 381 } 382 } 383 ``` 384 #### Future EIPS 385 386 It is also possible to experiment with future eips that are not yet defined in a hard fork. 387 Example, putting EIP-1344 into Frontier: 388 ``` 389 ./evm t8n --state.fork=Frontier+1344 --input.pre=./testdata/1/pre.json --input.txs=./testdata/1/txs.json --input.env=/testdata/1/env.json 390 ``` 391 392 #### Block history 393 394 The `BLOCKHASH` opcode requires blockhashes to be provided by the caller, inside the `env`. 395 If a required blockhash is not provided, the exit code should be `4`: 396 Example where blockhashes are provided: 397 ``` 398 ./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace --state.fork=Berlin 399 400 ``` 401 402 ``` 403 cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2 404 ``` 405 ``` 406 {"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} 407 {"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"BLOCKHASH"} 408 {"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"depth":1,"refund":0,"opName":"STOP"} 409 {"output":"","gasUsed":"0x17"} 410 ``` 411 412 In this example, the caller has not provided the required blockhash: 413 ``` 414 ./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace --state.fork=Berlin 415 ERROR(4): getHash(3) invoked, blockhash for that block not provided 416 ``` 417 Error code: 4 418 419 #### Chaining 420 421 Another thing that can be done, is to chain invocations: 422 ``` 423 ./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --state.fork=Berlin --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json --state.fork=Berlin 424 425 ``` 426 What happened here, is that we first applied two identical transactions, so the second one was rejected. 427 Then, taking the poststate alloc as the input for the next state, we tried again to include 428 the same two transactions: this time, both failed due to too low nonce. 429 430 In order to meaningfully chain invocations, one would need to provide meaningful new `env`, otherwise the 431 actual blocknumber (exposed to the EVM) would not increase. 432 433 #### Transactions in RLP form 434 435 It is possible to provide already-signed transactions as input to, using an `input.txs` which ends with the `rlp` suffix. 436 The input format for RLP-form transactions is _identical_ to the _output_ format for block bodies. Therefore, it's fully possible 437 to use the evm to go from `json` input to `rlp` input. 438 439 The following command takes **json** the transactions in `./testdata/13/txs.json` and signs them. After execution, they are output to `signed_txs.rlp`.: 440 ``` 441 ./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./testdata/13/txs.json --input.env=./testdata/13/env.json --output.result=alloc_jsontx.json --output.body=signed_txs.rlp 442 INFO [12-07|04:30:12.380] Trie dumping started root=e4b924..6aef61 443 INFO [12-07|04:30:12.380] Trie dumping complete accounts=3 elapsed="85.765µs" 444 INFO [12-07|04:30:12.380] Wrote file file=alloc.json 445 INFO [12-07|04:30:12.380] Wrote file file=alloc_jsontx.json 446 INFO [12-07|04:30:12.380] Wrote file file=signed_txs.rlp 447 ``` 448 449 The `output.body` is the rlp-list of transactions, encoded in hex and placed in a string a'la `json` encoding rules: 450 ``` 451 cat signed_txs.rlp 452 "0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9" 453 ``` 454 455 We can use `rlpdump` to check what the contents are: 456 ``` 457 rlpdump -hex $(cat signed_txs.rlp | jq -r ) 458 [ 459 02f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904, 460 02f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9, 461 ] 462 ``` 463 Now, we can now use those (or any other already signed transactions), as input, like so: 464 ``` 465 ./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./signed_txs.rlp --input.env=./testdata/13/env.json --output.result=alloc_rlptx.json 466 INFO [12-07|04:30:12.425] Trie dumping started root=e4b924..6aef61 467 INFO [12-07|04:30:12.425] Trie dumping complete accounts=3 elapsed="70.684µs" 468 INFO [12-07|04:30:12.425] Wrote file file=alloc.json 469 INFO [12-07|04:30:12.425] Wrote file file=alloc_rlptx.json 470 ``` 471 You might have noticed that the results from these two invocations were stored in two separate files. 472 And we can now finally check that they match. 473 ``` 474 cat alloc_jsontx.json | jq .stateRoot && cat alloc_rlptx.json | jq .stateRoot 475 "0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61" 476 "0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61" 477 ```