Skip to content

Commit

Permalink
cmd/evm: update script + readme
Browse files Browse the repository at this point in the history
  • Loading branch information
holiman committed Dec 7, 2022
1 parent d6300b8 commit 6d43e16
Show file tree
Hide file tree
Showing 2 changed files with 386 additions and 86 deletions.
227 changes: 184 additions & 43 deletions cmd/evm/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
## EVM state transition tool
# EVM tool

The EVM tool provides a few useful subcommands to facilitate testing at the EVM
layer.

* transition tool (`t8n`) : a stateless state transition utility
* transaction tool (`t9n`) : a transaction validation utility
* block builder tool (`b11r`): a block assembler utility

## State transition tool (`t8n`)


The `evm t8n` tool is a stateless state transition utility. It is a utility
which can
Expand All @@ -14,70 +24,201 @@ which can
- Information about rejected transactions,
- Optionally: a full or partial post-state dump

## Specification
### Specification

The idea is to specify the behaviour of this binary very _strict_, so that other
node implementors can build replicas based on their own state-machines, and the
state generators can swap between a `geth`-based implementation and a `parityvm`-based
state generators can swap between a \`geth\`-based implementation and a \`parityvm\`-based
implementation.

### Command line params
#### Command line params

Command line params that has to be supported are
```
Command line params that need to be supported are

transactions.The '.rlp' format is identical to the output.body format.
```
--input.alloc value (default: "alloc.json")
--input.env value (default: "env.json")
--input.txs value (default: "txs.json")
--output.alloc value (default: "alloc.json")
Determines where to put the `alloc` of the post-state.
stdout output
`stderr` - into the stderr output
--output.basedir value
Specifies where output files are placed. Will be created if it does not exist.
--output.body value
--output.result value (default: "result.json")
post-state.
`stdout` - into the stdout output
output
--state.chainid value (default: 1)
--state.fork value (default: "GrayGlacier")
--state.reward value (default: 0)
--trace (default: false)
Output full trace logs to files <txhash>.jsonl
--trace.memory (default: false)
Enable full memory dump in traces
--trace.nomemory (default: true)
Disable full memory dump in traces (deprecated)
--trace.noreturndata (default: true)
Disable return data output in traces (deprecated)
--trace.nostack (default: false)
Disable stack output in traces
--trace.returndata (default: false)
Enable return data output in traces
```
#### Objects

The transition tool uses JSON objects to read and write data related to the transition operation. The
following object definitions are required.

##### `alloc`

The `alloc` object defines the prestate that transition will begin with.

```go
// Map of address to account definition.
type Alloc map[common.Address]Account
// Genesis account. Each field is optional.
type Account struct {
Code []byte `json:"code"`
Storage map[common.Hash]common.Hash `json:"storage"`
Balance *big.Int `json:"balance"`
Nonce uint64 `json:"nonce"`
SecretKey []byte `json:"secretKey"`
}
```

##### `env`

The `env` object defines the environmental context in which the transition will
take place.

```go
type Env struct {
// required
CurrentCoinbase common.Address `json:"currentCoinbase"`
CurrentGasLimit uint64 `json:"currentGasLimit"`
CurrentNumber uint64 `json:"currentNumber"`
CurrentTimestamp uint64 `json:"currentTimestamp"`
Withdrawals []*Withdrawal `json:"withdrawals"`
// optional
CurrentDifficulty *big.Int `json:"currentDifficuly"`
CurrentRandom *big.Int `json:"currentRandom"`
CurrentBaseFee *big.Int `json:"currentBaseFee"`
ParentDifficulty *big.Int `json:"parentDifficulty"`
ParentGasUsed uint64 `json:"parentGasUsed"`
ParentGasLimit uint64 `json:"parentGasLimit"`
ParentTimestamp uint64 `json:"parentTimestamp"`
BlockHashes map[uint64]common.Hash `json:"blockHashes"`
ParentUncleHash common.Hash `json:"parentUncleHash"`
Ommers []Ommer `json:"ommers"`
}
type Ommer struct {
Delta uint64 `json:"delta"`
Address common.Address `json:"address"`
}
type Withdrawal struct {
Index uint64 `json:"index"`
ValidatorIndex uint64 `json:"validatorIndex"`
Recipient common.Address `json:"recipient"`
Amount *big.Int `json:"amount"`
}
```

