github.com/core-coin/go-core/v2@v2.1.9/cmd/devp2p/internal/xcbtest/chain.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of go-core.
     3  //
     4  // go-core 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-core 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-core. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package xcbtest
    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/core-coin/go-core/v2/core"
    30  	"github.com/core-coin/go-core/v2/core/forkid"
    31  	"github.com/core-coin/go-core/v2/core/types"
    32  	"github.com/core-coin/go-core/v2/params"
    33  	"github.com/core-coin/go-core/v2/rlp"
    34  )
    35  
    36  type Chain struct {
    37  	blocks      []*types.Block
    38  	chainConfig *params.ChainConfig
    39  }
    40  
    41  func (c *Chain) WriteTo(writer io.Writer) error {
    42  	for _, block := range c.blocks {
    43  		if err := rlp.Encode(writer, block); err != nil {
    44  			return err
    45  		}
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  // Len returns the length of the chain.
    52  func (c *Chain) Len() int {
    53  	return len(c.blocks)
    54  }
    55  
    56  // TD calculates the total difficulty of the chain.
    57  func (c *Chain) TD(height int) *big.Int { // TODO later on channge scheme so that the height is included in range
    58  	sum := big.NewInt(0)
    59  	for _, block := range c.blocks[:height] {
    60  		sum.Add(sum, block.Difficulty())
    61  	}
    62  	return sum
    63  }
    64  
    65  // ForkID gets the fork id of the chain.
    66  func (c *Chain) ForkID() forkid.ID {
    67  	return forkid.NewID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len()))
    68  }
    69  
    70  // Shorten returns a copy chain of a desired height from the imported
    71  func (c *Chain) Shorten(height int) *Chain {
    72  	blocks := make([]*types.Block, height)
    73  	copy(blocks, c.blocks[:height])
    74  
    75  	config := *c.chainConfig
    76  	return &Chain{
    77  		blocks:      blocks,
    78  		chainConfig: &config,
    79  	}
    80  }
    81  
    82  // Head returns the chain head.
    83  func (c *Chain) Head() *types.Block {
    84  	return c.blocks[c.Len()-1]
    85  }
    86  
    87  func (c *Chain) GetHeaders(req GetBlockHeaders) (BlockHeaders, error) {
    88  	if req.Amount < 1 {
    89  		return nil, fmt.Errorf("no block headers requested")
    90  	}
    91  
    92  	headers := make(BlockHeaders, req.Amount)
    93  	var blockNumber uint64
    94  
    95  	// range over blocks to check if our chain has the requested header
    96  	for _, block := range c.blocks {
    97  		if block.Hash() == req.Origin.Hash || block.Number().Uint64() == req.Origin.Number {
    98  			headers[0] = block.Header()
    99  			blockNumber = block.Number().Uint64()
   100  		}
   101  	}
   102  	if headers[0] == nil {
   103  		return nil, fmt.Errorf("no headers found for given origin number %v, hash %v", req.Origin.Number, req.Origin.Hash)
   104  	}
   105  
   106  	if req.Reverse {
   107  		for i := 1; i < int(req.Amount); i++ {
   108  			blockNumber -= (1 - req.Skip)
   109  			headers[i] = c.blocks[blockNumber].Header()
   110  
   111  		}
   112  
   113  		return headers, nil
   114  	}
   115  
   116  	for i := 1; i < int(req.Amount); i++ {
   117  		blockNumber += (1 + req.Skip)
   118  		headers[i] = c.blocks[blockNumber].Header()
   119  	}
   120  
   121  	return headers, nil
   122  }
   123  
   124  // loadChain takes the given chain.rlp file, and decodes and returns
   125  // the blocks from the file.
   126  func loadChain(chainfile string, genesis string) (*Chain, error) {
   127  	chainConfig, err := ioutil.ReadFile(genesis)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	var gen core.Genesis
   132  	if err := json.Unmarshal(chainConfig, &gen); err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	// Load chain.rlp.
   137  	fh, err := os.Open(chainfile)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	defer fh.Close()
   142  	var reader io.Reader = fh
   143  	if strings.HasSuffix(chainfile, ".gz") {
   144  		if reader, err = gzip.NewReader(reader); err != nil {
   145  			return nil, err
   146  		}
   147  	}
   148  	stream := rlp.NewStream(reader, 0)
   149  	var blocks = make([]*types.Block, 0)
   150  	for i := 0; ; i++ {
   151  		var b types.Block
   152  		if err := stream.Decode(&b); err == io.EOF {
   153  			break
   154  		} else if err != nil {
   155  			return nil, fmt.Errorf("at block index %d: %v", i, err)
   156  		}
   157  		if b.NumberU64() != uint64(i) {
   158  			return nil, fmt.Errorf("block at index %d has wrong number %d", i, b.NumberU64())
   159  		}
   160  		blocks = append(blocks, &b)
   161  	}
   162  
   163  	c := &Chain{blocks: blocks, chainConfig: gen.Config}
   164  	return c, nil
   165  }