Skip to content

Commit 84c51bc

Browse files
authored
cmd/evm: add difficulty calculation to t8n tool (ethereum#23353)
This PR adds functionality to the evm t8n to calculate ethash difficulty. If the caller does not provide a currentDifficulty, but instead provides the parentTimestamp (well, semi-optional, will default to 0 if not given), and parentDifficulty, we can calculate it for him. The caller can also provide a parentUncleHash. In most, but not all cases, the parent uncle hash also affects the formula. If no such hash is provided (or, if the empty all-zero hash is provided), it's assumed that there were no uncles.
1 parent efee853 commit 84c51bc

File tree

8 files changed

+172
-40
lines changed

8 files changed

+172
-40
lines changed

cmd/evm/internal/t8ntool/execution.go

+49-21
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/ethereum/go-ethereum/common"
2525
"github.com/ethereum/go-ethereum/common/math"
26+
"github.com/ethereum/go-ethereum/consensus/ethash"
2627
"github.com/ethereum/go-ethereum/consensus/misc"
2728
"github.com/ethereum/go-ethereum/core"
2829
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -46,13 +47,14 @@ type Prestate struct {
4647
// ExecutionResult contains the execution status after running a state test, any
4748
// error that might have occurred and a dump of the final state if requested.
4849
type ExecutionResult struct {
49-
StateRoot common.Hash `json:"stateRoot"`
50-
TxRoot common.Hash `json:"txRoot"`
51-
ReceiptRoot common.Hash `json:"receiptRoot"`
52-
LogsHash common.Hash `json:"logsHash"`
53-
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
54-
Receipts types.Receipts `json:"receipts"`
55-
Rejected []*rejectedTx `json:"rejected,omitempty"`
50+
StateRoot common.Hash `json:"stateRoot"`
51+
TxRoot common.Hash `json:"txRoot"`
52+
ReceiptRoot common.Hash `json:"receiptRoot"`
53+
LogsHash common.Hash `json:"logsHash"`
54+
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
55+
Receipts types.Receipts `json:"receipts"`
56+
Rejected []*rejectedTx `json:"rejected,omitempty"`
57+
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
5658
}
5759

5860
type ommer struct {
@@ -62,23 +64,28 @@ type ommer struct {
6264

6365
//go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
6466
type stEnv struct {
65-
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
66-
Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
67-
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
68-
Number uint64 `json:"currentNumber" gencodec:"required"`
69-
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
70-
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
71-
Ommers []ommer `json:"ommers,omitempty"`
72-
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
67+
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
68+
Difficulty *big.Int `json:"currentDifficulty"`
69+
ParentDifficulty *big.Int `json:"parentDifficulty"`
70+
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
71+
Number uint64 `json:"currentNumber" gencodec:"required"`
72+
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
73+
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
74+
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
75+
Ommers []ommer `json:"ommers,omitempty"`
76+
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
77+
ParentUncleHash common.Hash `json:"parentUncleHash"`
7378
}
7479

7580
type stEnvMarshaling struct {
76-
Coinbase common.UnprefixedAddress
77-
Difficulty *math.HexOrDecimal256
78-
GasLimit math.HexOrDecimal64
79-
Number math.HexOrDecimal64
80-
Timestamp math.HexOrDecimal64
81-
BaseFee *math.HexOrDecimal256
81+
Coinbase common.UnprefixedAddress
82+
Difficulty *math.HexOrDecimal256
83+
ParentDifficulty *math.HexOrDecimal256
84+
GasLimit math.HexOrDecimal64
85+
Number math.HexOrDecimal64
86+
Timestamp math.HexOrDecimal64
87+
ParentTimestamp math.HexOrDecimal64
88+
BaseFee *math.HexOrDecimal256
8289
}
8390

8491
type rejectedTx struct {
@@ -247,6 +254,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
247254
LogsHash: rlpHash(statedb.Logs()),
248255
Receipts: receipts,
249256
Rejected: rejectedTxs,
257+
Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
250258
}
251259
return statedb, execRs, nil
252260
}
@@ -274,3 +282,23 @@ func rlpHash(x interface{}) (h common.Hash) {
274282
hw.Sum(h[:0])
275283
return h
276284
}
285+
286+
// calcDifficulty is based on ethash.CalcDifficulty. This method is used in case
287+
// the caller does not provide an explicit difficulty, but instead provides only
288+
// parent timestamp + difficulty.
289+
// Note: this method only works for ethash engine.
290+
func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64,
291+
parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int {
292+
uncleHash := parentUncleHash
293+
if uncleHash == (common.Hash{}) {
294+
uncleHash = types.EmptyUncleHash
295+
}
296+
parent := &types.Header{
297+
ParentHash: common.Hash{},
298+
UncleHash: uncleHash,
299+
Difficulty: parentDifficulty,
300+
Number: new(big.Int).SetUint64(number - 1),
301+
Time: parentTime,
302+
}
303+
return ethash.CalcDifficulty(config, currentTime, parent)
304+
}

cmd/evm/internal/t8ntool/gen_stenv.go

+36-19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/evm/internal/t8ntool/transition.go

+14
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,20 @@ func Main(ctx *cli.Context) error {
252252
return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
253253
}
254254
}
255+
if env := prestate.Env; env.Difficulty == nil {
256+
// If difficulty was not provided by caller, we need to calculate it.
257+
switch {
258+
case env.ParentDifficulty == nil:
259+
return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty"))
260+
case env.Number == 0:
261+
return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0"))
262+
case env.Timestamp <= env.ParentTimestamp:
263+
return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)",
264+
env.Timestamp, env.ParentTimestamp))
265+
}
266+
prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp,
267+
env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash)
268+
}
255269
// Run the test and aggregate the result
256270
s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
257271
if err != nil {

cmd/evm/testdata/14/alloc.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
3+
"balance": "0x5ffd4878be161d74",
4+
"code": "0x",
5+
"nonce": "0xac",
6+
"storage": {}
7+
},
8+
"0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192":{
9+
"balance": "0xfeedbead",
10+
"nonce" : "0x00"
11+
}
12+
}

