mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
* refactor(all): cleanup the APIs for initializing genesis #25473 * fix(core): fix accessor mismatch for genesis state #26747
355 lines
12 KiB
Go
355 lines
12 KiB
Go
// Copyright 2015 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package tests
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"strings"
|
|
|
|
"github.com/XinFinOrg/XDPoSChain/common"
|
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
|
"github.com/XinFinOrg/XDPoSChain/core"
|
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
|
"github.com/XinFinOrg/XDPoSChain/params"
|
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
|
"github.com/holiman/uint256"
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
// StateTest checks transaction processing without block context.
|
|
// See https://github.com/ethereum/EIPs/issues/176 for the test format specification.
|
|
type StateTest struct {
|
|
json stJSON
|
|
}
|
|
|
|
// StateSubtest selects a specific configuration of a General State Test.
|
|
type StateSubtest struct {
|
|
Fork string
|
|
Index int
|
|
}
|
|
|
|
func (t *StateTest) UnmarshalJSON(in []byte) error {
|
|
return json.Unmarshal(in, &t.json)
|
|
}
|
|
|
|
type stJSON struct {
|
|
Env stEnv `json:"env"`
|
|
Pre types.GenesisAlloc `json:"pre"`
|
|
Tx stTransaction `json:"transaction"`
|
|
Out hexutil.Bytes `json:"out"`
|
|
Post map[string][]stPostState `json:"post"`
|
|
}
|
|
|
|
type stPostState struct {
|
|
Root common.UnprefixedHash `json:"hash"`
|
|
Logs common.UnprefixedHash `json:"logs"`
|
|
Indexes struct {
|
|
Data int `json:"data"`
|
|
Gas int `json:"gas"`
|
|
Value int `json:"value"`
|
|
}
|
|
}
|
|
|
|
//go:generate go run github.com/fjl/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"`
|
|
BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"`
|
|
}
|
|
|
|
type stEnvMarshaling struct {
|
|
Coinbase common.UnprefixedAddress
|
|
Difficulty *math.HexOrDecimal256
|
|
GasLimit math.HexOrDecimal64
|
|
Number math.HexOrDecimal64
|
|
Timestamp math.HexOrDecimal64
|
|
BaseFee *math.HexOrDecimal256
|
|
}
|
|
|
|
//go:generate go run github.com/fjl/gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go
|
|
|
|
type stTransaction struct {
|
|
GasPrice *big.Int `json:"gasPrice"`
|
|
MaxFeePerGas *big.Int `json:"maxFeePerGas"`
|
|
MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas"`
|
|
Nonce uint64 `json:"nonce"`
|
|
To string `json:"to"`
|
|
Data []string `json:"data"`
|
|
AccessLists []*types.AccessList `json:"accessLists,omitempty"`
|
|
GasLimit []uint64 `json:"gasLimit"`
|
|
Value []string `json:"value"`
|
|
PrivateKey []byte `json:"secretKey"`
|
|
AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"`
|
|
}
|
|
|
|
type stTransactionMarshaling struct {
|
|
GasPrice *math.HexOrDecimal256
|
|
MaxFeePerGas *math.HexOrDecimal256
|
|
MaxPriorityFeePerGas *math.HexOrDecimal256
|
|
Nonce math.HexOrDecimal64
|
|
GasLimit []math.HexOrDecimal64
|
|
PrivateKey hexutil.Bytes
|
|
}
|
|
|
|
//go:generate go run github.com/fjl/gencodec -type stAuthorization -field-override stAuthorizationMarshaling -out gen_stauthorization.go
|
|
|
|
// Authorization is an authorization from an account to deploy code at its address.
|
|
type stAuthorization struct {
|
|
ChainID *big.Int `json:"chainId" gencodec:"required"`
|
|
Address common.Address `json:"address" gencodec:"required"`
|
|
Nonce uint64 `json:"nonce" gencodec:"required"`
|
|
V uint8 `json:"v" gencodec:"required"`
|
|
R *big.Int `json:"r" gencodec:"required"`
|
|
S *big.Int `json:"s" gencodec:"required"`
|
|
}
|
|
|
|
// field type overrides for gencodec
|
|
type stAuthorizationMarshaling struct {
|
|
ChainID *math.HexOrDecimal256
|
|
Nonce math.HexOrDecimal64
|
|
V math.HexOrDecimal64
|
|
R *math.HexOrDecimal256
|
|
S *math.HexOrDecimal256
|
|
}
|
|
|
|
// Subtests returns all valid subtests of the test.
|
|
func (t *StateTest) Subtests() []StateSubtest {
|
|
var sub []StateSubtest
|
|
for fork, pss := range t.json.Post {
|
|
for i := range pss {
|
|
sub = append(sub, StateSubtest{fork, i})
|
|
}
|
|
}
|
|
return sub
|
|
}
|
|
|
|
// Run executes a specific subtest.
|
|
func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateDB, error) {
|
|
config, ok := Forks[subtest.Fork]
|
|
if !ok {
|
|
return nil, UnsupportedForkError{subtest.Fork}
|
|
}
|
|
block := t.genesis(config).ToBlock()
|
|
db := rawdb.NewMemoryDatabase()
|
|
statedb := MakePreState(db, t.json.Pre)
|
|
|
|
var baseFee *big.Int
|
|
if config.IsEIP1559(new(big.Int)) {
|
|
baseFee = t.json.Env.BaseFee
|
|
if baseFee == nil {
|
|
// Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to
|
|
// parent - 2 : 0xa as the basefee for 'this' context.
|
|
baseFee = big.NewInt(common.BaseFee.Int64())
|
|
}
|
|
}
|
|
post := t.json.Post[subtest.Fork][subtest.Index]
|
|
msg, err := t.json.Tx.toMessage(post, baseFee)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Prepare the EVM.
|
|
txContext := core.NewEVMTxContext(msg)
|
|
context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
|
|
context.GetHash = vmTestBlockHash
|
|
context.BaseFee = baseFee
|
|
evm := vm.NewEVM(context, txContext, statedb, nil, config, vmconfig)
|
|
|
|
// Execute the message.
|
|
snapshot := statedb.Snapshot()
|
|
gaspool := new(core.GasPool)
|
|
gaspool.AddGas(block.GasLimit())
|
|
|
|
coinbase := &t.json.Env.Coinbase
|
|
if _, err := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil {
|
|
statedb.RevertToSnapshot(snapshot)
|
|
}
|
|
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
|
|
return statedb, fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs)
|
|
}
|
|
|
|
// Commit block
|
|
root, _ := statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number()))
|
|
if root != common.Hash(post.Root) {
|
|
return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
|
|
}
|
|
// Re-init the post-state instance for further operation
|
|
statedb, err = state.New(root, statedb.Database())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return statedb, nil
|
|
}
|
|
|
|
func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
|
|
return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
|
|
}
|
|
|
|
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB {
|
|
sdb := state.NewDatabase(db)
|
|
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
|
for addr, a := range accounts {
|
|
statedb.SetCode(addr, a.Code)
|
|
statedb.SetNonce(addr, a.Nonce)
|
|
statedb.SetBalance(addr, a.Balance, tracing.BalanceChangeUnspecified)
|
|
for k, v := range a.Storage {
|
|
statedb.SetState(addr, k, v)
|
|
}
|
|
}
|
|
// Commit and re-open to start with a clean state.
|
|
root, _ := statedb.Commit(0, false)
|
|
statedb, _ = state.New(root, sdb)
|
|
return statedb
|
|
}
|
|
|
|
func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
|
|
return &core.Genesis{
|
|
Config: config,
|
|
Coinbase: t.json.Env.Coinbase,
|
|
Difficulty: t.json.Env.Difficulty,
|
|
GasLimit: t.json.Env.GasLimit,
|
|
Number: t.json.Env.Number,
|
|
Timestamp: t.json.Env.Timestamp,
|
|
Alloc: t.json.Pre,
|
|
}
|
|
}
|
|
|
|
func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Message, error) {
|
|
// Derive sender from private key if present.
|
|
var from common.Address
|
|
if len(tx.PrivateKey) > 0 {
|
|
key, err := crypto.ToECDSA(tx.PrivateKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid private key: %v", err)
|
|
}
|
|
from = crypto.PubkeyToAddress(key.PublicKey)
|
|
}
|
|
// Parse recipient if present.
|
|
var to *common.Address
|
|
if tx.To != "" {
|
|
to = new(common.Address)
|
|
if err := to.UnmarshalText([]byte(tx.To)); err != nil {
|
|
return nil, fmt.Errorf("invalid to address: %v", err)
|
|
}
|
|
}
|
|
|
|
// Get values specific to this post state.
|
|
if ps.Indexes.Data > len(tx.Data) {
|
|
return nil, fmt.Errorf("tx data index %d out of bounds", ps.Indexes.Data)
|
|
}
|
|
if ps.Indexes.Value > len(tx.Value) {
|
|
return nil, fmt.Errorf("tx value index %d out of bounds", ps.Indexes.Value)
|
|
}
|
|
if ps.Indexes.Gas > len(tx.GasLimit) {
|
|
return nil, fmt.Errorf("tx gas limit index %d out of bounds", ps.Indexes.Gas)
|
|
}
|
|
dataHex := tx.Data[ps.Indexes.Data]
|
|
valueHex := tx.Value[ps.Indexes.Value]
|
|
gasLimit := tx.GasLimit[ps.Indexes.Gas]
|
|
// Value, Data hex encoding is messy: https://github.com/ethereum/tests/issues/203
|
|
value := new(big.Int)
|
|
if valueHex != "0x" {
|
|
v, ok := math.ParseBig256(valueHex)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid tx value %q", valueHex)
|
|
}
|
|
value = v
|
|
}
|
|
data, err := hex.DecodeString(strings.TrimPrefix(dataHex, "0x"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid tx data %q", dataHex)
|
|
}
|
|
var accessList types.AccessList
|
|
if tx.AccessLists != nil && tx.AccessLists[ps.Indexes.Data] != nil {
|
|
accessList = *tx.AccessLists[ps.Indexes.Data]
|
|
}
|
|
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
|
gasPrice := tx.GasPrice
|
|
if baseFee != nil {
|
|
if tx.MaxFeePerGas == nil {
|
|
tx.MaxFeePerGas = gasPrice
|
|
}
|
|
if tx.MaxFeePerGas == nil {
|
|
tx.MaxFeePerGas = new(big.Int)
|
|
}
|
|
if tx.MaxPriorityFeePerGas == nil {
|
|
tx.MaxPriorityFeePerGas = tx.MaxFeePerGas
|
|
}
|
|
gasPrice = new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee)
|
|
if gasPrice.Cmp(tx.MaxFeePerGas) > 0 {
|
|
gasPrice.Set(tx.MaxFeePerGas)
|
|
}
|
|
}
|
|
if gasPrice == nil {
|
|
return nil, errors.New("no gas price provided")
|
|
}
|
|
var authList []types.SetCodeAuthorization
|
|
if tx.AuthorizationList != nil {
|
|
authList = make([]types.SetCodeAuthorization, len(tx.AuthorizationList))
|
|
for i, auth := range tx.AuthorizationList {
|
|
authList[i] = types.SetCodeAuthorization{
|
|
ChainID: *uint256.MustFromBig(auth.ChainID),
|
|
Address: auth.Address,
|
|
Nonce: auth.Nonce,
|
|
V: auth.V,
|
|
R: *uint256.MustFromBig(auth.R),
|
|
S: *uint256.MustFromBig(auth.S),
|
|
}
|
|
}
|
|
}
|
|
|
|
msg := &core.Message{
|
|
From: from,
|
|
To: to,
|
|
Nonce: tx.Nonce,
|
|
Value: value,
|
|
GasLimit: gasLimit,
|
|
GasPrice: tx.GasPrice,
|
|
GasFeeCap: tx.MaxFeePerGas,
|
|
GasTipCap: tx.MaxPriorityFeePerGas,
|
|
Data: data,
|
|
AccessList: accessList,
|
|
SetCodeAuthorizations: authList,
|
|
SkipNonceChecks: false,
|
|
SkipFromEOACheck: false,
|
|
}
|
|
return msg, nil
|
|
}
|
|
|
|
func rlpHash(x interface{}) (h common.Hash) {
|
|
hw := sha3.NewLegacyKeccak256()
|
|
if err := rlp.Encode(hw, x); err != nil {
|
|
panic("can't encode: " + err.Error())
|
|
}
|
|
hw.Sum(h[:0])
|
|
return h
|
|
}
|