github.com/DxChainNetwork/dxc@v0.8.1-0.20220824085222-1162e304b6e7/cmd/devp2p/internal/ethtest/chain.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser 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  // The go-ethereum library 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 Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package ethtest
    18  
    19  import (
    20  	"compress/gzip"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  	"io/ioutil"
    25  	"math/big"
    26  	"os"
    27  	"strings"
    28  
    29  	"github.com/DxChainNetwork/dxc/core"
    30  	"github.com/DxChainNetwork/dxc/core/forkid"
    31  	"github.com/DxChainNetwork/dxc/core/types"
    32  	"github.com/DxChainNetwork/dxc/params"
    33  	"github.com/DxChainNetwork/dxc/rlp"
    34  )
    35  
    36  type Chain struct {
    37  	genesis     core.Genesis
    38  	blocks      []*types.Block
    39  	chainConfig *params.ChainConfig
    40  }
    41  
    42  func (c *Chain) WriteTo(writer io.Writer) error {
    43  	for _, block := range c.blocks {
    44  		if err := rlp.Encode(writer, block); err != nil {
    45  			return err
    46  		}
    47  	}
    48  
    49  	return nil
    50  }
    51  
    52  // Len returns the length of the chain.
    53  func (c *Chain) Len() int {
    54  	return len(c.blocks)
    55  }
    56  
    57  // TD calculates the total difficulty of the chain at the
    58  // chain head.
    59  func (c *Chain) TD() *big.Int {
    60  	sum := big.NewInt(0)
    61  	for _, block := range c.blocks[:c.Len()] {
    62  		sum.Add(sum, block.Difficulty())
    63  	}
    64  	return sum
    65  }
    66  
    67  // TotalDifficultyAt calculates the total difficulty of the chain
    68  // at the given block height.
    69  func (c *Chain) TotalDifficultyAt(height int) *big.Int {
    70  	sum := big.NewInt(0)
    71  	if height >= c.Len() {
    72  		return sum
    73  	}
    74  	for _, block := range c.blocks[:height+1] {
    75  		sum.Add(sum, block.Difficulty())
    76  	}
    77  	return sum
    78  }
    79  
    80  // ForkID gets the fork id of the chain.
    81  func (c *Chain) ForkID() forkid.ID {
    82  	return forkid.NewID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len()))
    83  }
    84  
    85  // Shorten returns a copy chain of a desired height from the imported
    86  func (c *Chain) Shorten(height int) *Chain {
    87  	blocks := make([]*types.Block, height)
    88  	copy(blocks, c.blocks[:height])
    89  
    90  	config := *c.chainConfig
    91  	return &Chain{
    92  		blocks:      blocks,
    93  		chainConfig: &config,
    94  	}
    95  }
    96  
    97  // Head returns the chain head.
    98  func (c *Chain) Head() *types.Block {
    99  	return c.blocks[c.Len()-1]
   100  }
   101  
   102  func (c *Chain) GetHeaders(req GetBlockHeaders) (BlockHeaders, error) {
   103  	if req.Amount < 1 {
   104  		return nil, fmt.Errorf("no block headers requested")
   105  	}
   106  
   107  	headers := make(BlockHeaders, req.Amount)
   108  	var blockNumber uint64
   109  
   110  	// range over blocks to check if our chain has the requested header
   111  	for _, block := range c.blocks {
   112  		if block.Hash() == req.Origin.Hash || block.Number().Uint64() == req.Origin.Number {
   113  			headers[0] = block.Header()
   114  			blockNumber = block.Number().Uint64()
   115  		}
   116  	}
   117  	if headers[0] == nil {
   118  		return nil, fmt.Errorf("no headers found for given origin number %v, hash %v", req.Origin.Number, req.Origin.Hash)
   119  	}
   120  
   121  	if req.Reverse {
   122  		for i := 1; i < int(req.Amount); i++ {
   123  			blockNumber -= (1 - req.Skip)
   124  			headers[i] = c.blocks[blockNumber].Header()
   125  
   126  		}
   127  
   128  		return headers, nil
   129  	}
   130  
   131  	for i := 1; i < int(req.Amount); i++ {
   132  		blockNumber += (1 + req.Skip)
   133  		headers[i] = c.blocks[blockNumber].Header()
   134  	}
   135  
   136  	return headers, nil
   137  }
   138  
   139  // loadChain takes the given chain.rlp file, and decodes and returns
   140  // the blocks from the file.
   141  func loadChain(chainfile string, genesis string) (*Chain, error) {
   142  	gen, err := loadGenesis(genesis)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	gblock := gen.ToBlock(nil)
   147  
   148  	blocks, err := blocksFromFile(chainfile, gblock)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	c := &Chain{genesis: gen, blocks: blocks, chainConfig: gen.Config}
   154  	return c, nil
   155  }
   156  
   157  func loadGenesis(genesisFile string) (core.Genesis, error) {
   158  	chainConfig, err := ioutil.ReadFile(genesisFile)
   159  	if err != nil {
   160  		return core.Genesis{}, err
   161  	}
   162  	var gen core.Genesis
   163  	if err := json.Unmarshal(chainConfig, &gen); err != nil {
   164  		return core.Genesis{}, err
   165  	}
   166  	return gen, nil
   167  }
   168  
   169  func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, error) {
   170  	// Load chain.rlp.
   171  	fh, err := os.Open(chainfile)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	defer fh.Close()
   176  	var reader io.Reader = fh
   177  	if strings.HasSuffix(chainfile, ".gz") {
   178  		if reader, err = gzip.NewReader(reader); err != nil {
   179  			return nil, err
   180  		}
   181  	}
   182  	stream := rlp.NewStream(reader, 0)
   183  	var blocks = make([]*types.Block, 1)
   184  	blocks[0] = gblock
   185  	for i := 0; ; i++ {
   186  		var b types.Block
   187  		if err := stream.Decode(&b); err == io.EOF {
   188  			break
   189  		} else if err != nil {
   190  			return nil, fmt.Errorf("at block index %d: %v", i, err)
   191  		}
   192  		if b.NumberU64() != uint64(i+1) {
   193  			return nil, fmt.Errorf("block at index %d has wrong number %d", i, b.NumberU64())
   194  		}
   195  		blocks = append(blocks, &b)
   196  	}
   197  	return blocks, nil
   198  }