cmd/evm: add difficulty calculation to t8n tool (#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.
This commit is contained in:
		
							parent
							
								
									efee85378e
								
							
						
					
					
						commit
						84c51bc5ec
					
				| @ -23,6 +23,7 @@ import ( | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/common/math" | ||||
| 	"github.com/ethereum/go-ethereum/consensus/ethash" | ||||
| 	"github.com/ethereum/go-ethereum/consensus/misc" | ||||
| 	"github.com/ethereum/go-ethereum/core" | ||||
| 	"github.com/ethereum/go-ethereum/core/rawdb" | ||||
| @ -46,13 +47,14 @@ type Prestate struct { | ||||
| // ExecutionResult contains the execution status after running a state test, any
 | ||||
| // error that might have occurred and a dump of the final state if requested.
 | ||||
| type ExecutionResult struct { | ||||
| 	StateRoot   common.Hash    `json:"stateRoot"` | ||||
| 	TxRoot      common.Hash    `json:"txRoot"` | ||||
| 	ReceiptRoot common.Hash    `json:"receiptRoot"` | ||||
| 	LogsHash    common.Hash    `json:"logsHash"` | ||||
| 	Bloom       types.Bloom    `json:"logsBloom"        gencodec:"required"` | ||||
| 	Receipts    types.Receipts `json:"receipts"` | ||||
| 	Rejected    []*rejectedTx  `json:"rejected,omitempty"` | ||||
| 	StateRoot   common.Hash           `json:"stateRoot"` | ||||
| 	TxRoot      common.Hash           `json:"txRoot"` | ||||
| 	ReceiptRoot common.Hash           `json:"receiptRoot"` | ||||
| 	LogsHash    common.Hash           `json:"logsHash"` | ||||
| 	Bloom       types.Bloom           `json:"logsBloom"        gencodec:"required"` | ||||
| 	Receipts    types.Receipts        `json:"receipts"` | ||||
| 	Rejected    []*rejectedTx         `json:"rejected,omitempty"` | ||||
| 	Difficulty  *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"` | ||||
| } | ||||
| 
 | ||||
| type ommer struct { | ||||
| @ -62,23 +64,28 @@ type ommer struct { | ||||
| 
 | ||||
| //go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
 | ||||
| type stEnv struct { | ||||
| 	Coinbase    common.Address                      `json:"currentCoinbase"   gencodec:"required"` | ||||
| 	Difficulty  *big.Int                            `json:"currentDifficulty" gencodec:"required"` | ||||
| 	GasLimit    uint64                              `json:"currentGasLimit"   gencodec:"required"` | ||||
| 	Number      uint64                              `json:"currentNumber"     gencodec:"required"` | ||||
| 	Timestamp   uint64                              `json:"currentTimestamp"  gencodec:"required"` | ||||
| 	BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` | ||||
| 	Ommers      []ommer                             `json:"ommers,omitempty"` | ||||
| 	BaseFee     *big.Int                            `json:"currentBaseFee,omitempty"` | ||||
| 	Coinbase         common.Address                      `json:"currentCoinbase"   gencodec:"required"` | ||||
| 	Difficulty       *big.Int                            `json:"currentDifficulty"` | ||||
| 	ParentDifficulty *big.Int                            `json:"parentDifficulty"` | ||||
| 	GasLimit         uint64                              `json:"currentGasLimit"   gencodec:"required"` | ||||
| 	Number           uint64                              `json:"currentNumber"     gencodec:"required"` | ||||
| 	Timestamp        uint64                              `json:"currentTimestamp"  gencodec:"required"` | ||||
| 	ParentTimestamp  uint64                              `json:"parentTimestamp,omitempty"` | ||||
| 	BlockHashes      map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` | ||||
| 	Ommers           []ommer                             `json:"ommers,omitempty"` | ||||
| 	BaseFee          *big.Int                            `json:"currentBaseFee,omitempty"` | ||||
| 	ParentUncleHash  common.Hash                         `json:"parentUncleHash"` | ||||
| } | ||||
| 
 | ||||
| type stEnvMarshaling struct { | ||||
| 	Coinbase   common.UnprefixedAddress | ||||
| 	Difficulty *math.HexOrDecimal256 | ||||
| 	GasLimit   math.HexOrDecimal64 | ||||
| 	Number     math.HexOrDecimal64 | ||||
| 	Timestamp  math.HexOrDecimal64 | ||||
| 	BaseFee    *math.HexOrDecimal256 | ||||
| 	Coinbase         common.UnprefixedAddress | ||||
| 	Difficulty       *math.HexOrDecimal256 | ||||
| 	ParentDifficulty *math.HexOrDecimal256 | ||||
| 	GasLimit         math.HexOrDecimal64 | ||||
| 	Number           math.HexOrDecimal64 | ||||
| 	Timestamp        math.HexOrDecimal64 | ||||
| 	ParentTimestamp  math.HexOrDecimal64 | ||||
| 	BaseFee          *math.HexOrDecimal256 | ||||
| } | ||||
| 
 | ||||
| type rejectedTx struct { | ||||
| @ -247,6 +254,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, | ||||
| 		LogsHash:    rlpHash(statedb.Logs()), | ||||
| 		Receipts:    receipts, | ||||
| 		Rejected:    rejectedTxs, | ||||
| 		Difficulty:  (*math.HexOrDecimal256)(vmContext.Difficulty), | ||||
| 	} | ||||
| 	return statedb, execRs, nil | ||||
| } | ||||
| @ -274,3 +282,23 @@ func rlpHash(x interface{}) (h common.Hash) { | ||||
| 	hw.Sum(h[:0]) | ||||
| 	return h | ||||
| } | ||||
| 
 | ||||
| // calcDifficulty is based on ethash.CalcDifficulty. This method is used in case
 | ||||
| // the caller does not provide an explicit difficulty, but instead provides only
 | ||||
| // parent timestamp + difficulty.
 | ||||
| // Note: this method only works for ethash engine.
 | ||||
| func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64, | ||||
| 	parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int { | ||||
| 	uncleHash := parentUncleHash | ||||
| 	if uncleHash == (common.Hash{}) { | ||||
| 		uncleHash = types.EmptyUncleHash | ||||
| 	} | ||||
| 	parent := &types.Header{ | ||||
| 		ParentHash: common.Hash{}, | ||||
| 		UncleHash:  uncleHash, | ||||
| 		Difficulty: parentDifficulty, | ||||
| 		Number:     new(big.Int).SetUint64(number - 1), | ||||
| 		Time:       parentTime, | ||||
| 	} | ||||
| 	return ethash.CalcDifficulty(config, currentTime, parent) | ||||
| } | ||||
|  | ||||
| @ -16,38 +16,47 @@ var _ = (*stEnvMarshaling)(nil) | ||||
| // MarshalJSON marshals as JSON.
 | ||||
| func (s stEnv) MarshalJSON() ([]byte, error) { | ||||
| 	type stEnv struct { | ||||
| 		Coinbase    common.UnprefixedAddress            `json:"currentCoinbase"   gencodec:"required"` | ||||
| 		Difficulty  *math.HexOrDecimal256               `json:"currentDifficulty" gencodec:"required"` | ||||
| 		GasLimit    math.HexOrDecimal64                 `json:"currentGasLimit"   gencodec:"required"` | ||||
| 		Number      math.HexOrDecimal64                 `json:"currentNumber"     gencodec:"required"` | ||||
| 		Timestamp   math.HexOrDecimal64                 `json:"currentTimestamp"  gencodec:"required"` | ||||
| 		BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` | ||||
| 		Ommers      []ommer                             `json:"ommers,omitempty"` | ||||
| 		BaseFee     *math.HexOrDecimal256               `json:"currentBaseFee,omitempty"` | ||||
| 		Coinbase         common.UnprefixedAddress            `json:"currentCoinbase"   gencodec:"required"` | ||||
| 		Difficulty       *math.HexOrDecimal256               `json:"currentDifficulty"` | ||||
| 		ParentDifficulty *math.HexOrDecimal256               `json:"parentDifficulty"` | ||||
| 		GasLimit         math.HexOrDecimal64                 `json:"currentGasLimit"   gencodec:"required"` | ||||
| 		Number           math.HexOrDecimal64                 `json:"currentNumber"     gencodec:"required"` | ||||
| 		Timestamp        math.HexOrDecimal64                 `json:"currentTimestamp"  gencodec:"required"` | ||||
| 		ParentTimestamp  math.HexOrDecimal64                 `json:"parentTimestamp,omitempty"` | ||||
| 		BlockHashes      map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` | ||||
| 		Ommers           []ommer                             `json:"ommers,omitempty"` | ||||
| 		BaseFee          *math.HexOrDecimal256               `json:"currentBaseFee,omitempty"` | ||||
| 		ParentUncleHash  common.Hash                         `json:"parentUncleHash"` | ||||
| 	} | ||||
| 	var enc stEnv | ||||
| 	enc.Coinbase = common.UnprefixedAddress(s.Coinbase) | ||||
| 	enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty) | ||||
| 	enc.ParentDifficulty = (*math.HexOrDecimal256)(s.ParentDifficulty) | ||||
| 	enc.GasLimit = math.HexOrDecimal64(s.GasLimit) | ||||
| 	enc.Number = math.HexOrDecimal64(s.Number) | ||||
| 	enc.Timestamp = math.HexOrDecimal64(s.Timestamp) | ||||
| 	enc.ParentTimestamp = math.HexOrDecimal64(s.ParentTimestamp) | ||||
| 	enc.BlockHashes = s.BlockHashes | ||||
| 	enc.Ommers = s.Ommers | ||||
| 	enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee) | ||||
| 	enc.ParentUncleHash = s.ParentUncleHash | ||||
| 	return json.Marshal(&enc) | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON unmarshals from JSON.
 | ||||
| func (s *stEnv) UnmarshalJSON(input []byte) error { | ||||
| 	type stEnv struct { | ||||
| 		Coinbase    *common.UnprefixedAddress           `json:"currentCoinbase"   gencodec:"required"` | ||||
| 		Difficulty  *math.HexOrDecimal256               `json:"currentDifficulty" gencodec:"required"` | ||||
| 		GasLimit    *math.HexOrDecimal64                `json:"currentGasLimit"   gencodec:"required"` | ||||
| 		Number      *math.HexOrDecimal64                `json:"currentNumber"     gencodec:"required"` | ||||
| 		Timestamp   *math.HexOrDecimal64                `json:"currentTimestamp"  gencodec:"required"` | ||||
| 		BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` | ||||
| 		Ommers      []ommer                             `json:"ommers,omitempty"` | ||||
| 		BaseFee     *math.HexOrDecimal256               `json:"currentBaseFee,omitempty"` | ||||
| 		Coinbase         *common.UnprefixedAddress           `json:"currentCoinbase"   gencodec:"required"` | ||||
| 		Difficulty       *math.HexOrDecimal256               `json:"currentDifficulty"` | ||||
| 		ParentDifficulty *math.HexOrDecimal256               `json:"parentDifficulty"` | ||||
| 		GasLimit         *math.HexOrDecimal64                `json:"currentGasLimit"   gencodec:"required"` | ||||
| 		Number           *math.HexOrDecimal64                `json:"currentNumber"     gencodec:"required"` | ||||
| 		Timestamp        *math.HexOrDecimal64                `json:"currentTimestamp"  gencodec:"required"` | ||||
| 		ParentTimestamp  *math.HexOrDecimal64                `json:"parentTimestamp,omitempty"` | ||||
| 		BlockHashes      map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` | ||||
| 		Ommers           []ommer                             `json:"ommers,omitempty"` | ||||
| 		BaseFee          *math.HexOrDecimal256               `json:"currentBaseFee,omitempty"` | ||||
| 		ParentUncleHash  *common.Hash                        `json:"parentUncleHash"` | ||||
| 	} | ||||
| 	var dec stEnv | ||||
| 	if err := json.Unmarshal(input, &dec); err != nil { | ||||
| @ -57,10 +66,12 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { | ||||
| 		return errors.New("missing required field 'currentCoinbase' for stEnv") | ||||
| 	} | ||||
| 	s.Coinbase = common.Address(*dec.Coinbase) | ||||
| 	if dec.Difficulty == nil { | ||||
| 		return errors.New("missing required field 'currentDifficulty' for stEnv") | ||||
| 	if dec.Difficulty != nil { | ||||
| 		s.Difficulty = (*big.Int)(dec.Difficulty) | ||||
| 	} | ||||
| 	if dec.ParentDifficulty != nil { | ||||
| 		s.ParentDifficulty = (*big.Int)(dec.ParentDifficulty) | ||||
| 	} | ||||
| 	s.Difficulty = (*big.Int)(dec.Difficulty) | ||||
| 	if dec.GasLimit == nil { | ||||
| 		return errors.New("missing required field 'currentGasLimit' for stEnv") | ||||
| 	} | ||||
| @ -73,6 +84,9 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { | ||||
| 		return errors.New("missing required field 'currentTimestamp' for stEnv") | ||||
| 	} | ||||
| 	s.Timestamp = uint64(*dec.Timestamp) | ||||
| 	if dec.ParentTimestamp != nil { | ||||
| 		s.ParentTimestamp = uint64(*dec.ParentTimestamp) | ||||
| 	} | ||||
| 	if dec.BlockHashes != nil { | ||||
| 		s.BlockHashes = dec.BlockHashes | ||||
| 	} | ||||
| @ -82,5 +96,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { | ||||
| 	if dec.BaseFee != nil { | ||||
| 		s.BaseFee = (*big.Int)(dec.BaseFee) | ||||
| 	} | ||||
| 	if dec.ParentUncleHash != nil { | ||||
| 		s.ParentUncleHash = *dec.ParentUncleHash | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -252,6 +252,20 @@ func Main(ctx *cli.Context) error { | ||||
| 			return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) | ||||
| 		} | ||||
| 	} | ||||
| 	if env := prestate.Env; env.Difficulty == nil { | ||||
| 		// If difficulty was not provided by caller, we need to calculate it.
 | ||||
| 		switch { | ||||
| 		case env.ParentDifficulty == nil: | ||||
| 			return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty")) | ||||
| 		case env.Number == 0: | ||||
| 			return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0")) | ||||
| 		case env.Timestamp <= env.ParentTimestamp: | ||||
| 			return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)", | ||||
| 				env.Timestamp, env.ParentTimestamp)) | ||||
| 		} | ||||
| 		prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp, | ||||
| 			env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash) | ||||
| 	} | ||||
| 	// Run the test and aggregate the result
 | ||||
| 	s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer) | ||||
| 	if err != nil { | ||||
|  | ||||
							
								
								
									
										12
									
								
								cmd/evm/testdata/14/alloc.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								cmd/evm/testdata/14/alloc.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| { | ||||
|   "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { | ||||
|     "balance": "0x5ffd4878be161d74", | ||||
|     "code": "0x", | ||||
|     "nonce": "0xac", | ||||
|     "storage": {} | ||||
|   }, | ||||
|   "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192":{ | ||||
|     "balance": "0xfeedbead", | ||||
|     "nonce" : "0x00" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										9
									
								
								cmd/evm/testdata/14/env.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								cmd/evm/testdata/14/env.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| { | ||||
|   "currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b", | ||||
|   "currentGasLimit": "0x750a163df65e8a", | ||||
|   "currentBaseFee": "0x500", | ||||
|   "currentNumber": "12800000", | ||||
|   "currentTimestamp": "10015", | ||||
|   "parentTimestamp" : "99999", | ||||
|   "parentDifficulty" : "0x2000000000000" | ||||
| } | ||||
							
								
								
									
										10
									
								
								cmd/evm/testdata/14/env.uncles.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								cmd/evm/testdata/14/env.uncles.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| { | ||||
|   "currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b", | ||||
|   "currentGasLimit": "0x750a163df65e8a", | ||||
|   "currentBaseFee": "0x500", | ||||
|   "currentNumber": "12800000", | ||||
|   "currentTimestamp": "10035", | ||||
|   "parentTimestamp" : "99999", | ||||
|   "parentDifficulty" : "0x2000000000000", | ||||
|   "parentUncleHash" : "0x000000000000000000000000000000000000000000000000000000000000beef" | ||||
| } | ||||
							
								
								
									
										41
									
								
								cmd/evm/testdata/14/readme.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								cmd/evm/testdata/14/readme.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| ## Difficulty calculation | ||||
| 
 | ||||
| This test shows how the `evm t8n` can be used to calculate the (ethash) difficulty, if none is provided by the caller.  | ||||
| 
 | ||||
| Calculating it (with an empty set of txs) using `London` rules (and no provided unclehash for the parent block): | ||||
| ``` | ||||
| [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 | ||||
| INFO [08-08|17:35:46.876] Trie dumping started                     root=6f0588..7f4bdc | ||||
| INFO [08-08|17:35:46.876] Trie dumping complete                    accounts=2 elapsed="89.313µs" | ||||
| INFO [08-08|17:35:46.877] Wrote file                               file=alloc.json | ||||
| { | ||||
|  "result": { | ||||
|   "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc", | ||||
|   "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", | ||||
|   "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", | ||||
|   "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", | ||||
|   "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", | ||||
|   "receipts": [], | ||||
|   "currentDifficulty": 3311729559732224 | ||||
|  } | ||||
| } | ||||
| ``` | ||||
| Same thing, but this time providing a non-empty (and non-`emptyKeccak`) unclehash, which leads to a slightly different result: | ||||
| ``` | ||||
| [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 | ||||
| INFO [08-08|17:35:49.232] Trie dumping started                     root=6f0588..7f4bdc | ||||
| INFO [08-08|17:35:49.232] Trie dumping complete                    accounts=2 elapsed="83.069µs" | ||||
| INFO [08-08|17:35:49.233] Wrote file                               file=alloc.json | ||||
| { | ||||
|  "result": { | ||||
|   "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc", | ||||
|   "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", | ||||
|   "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", | ||||
|   "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", | ||||
|   "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", | ||||
|   "receipts": [], | ||||
|   "currentDifficulty": 3311179803918336 | ||||
|  } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
							
								
								
									
										1
									
								
								cmd/evm/testdata/14/txs.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cmd/evm/testdata/14/txs.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| [] | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user