### Error codes and output
##### `txs`

The `txs` object is an array of any of the transaction types: `LegacyTx`,
`AccessListTx`, or `DynamicFeeTx`.

```go
type LegacyTx struct {
Nonce uint64 `json:"nonce"`
GasPrice *big.Int `json:"gasPrice"`
Gas uint64 `json:"gas"`
To *common.Address `json:"to"`
Value *big.Int `json:"value"`
Data []byte `json:"data"`
V *big.Int `json:"v"`
R *big.Int `json:"r"`
S *big.Int `json:"s"`
SecretKey *common.Hash `json:"secretKey"`
}
type AccessList []AccessTuple
type AccessTuple struct {
Address common.Address `json:"address" gencodec:"required"`
StorageKeys []common.Hash `json:"storageKeys" gencodec:"required"`
}
type AccessListTx struct {
ChainID *big.Int `json:"chainId"`
Nonce uint64 `json:"nonce"`
GasPrice *big.Int `json:"gasPrice"`
Gas uint64 `json:"gas"`
To *common.Address `json:"to"`
Value *big.Int `json:"value"`
Data []byte `json:"data"`
AccessList AccessList `json:"accessList"`
V *big.Int `json:"v"`
R *big.Int `json:"r"`
S *big.Int `json:"s"`
SecretKey *common.Hash `json:"secretKey"`
}
type DynamicFeeTx struct {
ChainID *big.Int `json:"chainId"`
Nonce uint64 `json:"nonce"`
GasTipCap *big.Int `json:"maxPriorityFeePerGas"`
GasFeeCap *big.Int `json:"maxFeePerGas"`
Gas uint64 `json:"gas"`
To *common.Address `json:"to"`
Value *big.Int `json:"value"`
Data []byte `json:"data"`
AccessList AccessList `json:"accessList"`
V *big.Int `json:"v"`
R *big.Int `json:"r"`
S *big.Int `json:"s"`
SecretKey *common.Hash `json:"secretKey"`
}
```

##### `result`

The `result` object is output after a transition is executed. It includes
information about the post-transition environment.

```go
type ExecutionResult struct {
StateRoot common.Hash `json:"stateRoot"`
TxRoot common.Hash `json:"txRoot"`
ReceiptRoot common.Hash `json:"receiptsRoot"`
LogsHash common.Hash `json:"logsHash"`
Bloom types.Bloom `json:"logsBloom"`
Receipts types.Receipts `json:"receipts"`
Rejected []*rejectedTx `json:"rejected,omitempty"`
Difficulty *big.Int `json:"currentDifficulty"`
GasUsed uint64 `json:"gasUsed"`
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
}
```

#### Error codes and output

All logging should happen against the `stderr`.
There are a few (not many) errors that can occur, those are defined below.

#### EVM-based errors (`2` to `9`)
##### EVM-based errors (`2` to `9`)

- Other EVM error. Exit code `2`
- Failed configuration: when a non-supported or invalid fork was specified. Exit code `3`.
- Block history is not supplied, but needed for a `BLOCKHASH` operation. If `BLOCKHASH`
is invoked targeting a block which history has not been provided for, the program will
exit with code `4`.

#### IO errors (`10`-`20`)
##### IO errors (`10`-`20`)

- Invalid input json: the supplied data could not be marshalled.
The program will exit with code `10`
- IO problems: failure to load or save files, the program will exit with code `11`

## Examples
```
# This should exit with 3
./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
exitcode:3 OK
```
#### Forks
### Basic usage

The chain configuration to be used for a transition is specified via the
`--state.fork` CLI flag. A list of possible values and configurations can be
found in [`tests/init.go`](tests/init.go).

#### Examples
##### Basic usage

Invoking it with the provided example files
```
./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --state.fork=Berlin
Expand Down Expand Up @@ -185,7 +326,7 @@ Output:
}
```

## About Ommers
#### About Ommers

Mining rewards and ommer rewards might need to be added. This is how those are applied:

Expand All @@ -195,14 +336,14 @@ Mining rewards and ommer rewards might need to be added. This is how those are a
- The account `0xbb` (ommer miner) is awarded `(8-delta)/ 8 * block_reward`
- The account `0xaa` (block miner) is awarded `block_reward / 32`

