| 
									
										
										
										
											2020-10-07 17:22:44 +02:00
										 |  |  | // Copyright 2020 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/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 15:18:17 +02:00
										 |  |  | package ethtest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"compress/gzip" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core/forkid" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/params" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/rlp" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Chain struct { | 
					
						
							| 
									
										
										
										
											2021-04-23 11:15:42 +02:00
										 |  |  | 	genesis     core.Genesis | 
					
						
							| 
									
										
										
										
											2020-09-23 15:18:17 +02:00
										 |  |  | 	blocks      []*types.Block | 
					
						
							|  |  |  | 	chainConfig *params.ChainConfig | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Len returns the length of the chain. | 
					
						
							|  |  |  | func (c *Chain) Len() int { | 
					
						
							|  |  |  | 	return len(c.blocks) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-25 23:09:11 +02:00
										 |  |  | // TD calculates the total difficulty of the chain at the | 
					
						
							|  |  |  | // chain head. | 
					
						
							|  |  |  | func (c *Chain) TD() *big.Int { | 
					
						
							| 
									
										
										
										
											2020-09-23 15:18:17 +02:00
										 |  |  | 	sum := big.NewInt(0) | 
					
						
							| 
									
										
										
										
											2021-05-25 23:09:11 +02:00
										 |  |  | 	for _, block := range c.blocks[:c.Len()] { | 
					
						
							|  |  |  | 		sum.Add(sum, block.Difficulty()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sum | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TotalDifficultyAt calculates the total difficulty of the chain | 
					
						
							|  |  |  | // at the given block height. | 
					
						
							|  |  |  | func (c *Chain) TotalDifficultyAt(height int) *big.Int { | 
					
						
							|  |  |  | 	sum := big.NewInt(0) | 
					
						
							|  |  |  | 	if height >= c.Len() { | 
					
						
							|  |  |  | 		return sum | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, block := range c.blocks[:height+1] { | 
					
						
							| 
									
										
										
										
											2020-09-23 15:18:17 +02:00
										 |  |  | 		sum.Add(sum, block.Difficulty()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sum | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ForkID gets the fork id of the chain. | 
					
						
							|  |  |  | func (c *Chain) ForkID() forkid.ID { | 
					
						
							|  |  |  | 	return forkid.NewID(c.chainConfig, c.blocks[0].Hash(), uint64(c.Len())) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Shorten returns a copy chain of a desired height from the imported | 
					
						
							|  |  |  | func (c *Chain) Shorten(height int) *Chain { | 
					
						
							|  |  |  | 	blocks := make([]*types.Block, height) | 
					
						
							|  |  |  | 	copy(blocks, c.blocks[:height]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	config := *c.chainConfig | 
					
						
							|  |  |  | 	return &Chain{ | 
					
						
							|  |  |  | 		blocks:      blocks, | 
					
						
							|  |  |  | 		chainConfig: &config, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Head returns the chain head. | 
					
						
							|  |  |  | func (c *Chain) Head() *types.Block { | 
					
						
							|  |  |  | 	return c.blocks[c.Len()-1] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-07 17:22:44 +02:00
										 |  |  | func (c *Chain) GetHeaders(req GetBlockHeaders) (BlockHeaders, error) { | 
					
						
							|  |  |  | 	if req.Amount < 1 { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("no block headers requested") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	headers := make(BlockHeaders, req.Amount) | 
					
						
							|  |  |  | 	var blockNumber uint64 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// range over blocks to check if our chain has the requested header | 
					
						
							|  |  |  | 	for _, block := range c.blocks { | 
					
						
							|  |  |  | 		if block.Hash() == req.Origin.Hash || block.Number().Uint64() == req.Origin.Number { | 
					
						
							|  |  |  | 			headers[0] = block.Header() | 
					
						
							|  |  |  | 			blockNumber = block.Number().Uint64() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if headers[0] == nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("no headers found for given origin number %v, hash %v", req.Origin.Number, req.Origin.Hash) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if req.Reverse { | 
					
						
							|  |  |  | 		for i := 1; i < int(req.Amount); i++ { | 
					
						
							|  |  |  | 			blockNumber -= (1 - req.Skip) | 
					
						
							|  |  |  | 			headers[i] = c.blocks[blockNumber].Header() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return headers, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := 1; i < int(req.Amount); i++ { | 
					
						
							|  |  |  | 		blockNumber += (1 + req.Skip) | 
					
						
							|  |  |  | 		headers[i] = c.blocks[blockNumber].Header() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return headers, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 15:18:17 +02:00
										 |  |  | // loadChain takes the given chain.rlp file, and decodes and returns | 
					
						
							|  |  |  | // the blocks from the file. | 
					
						
							|  |  |  | func loadChain(chainfile string, genesis string) (*Chain, error) { | 
					
						
							| 
									
										
										
										
											2021-04-23 11:15:42 +02:00
										 |  |  | 	gen, err := loadGenesis(genesis) | 
					
						
							| 
									
										
										
										
											2020-10-23 13:34:44 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-23 11:15:42 +02:00
										 |  |  | 	gblock := gen.ToBlock(nil) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	blocks, err := blocksFromFile(chainfile, gblock) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	c := &Chain{genesis: gen, blocks: blocks, chainConfig: gen.Config} | 
					
						
							|  |  |  | 	return c, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func loadGenesis(genesisFile string) (core.Genesis, error) { | 
					
						
							|  |  |  | 	chainConfig, err := ioutil.ReadFile(genesisFile) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return core.Genesis{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-23 13:34:44 +02:00
										 |  |  | 	var gen core.Genesis | 
					
						
							|  |  |  | 	if err := json.Unmarshal(chainConfig, &gen); err != nil { | 
					
						
							| 
									
										
										
										
											2021-04-23 11:15:42 +02:00
										 |  |  | 		return core.Genesis{}, err | 
					
						
							| 
									
										
										
										
											2020-10-23 13:34:44 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-23 11:15:42 +02:00
										 |  |  | 	return gen, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-10-23 13:34:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-23 11:15:42 +02:00
										 |  |  | func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, error) { | 
					
						
							| 
									
										
										
										
											2020-10-23 13:34:44 +02:00
										 |  |  | 	// Load chain.rlp. | 
					
						
							| 
									
										
										
										
											2020-09-23 15:18:17 +02:00
										 |  |  | 	fh, err := os.Open(chainfile) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer fh.Close() | 
					
						
							|  |  |  | 	var reader io.Reader = fh | 
					
						
							|  |  |  | 	if strings.HasSuffix(chainfile, ".gz") { | 
					
						
							|  |  |  | 		if reader, err = gzip.NewReader(reader); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	stream := rlp.NewStream(reader, 0) | 
					
						
							| 
									
										
										
										
											2020-10-23 13:34:44 +02:00
										 |  |  | 	var blocks = make([]*types.Block, 1) | 
					
						
							|  |  |  | 	blocks[0] = gblock | 
					
						
							| 
									
										
										
										
											2020-09-23 15:18:17 +02:00
										 |  |  | 	for i := 0; ; i++ { | 
					
						
							|  |  |  | 		var b types.Block | 
					
						
							|  |  |  | 		if err := stream.Decode(&b); err == io.EOF { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} else if err != nil { | 
					
						
							| 
									
										
										
										
											2020-10-23 13:34:44 +02:00
										 |  |  | 			return nil, fmt.Errorf("at block index %d: %v", i, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if b.NumberU64() != uint64(i+1) { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("block at index %d has wrong number %d", i, b.NumberU64()) | 
					
						
							| 
									
										
										
										
											2020-09-23 15:18:17 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		blocks = append(blocks, &b) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-23 11:15:42 +02:00
										 |  |  | 	return blocks, nil | 
					
						
							| 
									
										
										
										
											2020-09-23 15:18:17 +02:00
										 |  |  | } |