github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/cmd/evm/README.md (about) 1 ## EVM state transition tool 2 3 The `evm t8n` tool is a stateless state transition utility. It is a utility 4 which can 5 6 1. Take a prestate, including 7 - Accounts, 8 - Block context information, 9 - Previous blockshashes (*optional) 10 2. Apply a set of transactions, 11 3. Apply a mining-reward (*optional), 12 4. And generate a post-state, including 13 - State root, transaction root, receipt root, 14 - Information about rejected transactions, 15 - Optionally: a full or partial post-state dump 16 17 ## Specification 18 19 The idea is to specify the behaviour of this binary very _strict_, so that other 20 node implementors can build replicas based on their own state-machines, and the 21 state generators can swap between a `geth`-based implementation and a `parityvm`-based 22 implementation. 23 24 ### Command line params 25 26 Command line params that has to be supported are 27 ``` 28 29 --trace Output full trace logs to files <txhash>.jsonl 30 --trace.nomemory Disable full memory dump in traces 31 --trace.nostack Disable stack output in traces 32 --trace.noreturndata Disable return data output in traces 33 --output.basedir value Specifies where output files are placed. Will be created if it does not exist. 34 --output.alloc alloc Determines where to put the alloc of the post-state. 35 `stdout` - into the stdout output 36 `stderr` - into the stderr output 37 --output.result result Determines where to put the result (stateroot, txroot etc) of the post-state. 38 `stdout` - into the stdout output 39 `stderr` - into the stderr output 40 --output.body value If set, the RLP of the transactions (block body) will be written to this file. 41 --input.txs stdin stdin or file name of where to find the transactions to apply. If the file prefix is '.rlp', then the data is interpreted as an RLP list of signed transactions.The '.rlp' format is identical to the output.body format. (default: "txs.json") 42 --state.fork value Name of ruleset to use. 43 --state.chainid value ChainID to use (default: 1) 44 --state.reward value Mining reward. Set to -1 to disable (default: 0) 45 46 ``` 47 48 ### Error codes and output 49 50 All logging should happen against the `stderr`. 51 There are a few (not many) errors that can occur, those are defined below. 52 53 #### EVM-based errors (`2` to `9`) 54 55 - Other EVM error. Exit code `2` 56 - Failed configuration: when a non-supported or invalid fork was specified. Exit code `3`. 57 - Block history is not supplied, but needed for a `BLOCKHASH` operation. If `BLOCKHASH` 58 is invoked targeting a block which history has not been provided for, the program will 59 exit with code `4`. 60 61 #### IO errors (`10`-`20`) 62 63 - Invalid input json: the supplied data could not be marshalled. 64 The program will exit with code `10` 65 - IO problems: failure to load or save files, the program will exit with code `11` 66 67 ## Examples 68 ### Basic usage 69 70 Invoking it with the provided example files 71 ``` 72 ./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json 73 ``` 74 Two resulting files: 75 76 `alloc.json`: 77 ```json 78 { 79 "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": { 80 "balance": "0xfeed1a9d", 81 "nonce": "0x1" 82 }, 83 "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { 84 "balance": "0x5ffd4878be161d74", 85 "nonce": "0xac" 86 }, 87 "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": { 88 "balance": "0xa410" 89 } 90 } 91 ``` 92 `result.json`: 93 ```json 94 { 95 "stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13", 96 "txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d", 97 "receiptRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", 98 "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 99 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 100 "receipts": [ 101 { 102 "root": "0x", 103 "status": "0x1", 104 "cumulativeGasUsed": "0x5208", 105 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 106 "logs": null, 107 "transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673", 108 "contractAddress": "0x0000000000000000000000000000000000000000", 109 "gasUsed": "0x5208", 110 "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 111 "transactionIndex": "0x0" 112 } 113 ], 114 "rejected": [ 115 { 116 "index": 1, 117 "error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" 118 } 119 ] 120 } 121 ``` 122 123 We can make them spit out the data to e.g. `stdout` like this: 124 ``` 125 ./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 126 ``` 127 Output: 128 ```json 129 { 130 "alloc": { 131 "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": { 132 "balance": "0xfeed1a9d", 133 "nonce": "0x1" 134 }, 135 "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { 136 "balance": "0x5ffd4878be161d74", 137 "nonce": "0xac" 138 }, 139 "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": { 140 "balance": "0xa410" 141 } 142 }, 143 "result": { 144 "stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13", 145 "txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d", 146 "receiptRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", 147 "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", 148 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 149 "receipts": [ 150 { 151 "root": "0x", 152 "status": "0x1", 153 "cumulativeGasUsed": "0x5208", 154 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 155 "logs": null, 156 "transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673", 157 "contractAddress": "0x0000000000000000000000000000000000000000", 158 "gasUsed": "0x5208", 159 "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 160 "transactionIndex": "0x0" 161 } 162 ], 163 "rejected": [ 164 { 165 "index": 1, 166 "error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" 167 } 168 ] 169 } 170 } 171 ``` 172 173 ## About Ommers 174 175 Mining rewards and ommer rewards might need to be added. This is how those are applied: 176 177 - `block_reward` is the block mining reward for the miner (`0xaa`), of a block at height `N`. 178 - For each ommer (mined by `0xbb`), with blocknumber `N-delta` 179 - (where `delta` is the difference between the current block and the ommer) 180 - The account `0xbb` (ommer miner) is awarded `(8-delta)/ 8 * block_reward` 181 - The account `0xaa` (block miner) is awarded `block_reward / 32` 182 183 To make `state_t8n` apply these, the following inputs are required: 184 185 - `state.reward` 186 - For ethash, it is `5000000000000000000` `wei`, 187 - If this is not defined, mining rewards are not applied, 188 - A value of `0` is valid, and causes accounts to be 'touched'. 189 - For each ommer, the tool needs to be given an `address` and a `delta`. This 190 is done via the `env`. 191 192 Note: the tool does not verify that e.g. the normal uncle rules apply, 193 and allows e.g two uncles at the same height, or the uncle-distance. This means that 194 the tool allows for negative uncle reward (distance > 8) 195 196 Example: 197 `./testdata/5/env.json`: 198 ```json 199 { 200 "currentCoinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 201 "currentDifficulty": "0x20000", 202 "currentGasLimit": "0x750a163df65e8a", 203 "currentNumber": "1", 204 "currentTimestamp": "1000", 205 "ommers": [ 206 {"delta": 1, "address": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" }, 207 {"delta": 2, "address": "0xcccccccccccccccccccccccccccccccccccccccc" } 208 ] 209 } 210 ``` 211 When applying this, using a reward of `0x80` 212 Output: 213 ```json 214 { 215 "alloc": { 216 "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": { 217 "balance": "0x88" 218 }, 219 "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": { 220 "balance": "0x70" 221 }, 222 "0xcccccccccccccccccccccccccccccccccccccccc": { 223 "balance": "0x60" 224 } 225 } 226 } 227 ``` 228 ### Future EIPS 229 230 It is also possible to experiment with future eips that are not yet defined in a hard fork. 231 Example, putting EIP-1344 into Frontier: 232 ``` 233 ./evm t8n --state.fork=Frontier+1344 --input.pre=./testdata/1/pre.json --input.txs=./testdata/1/txs.json --input.env=/testdata/1/env.json 234 ``` 235 236 ### Block history 237 238 The `BLOCKHASH` opcode requires blockhashes to be provided by the caller, inside the `env`. 239 If a required blockhash is not provided, the exit code should be `4`: 240 Example where blockhashes are provided: 241 ``` 242 ./evm --verbosity=1 t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace 243 INFO [07-27|11:53:40.960] Trie dumping started root=b7341d..857ea1 244 INFO [07-27|11:53:40.960] Trie dumping complete accounts=3 elapsed="103.298µs" 245 INFO [07-27|11:53:40.960] Wrote file file=alloc.json 246 INFO [07-27|11:53:40.960] Wrote file file=result.json 247 248 ``` 249 250 ``` 251 cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2 252 ``` 253 ``` 254 {"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} 255 {"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnData":"0x","depth":1,"refund":0,"opName":"BLOCKHASH","error":""} 256 {"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnData":"0x","depth":1,"refund":0,"opName":"STOP","error":""} 257 {"output":"","gasUsed":"0x17","time":156276} 258 ``` 259 260 In this example, the caller has not provided the required blockhash: 261 ``` 262 ./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace 263 ERROR(4): getHash(3) invoked, blockhash for that block not provided 264 ``` 265 Error code: 4 266 267 ### Chaining 268 269 Another thing that can be done, is to chain invocations: 270 ``` 271 ./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json 272 INFO [07-27|11:53:41.049] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" 273 INFO [07-27|11:53:41.050] Trie dumping started root=84208a..ae4e13 274 INFO [07-27|11:53:41.050] Trie dumping complete accounts=3 elapsed="59.412µs" 275 INFO [07-27|11:53:41.050] Wrote file file=result.json 276 INFO [07-27|11:53:41.051] rejected tx index=0 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" 277 INFO [07-27|11:53:41.051] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1" 278 INFO [07-27|11:53:41.052] Trie dumping started root=84208a..ae4e13 279 INFO [07-27|11:53:41.052] Trie dumping complete accounts=3 elapsed="45.734µs" 280 INFO [07-27|11:53:41.052] Wrote file file=alloc.json 281 INFO [07-27|11:53:41.052] Wrote file file=result.json 282 283 ``` 284 What happened here, is that we first applied two identical transactions, so the second one was rejected. 285 Then, taking the poststate alloc as the input for the next state, we tried again to include 286 the same two transactions: this time, both failed due to too low nonce. 287 288 In order to meaningfully chain invocations, one would need to provide meaningful new `env`, otherwise the 289 actual blocknumber (exposed to the EVM) would not increase. 290 291 ### Transactions in RLP form 292 293 It is possible to provide already-signed transactions as input to, using an `input.txs` which ends with the `rlp` suffix. 294 The input format for RLP-form transactions is _identical_ to the _output_ format for block bodies. Therefore, it's fully possible 295 to use the evm to go from `json` input to `rlp` input. 296 297 The following command takes **json** the transactions in `./testdata/13/txs.json` and signs them. After execution, they are output to `signed_txs.rlp`.: 298 ``` 299 ./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 300 INFO [07-27|11:53:41.124] Trie dumping started root=e4b924..6aef61 301 INFO [07-27|11:53:41.124] Trie dumping complete accounts=3 elapsed="94.284µs" 302 INFO [07-27|11:53:41.125] Wrote file file=alloc.json 303 INFO [07-27|11:53:41.125] Wrote file file=alloc_jsontx.json 304 INFO [07-27|11:53:41.125] Wrote file file=signed_txs.rlp 305 306 ``` 307 308 The `output.body` is the rlp-list of transactions, encoded in hex and placed in a string a'la `json` encoding rules: 309 ``` 310 cat signed_txs.rlp 311 "0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9" 312 ``` 313 314 We can use `rlpdump` to check what the contents are: 315 ``` 316 rlpdump -hex $(cat signed_txs.rlp | jq -r ) 317 [ 318 02f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904, 319 02f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9, 320 ] 321 ``` 322 Now, we can now use those (or any other already signed transactions), as input, like so: 323 ``` 324 ./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 325 INFO [07-27|11:53:41.253] Trie dumping started root=e4b924..6aef61 326 INFO [07-27|11:53:41.253] Trie dumping complete accounts=3 elapsed="128.445µs" 327 INFO [07-27|11:53:41.253] Wrote file file=alloc.json 328 INFO [07-27|11:53:41.255] Wrote file file=alloc_rlptx.json 329 330 ``` 331 332 You might have noticed that the results from these two invocations were stored in two separate files. 333 And we can now finally check that they match. 334 ``` 335 cat alloc_jsontx.json | jq .stateRoot && cat alloc_rlptx.json | jq .stateRoot 336 "0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61" 337 "0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61" 338 ```