To make `state_t8n` apply these, the following inputs are required:
To make `t8n` apply these, the following inputs are required:

- `state.reward`
- `--state.reward`
- For ethash, it is `5000000000000000000` `wei`,
- If this is not defined, mining rewards are not applied,
- A value of `0` is valid, and causes accounts to be 'touched'.
- For each ommer, the tool needs to be given an `address` and a `delta`. This
is done via the `env`.
- For each ommer, the tool needs to be given an `addres\` and a `delta`. This
is done via the `ommers` field in `env`.

Note: the tool does not verify that e.g. the normal uncle rules apply,
and allows e.g two uncles at the same height, or the uncle-distance. This means that
Expand Down Expand Up @@ -240,15 +381,15 @@ Output:
}
}
```
### Future EIPS
#### Future EIPS

It is also possible to experiment with future eips that are not yet defined in a hard fork.
Example, putting EIP-1344 into Frontier:
```
./evm t8n --state.fork=Frontier+1344 --input.pre=./testdata/1/pre.json --input.txs=./testdata/1/txs.json --input.env=/testdata/1/env.json
```

### Block history
#### Block history

The `BLOCKHASH` opcode requires blockhashes to be provided by the caller, inside the `env`.
If a required blockhash is not provided, the exit code should be `4`:
Expand All @@ -275,7 +416,7 @@ ERROR(4): getHash(3) invoked, blockhash for that block not provided
```
Error code: 4

### Chaining
#### Chaining

Another thing that can be done, is to chain invocations:
```
Expand All @@ -289,7 +430,7 @@ the same two transactions: this time, both failed due to too low nonce.
In order to meaningfully chain invocations, one would need to provide meaningful new `env`, otherwise the
actual blocknumber (exposed to the EVM) would not increase.

### Transactions in RLP form
#### Transactions in RLP form

It is possible to provide already-signed transactions as input to, using an `input.txs` which ends with the `rlp` suffix.
The input format for RLP-form transactions is _identical_ to the _output_ format for block bodies. Therefore, it's fully possible
Expand All @@ -298,11 +439,11 @@ to use the evm to go from `json` input to `rlp` input.
The following command takes **json** the transactions in `./testdata/13/txs.json` and signs them. After execution, they are output to `signed_txs.rlp`.:
```
./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
INFO [12-06|13:06:54.845] Trie dumping started root=e4b924..6aef61
INFO [12-06|13:06:54.845] Trie dumping complete accounts=3 elapsed="84.658µs"
INFO [12-06|13:06:54.846] Wrote file file=alloc.json
INFO [12-06|13:06:54.846] Wrote file file=alloc_jsontx.json
INFO [12-06|13:06:54.846] Wrote file file=signed_txs.rlp
INFO [12-07|04:30:12.380] Trie dumping started root=e4b924..6aef61
INFO [12-07|04:30:12.380] Trie dumping complete accounts=3 elapsed="85.765µs"
INFO [12-07|04:30:12.380] Wrote file file=alloc.json
INFO [12-07|04:30:12.380] Wrote file file=alloc_jsontx.json
INFO [12-07|04:30:12.380] Wrote file file=signed_txs.rlp
```

The `output.body` is the rlp-list of transactions, encoded in hex and placed in a string a'la `json` encoding rules:
Expand All @@ -322,10 +463,10 @@ rlpdump -hex $(cat signed_txs.rlp | jq -r )
Now, we can now use those (or any other already signed transactions), as input, like so:
```
./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
INFO [12-06|13:06:54.885] Trie dumping started root=e4b924..6aef61
INFO [12-06|13:06:54.885] Trie dumping complete accounts=3 elapsed="100.314µs"
INFO [12-06|13:06:54.885] Wrote file file=alloc.json
INFO [12-06|13:06:54.885] Wrote file file=alloc_rlptx.json
INFO [12-07|04:30:12.425] Trie dumping started root=e4b924..6aef61
INFO [12-07|04:30:12.425] Trie dumping complete accounts=3 elapsed="70.684µs"
INFO [12-07|04:30:12.425] Wrote file file=alloc.json
INFO [12-07|04:30:12.425] Wrote file file=alloc_rlptx.json
```
You might have noticed that the results from these two invocations were stored in two separate files.
And we can now finally check that they match.
Expand Down
Loading

0 comments on commit 6d43e16

Please sign in to comment.