github.com/ethereum/go-ethereum@v1.16.1/cmd/devp2p/internal/ethtest/chain.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package ethtest
    18  
    19  import (
    20  	"bytes"
    21  	"compress/gzip"
    22  	"crypto/ecdsa"
    23  	"encoding/json"
    24  	"errors"
    25  	"fmt"
    26  	"io"
    27  	"maps"
    28  	"math/big"
    29  	"os"
    30  	"path/filepath"
    31  	"slices"
    32  	"strings"
    33  
    34  	"github.com/ethereum/go-ethereum/common"
    35  	"github.com/ethereum/go-ethereum/common/hexutil"
    36  	"github.com/ethereum/go-ethereum/core"
    37  	"github.com/ethereum/go-ethereum/core/forkid"
    38  	"github.com/ethereum/go-ethereum/core/state"
    39  	"github.com/ethereum/go-ethereum/core/types"
    40  	"github.com/ethereum/go-ethereum/crypto"
    41  	"github.com/ethereum/go-ethereum/eth/protocols/eth"
    42  	"github.com/ethereum/go-ethereum/params"
    43  	"github.com/ethereum/go-ethereum/rlp"
    44  )
    45  
    46  // Chain is a lightweight blockchain-like store which can read a hivechain
    47  // created chain.
    48  type Chain struct {
    49  	genesis core.Genesis
    50  	blocks  []*types.Block
    51  	state   map[common.Address]state.DumpAccount // state of head block
    52  	senders map[common.Address]*senderInfo
    53  	config  *params.ChainConfig
    54  }
    55  
    56  // NewChain takes the given chain.rlp file, and decodes and returns
    57  // the blocks from the file.
    58  func NewChain(dir string) (*Chain, error) {
    59  	gen, err := loadGenesis(filepath.Join(dir, "genesis.json"))
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	gblock := gen.ToBlock()
    64  
    65  	blocks, err := blocksFromFile(filepath.Join(dir, "chain.rlp"), gblock)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	state, err := readState(filepath.Join(dir, "headstate.json"))
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	accounts, err := readAccounts(filepath.Join(dir, "accounts.json"))
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	return &Chain{
    78  		genesis: gen,
    79  		blocks:  blocks,
    80  		state:   state,
    81  		senders: accounts,
    82  		config:  gen.Config,
    83  	}, nil
    84  }
    85  
    86  // senderInfo is an account record as output in the "accounts.json" file from
    87  // hivechain.
    88  type senderInfo struct {
    89  	Key   *ecdsa.PrivateKey `json:"key"`
    90  	Nonce uint64            `json:"nonce"`
    91  }
    92  
    93  // Head returns the chain head.
    94  func (c *Chain) Head() *types.Block {
    95  	return c.blocks[c.Len()-1]
    96  }
    97  
    98  // AccountsInHashOrder returns all accounts of the head state, ordered by hash of address.
    99  func (c *Chain) AccountsInHashOrder() []state.DumpAccount {
   100  	list := make([]state.DumpAccount, len(c.state))
   101  	i := 0
   102  	for addr, acc := range c.state {
   103  		list[i] = acc
   104  		list[i].Address = &addr
   105  		if len(acc.AddressHash) != 32 {
   106  			panic(fmt.Errorf("missing/invalid SecureKey in dump account %v", addr))
   107  		}
   108  		i++
   109  	}
   110  	slices.SortFunc(list, func(x, y state.DumpAccount) int {
   111  		return bytes.Compare(x.AddressHash, y.AddressHash)
   112  	})
   113  	return list
   114  }
   115  
   116  // CodeHashes returns all bytecode hashes contained in the head state.
   117  func (c *Chain) CodeHashes() []common.Hash {
   118  	var hashes []common.Hash
   119  	seen := make(map[common.Hash]struct{})
   120  	seen[types.EmptyCodeHash] = struct{}{}
   121  	for _, acc := range c.state {
   122  		h := common.BytesToHash(acc.CodeHash)
   123  		if _, ok := seen[h]; ok {
   124  			continue
   125  		}
   126  		hashes = append(hashes, h)
   127  		seen[h] = struct{}{}
   128  	}
   129  	slices.SortFunc(hashes, (common.Hash).Cmp)
   130  	return hashes
   131  }
   132  
   133  // Len returns the length of the chain.
   134  func (c *Chain) Len() int {
   135  	return len(c.blocks)
   136  }
   137  
   138  // ForkID gets the fork id of the chain.
   139  func (c *Chain) ForkID() forkid.ID {
   140  	return forkid.NewID(c.config, c.blocks[0], uint64(c.Len()), c.blocks[c.Len()-1].Time())
   141  }
   142  
   143  // TD calculates the total difficulty of the chain at the
   144  // chain head.
   145  func (c *Chain) TD() *big.Int {
   146  	return new(big.Int)
   147  }
   148  
   149  // GetBlock returns the block at the specified number.
   150  func (c *Chain) GetBlock(number int) *types.Block {
   151  	return c.blocks[number]
   152  }
   153  
   154  // RootAt returns the state root for the block at the given height.
   155  func (c *Chain) RootAt(height int) common.Hash {
   156  	if height < c.Len() {
   157  		return c.blocks[height].Root()
   158  	}
   159  	return common.Hash{}
   160  }
   161  
   162  // GetSender returns the address associated with account at the index in the
   163  // pre-funded accounts list.
   164  func (c *Chain) GetSender(idx int) (common.Address, uint64) {
   165  	accounts := slices.SortedFunc(maps.Keys(c.senders), common.Address.Cmp)
   166  
   167  	addr := accounts[idx]
   168  	return addr, c.senders[addr].Nonce
   169  }
   170  
   171  // IncNonce increases the specified signing account's pending nonce.
   172  func (c *Chain) IncNonce(addr common.Address, amt uint64) {
   173  	if _, ok := c.senders[addr]; !ok {
   174  		panic("nonce increment for non-signer")
   175  	}
   176  	c.senders[addr].Nonce += amt
   177  }
   178  
   179  // Balance returns the balance of an account at the head of the chain.
   180  func (c *Chain) Balance(addr common.Address) *big.Int {
   181  	bal := new(big.Int)
   182  	if acc, ok := c.state[addr]; ok {
   183  		bal, _ = bal.SetString(acc.Balance, 10)
   184  	}
   185  	return bal
   186  }
   187  
   188  // SignTx signs a transaction for the specified from account, so long as that
   189  // account was in the hivechain accounts dump.
   190  func (c *Chain) SignTx(from common.Address, tx *types.Transaction) (*types.Transaction, error) {
   191  	signer := types.LatestSigner(c.config)
   192  	acc, ok := c.senders[from]
   193  	if !ok {
   194  		return nil, fmt.Errorf("account not available for signing: %s", from)
   195  	}
   196  	return types.SignTx(tx, signer, acc.Key)
   197  }
   198  
   199  // GetHeaders returns the headers base on an ethGetPacketHeadersPacket.
   200  func (c *Chain) GetHeaders(req *eth.GetBlockHeadersPacket) ([]*types.Header, error) {
   201  	if req.Amount < 1 {
   202  		return nil, errors.New("no block headers requested")
   203  	}
   204  	var (
   205  		headers     = make([]*types.Header, req.Amount)
   206  		blockNumber uint64
   207  	)
   208  	// Range over blocks to check if our chain has the requested header.
   209  	for _, block := range c.blocks {
   210  		if block.Hash() == req.Origin.Hash || block.Number().Uint64() == req.Origin.Number {
   211  			headers[0] = block.Header()
   212  			blockNumber = block.Number().Uint64()
   213  		}
   214  	}
   215  	if headers[0] == nil {
   216  		return nil, fmt.Errorf("no headers found for given origin number %v, hash %v", req.Origin.Number, req.Origin.Hash)
   217  	}
   218  	if req.Reverse {
   219  		for i := 1; i < int(req.Amount); i++ {
   220  			blockNumber -= (1 - req.Skip)
   221  			headers[i] = c.blocks[blockNumber].Header()
   222  		}
   223  		return headers, nil
   224  	}
   225  	for i := 1; i < int(req.Amount); i++ {
   226  		blockNumber += (1 + req.Skip)
   227  		headers[i] = c.blocks[blockNumber].Header()
   228  	}
   229  	return headers, nil
   230  }
   231  
   232  // Shorten returns a copy chain of a desired height from the imported
   233  func (c *Chain) Shorten(height int) *Chain {
   234  	blocks := make([]*types.Block, height)
   235  	copy(blocks, c.blocks[:height])
   236  
   237  	config := *c.config
   238  	return &Chain{
   239  		blocks: blocks,
   240  		config: &config,
   241  	}
   242  }
   243  
   244  func loadGenesis(genesisFile string) (core.Genesis, error) {
   245  	chainConfig, err := os.ReadFile(genesisFile)
   246  	if err != nil {
   247  		return core.Genesis{}, err
   248  	}
   249  	var gen core.Genesis
   250  	if err := json.Unmarshal(chainConfig, &gen); err != nil {
   251  		return core.Genesis{}, err
   252  	}
   253  	return gen, nil
   254  }
   255  
   256  func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, error) {
   257  	// Load chain.rlp.
   258  	fh, err := os.Open(chainfile)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	defer fh.Close()
   263  	var reader io.Reader = fh
   264  	if strings.HasSuffix(chainfile, ".gz") {
   265  		if reader, err = gzip.NewReader(reader); err != nil {
   266  			return nil, err
   267  		}
   268  	}
   269  	stream := rlp.NewStream(reader, 0)
   270  	var blocks = make([]*types.Block, 1)
   271  	blocks[0] = gblock
   272  	for i := 0; ; i++ {
   273  		var b types.Block
   274  		if err := stream.Decode(&b); err == io.EOF {
   275  			break
   276  		} else if err != nil {
   277  			return nil, fmt.Errorf("at block index %d: %v", i, err)
   278  		}
   279  		if b.NumberU64() != uint64(i+1) {
   280  			return nil, fmt.Errorf("block at index %d has wrong number %d", i, b.NumberU64())
   281  		}
   282  		blocks = append(blocks, &b)
   283  	}
   284  	return blocks, nil
   285  }
   286  
   287  func readState(file string) (map[common.Address]state.DumpAccount, error) {
   288  	f, err := os.ReadFile(file)
   289  	if err != nil {
   290  		return nil, fmt.Errorf("unable to read state: %v", err)
   291  	}
   292  	var dump state.Dump
   293  	if err := json.Unmarshal(f, &dump); err != nil {
   294  		return nil, fmt.Errorf("unable to unmarshal state: %v", err)
   295  	}
   296  
   297  	state := make(map[common.Address]state.DumpAccount)
   298  	for key, acct := range dump.Accounts {
   299  		var addr common.Address
   300  		if err := addr.UnmarshalText([]byte(key)); err != nil {
   301  			return nil, fmt.Errorf("invalid address %q", key)
   302  		}
   303  		state[addr] = acct
   304  	}
   305  	return state, nil
   306  }
   307  
   308  func readAccounts(file string) (map[common.Address]*senderInfo, error) {
   309  	f, err := os.ReadFile(file)
   310  	if err != nil {
   311  		return nil, fmt.Errorf("unable to read state: %v", err)
   312  	}
   313  	type account struct {
   314  		Key hexutil.Bytes `json:"key"`
   315  	}
   316  	keys := make(map[common.Address]account)
   317  	if err := json.Unmarshal(f, &keys); err != nil {
   318  		return nil, fmt.Errorf("unable to unmarshal accounts: %v", err)
   319  	}
   320  	accounts := make(map[common.Address]*senderInfo)
   321  	for addr, acc := range keys {
   322  		pk, err := crypto.HexToECDSA(common.Bytes2Hex(acc.Key))
   323  		if err != nil {
   324  			return nil, fmt.Errorf("unable to read private key for %s: %v", err, addr)
   325  		}
   326  		accounts[addr] = &senderInfo{Key: pk, Nonce: 0}
   327  	}
   328  	return accounts, nil
   329  }