cmd/evm/testdata/14/env.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
3+
"currentGasLimit": "0x750a163df65e8a",
4+
"currentBaseFee": "0x500",
5+
"currentNumber": "12800000",
6+
"currentTimestamp": "10015",
7+
"parentTimestamp" : "99999",
8+
"parentDifficulty" : "0x2000000000000"
9+
}

cmd/evm/testdata/14/env.uncles.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
3+
"currentGasLimit": "0x750a163df65e8a",
4+
"currentBaseFee": "0x500",
5+
"currentNumber": "12800000",
6+
"currentTimestamp": "10035",
7+
"parentTimestamp" : "99999",
8+
"parentDifficulty" : "0x2000000000000",
9+
"parentUncleHash" : "0x000000000000000000000000000000000000000000000000000000000000beef"
10+
}

cmd/evm/testdata/14/readme.md

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
## Difficulty calculation
2+
3+
This test shows how the `evm t8n` can be used to calculate the (ethash) difficulty, if none is provided by the caller.
4+
5+
Calculating it (with an empty set of txs) using `London` rules (and no provided unclehash for the parent block):
6+
```
7+
[user@work evm]$ ./evm t8n --input.alloc=./testdata/14/alloc.json --input.txs=./testdata/14/txs.json --input.env=./testdata/14/env.json --output.result=stdout --state.fork=London
8+
INFO [08-08|17:35:46.876] Trie dumping started root=6f0588..7f4bdc
9+
INFO [08-08|17:35:46.876] Trie dumping complete accounts=2 elapsed="89.313µs"
10+
INFO [08-08|17:35:46.877] Wrote file file=alloc.json
11+
{
12+
"result": {
13+
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
14+
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
15+
"receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
16+
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
17+
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
18+
"receipts": [],
19+
"currentDifficulty": 3311729559732224
20+
}
21+
}
22+
```
23+
Same thing, but this time providing a non-empty (and non-`emptyKeccak`) unclehash, which leads to a slightly different result:
24+
```
25+
[user@work evm]$ ./evm t8n --input.alloc=./testdata/14/alloc.json --input.txs=./testdata/14/txs.json --input.env=./testdata/14/env.uncles.json --output.result=stdout --state.fork=London
26+
INFO [08-08|17:35:49.232] Trie dumping started root=6f0588..7f4bdc
27+
INFO [08-08|17:35:49.232] Trie dumping complete accounts=2 elapsed="83.069µs"
28+
INFO [08-08|17:35:49.233] Wrote file file=alloc.json
29+
{
30+
"result": {
31+
"stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
32+
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
33+
"receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
34+
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
35+
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
36+
"receipts": [],
37+
"currentDifficulty": 3311179803918336
38+
}
39+
}
40+
```
41+

cmd/evm/testdata/14/txs.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]

0 commit comments

Comments
 (0)