| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | // Copyright 2019 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of go-ethereum. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // go-ethereum is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // go-ethereum 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 General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package main | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/signal" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/cmd/utils" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common/hexutil" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common/math" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/consensus" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/consensus/ethash" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/consensus/misc" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core/rawdb" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core/state" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core/vm" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/ethdb" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/node" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/params" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/rlp" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/rpc" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/trie" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cli "gopkg.in/urfave/cli.v1" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	rpcPortFlag = cli.IntFlag{ | 
					
						
							|  |  |  | 		Name:  "rpcport", | 
					
						
							|  |  |  | 		Usage: "HTTP-RPC server listening port", | 
					
						
							|  |  |  | 		Value: node.DefaultHTTPPort, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	retestethCommand = cli.Command{ | 
					
						
							|  |  |  | 		Action:      utils.MigrateFlags(retesteth), | 
					
						
							|  |  |  | 		Name:        "retesteth", | 
					
						
							|  |  |  | 		Usage:       "Launches geth in retesteth mode", | 
					
						
							|  |  |  | 		ArgsUsage:   "", | 
					
						
							|  |  |  | 		Flags:       []cli.Flag{rpcPortFlag}, | 
					
						
							|  |  |  | 		Category:    "MISCELLANEOUS COMMANDS", | 
					
						
							|  |  |  | 		Description: `Launches geth in retesteth mode (no database, no network, only retesteth RPC interface)`, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type RetestethTestAPI interface { | 
					
						
							|  |  |  | 	SetChainParams(ctx context.Context, chainParams ChainParams) (bool, error) | 
					
						
							|  |  |  | 	MineBlocks(ctx context.Context, number uint64) (bool, error) | 
					
						
							|  |  |  | 	ModifyTimestamp(ctx context.Context, interval uint64) (bool, error) | 
					
						
							|  |  |  | 	ImportRawBlock(ctx context.Context, rawBlock hexutil.Bytes) (common.Hash, error) | 
					
						
							|  |  |  | 	RewindToBlock(ctx context.Context, number uint64) (bool, error) | 
					
						
							|  |  |  | 	GetLogHash(ctx context.Context, txHash common.Hash) (common.Hash, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type RetestethEthAPI interface { | 
					
						
							|  |  |  | 	SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error) | 
					
						
							|  |  |  | 	BlockNumber(ctx context.Context) (uint64, error) | 
					
						
							|  |  |  | 	GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error) | 
					
						
							| 
									
										
										
										
											2020-02-11 10:54:05 +01:00
										 |  |  | 	GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error) | 
					
						
							|  |  |  | 	GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error) | 
					
						
							|  |  |  | 	GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type RetestethDebugAPI interface { | 
					
						
							| 
									
										
										
										
											2019-07-13 07:48:55 -06:00
										 |  |  | 	AccountRange(ctx context.Context, | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 		blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, | 
					
						
							|  |  |  | 		addressHash *math.HexOrDecimal256, maxResults uint64, | 
					
						
							|  |  |  | 	) (AccountRangeResult, error) | 
					
						
							|  |  |  | 	StorageRangeAt(ctx context.Context, | 
					
						
							|  |  |  | 		blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, | 
					
						
							|  |  |  | 		address common.Address, | 
					
						
							|  |  |  | 		begin *math.HexOrDecimal256, maxResults uint64, | 
					
						
							|  |  |  | 	) (StorageRangeResult, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type RetestWeb3API interface { | 
					
						
							|  |  |  | 	ClientVersion(ctx context.Context) (string, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type RetestethAPI struct { | 
					
						
							|  |  |  | 	ethDb         ethdb.Database | 
					
						
							|  |  |  | 	db            state.Database | 
					
						
							|  |  |  | 	chainConfig   *params.ChainConfig | 
					
						
							|  |  |  | 	author        common.Address | 
					
						
							|  |  |  | 	extraData     []byte | 
					
						
							|  |  |  | 	genesisHash   common.Hash | 
					
						
							|  |  |  | 	engine        *NoRewardEngine | 
					
						
							|  |  |  | 	blockchain    *core.BlockChain | 
					
						
							|  |  |  | 	txMap         map[common.Address]map[uint64]*types.Transaction // Sender -> Nonce -> Transaction | 
					
						
							|  |  |  | 	txSenders     map[common.Address]struct{}                      // Set of transaction senders | 
					
						
							|  |  |  | 	blockInterval uint64 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ChainParams struct { | 
					
						
							|  |  |  | 	SealEngine string                            `json:"sealEngine"` | 
					
						
							|  |  |  | 	Params     CParamsParams                     `json:"params"` | 
					
						
							|  |  |  | 	Genesis    CParamsGenesis                    `json:"genesis"` | 
					
						
							|  |  |  | 	Accounts   map[common.Address]CParamsAccount `json:"accounts"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type CParamsParams struct { | 
					
						
							|  |  |  | 	AccountStartNonce          math.HexOrDecimal64   `json:"accountStartNonce"` | 
					
						
							|  |  |  | 	HomesteadForkBlock         *math.HexOrDecimal64  `json:"homesteadForkBlock"` | 
					
						
							|  |  |  | 	EIP150ForkBlock            *math.HexOrDecimal64  `json:"EIP150ForkBlock"` | 
					
						
							|  |  |  | 	EIP158ForkBlock            *math.HexOrDecimal64  `json:"EIP158ForkBlock"` | 
					
						
							|  |  |  | 	DaoHardforkBlock           *math.HexOrDecimal64  `json:"daoHardforkBlock"` | 
					
						
							|  |  |  | 	ByzantiumForkBlock         *math.HexOrDecimal64  `json:"byzantiumForkBlock"` | 
					
						
							|  |  |  | 	ConstantinopleForkBlock    *math.HexOrDecimal64  `json:"constantinopleForkBlock"` | 
					
						
							|  |  |  | 	ConstantinopleFixForkBlock *math.HexOrDecimal64  `json:"constantinopleFixForkBlock"` | 
					
						
							| 
									
										
										
										
											2019-08-23 09:48:40 +02:00
										 |  |  | 	IstanbulBlock              *math.HexOrDecimal64  `json:"istanbulForkBlock"` | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	ChainID                    *math.HexOrDecimal256 `json:"chainID"` | 
					
						
							|  |  |  | 	MaximumExtraDataSize       math.HexOrDecimal64   `json:"maximumExtraDataSize"` | 
					
						
							|  |  |  | 	TieBreakingGas             bool                  `json:"tieBreakingGas"` | 
					
						
							|  |  |  | 	MinGasLimit                math.HexOrDecimal64   `json:"minGasLimit"` | 
					
						
							|  |  |  | 	MaxGasLimit                math.HexOrDecimal64   `json:"maxGasLimit"` | 
					
						
							|  |  |  | 	GasLimitBoundDivisor       math.HexOrDecimal64   `json:"gasLimitBoundDivisor"` | 
					
						
							|  |  |  | 	MinimumDifficulty          math.HexOrDecimal256  `json:"minimumDifficulty"` | 
					
						
							|  |  |  | 	DifficultyBoundDivisor     math.HexOrDecimal256  `json:"difficultyBoundDivisor"` | 
					
						
							|  |  |  | 	DurationLimit              math.HexOrDecimal256  `json:"durationLimit"` | 
					
						
							|  |  |  | 	BlockReward                math.HexOrDecimal256  `json:"blockReward"` | 
					
						
							|  |  |  | 	NetworkID                  math.HexOrDecimal256  `json:"networkID"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type CParamsGenesis struct { | 
					
						
							|  |  |  | 	Nonce      math.HexOrDecimal64   `json:"nonce"` | 
					
						
							|  |  |  | 	Difficulty *math.HexOrDecimal256 `json:"difficulty"` | 
					
						
							|  |  |  | 	MixHash    *math.HexOrDecimal256 `json:"mixHash"` | 
					
						
							|  |  |  | 	Author     common.Address        `json:"author"` | 
					
						
							|  |  |  | 	Timestamp  math.HexOrDecimal64   `json:"timestamp"` | 
					
						
							|  |  |  | 	ParentHash common.Hash           `json:"parentHash"` | 
					
						
							|  |  |  | 	ExtraData  hexutil.Bytes         `json:"extraData"` | 
					
						
							|  |  |  | 	GasLimit   math.HexOrDecimal64   `json:"gasLimit"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type CParamsAccount struct { | 
					
						
							|  |  |  | 	Balance     *math.HexOrDecimal256 `json:"balance"` | 
					
						
							|  |  |  | 	Precompiled *CPAccountPrecompiled `json:"precompiled"` | 
					
						
							|  |  |  | 	Code        hexutil.Bytes         `json:"code"` | 
					
						
							|  |  |  | 	Storage     map[string]string     `json:"storage"` | 
					
						
							|  |  |  | 	Nonce       *math.HexOrDecimal64  `json:"nonce"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type CPAccountPrecompiled struct { | 
					
						
							|  |  |  | 	Name          string                `json:"name"` | 
					
						
							|  |  |  | 	StartingBlock math.HexOrDecimal64   `json:"startingBlock"` | 
					
						
							|  |  |  | 	Linear        *CPAPrecompiledLinear `json:"linear"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type CPAPrecompiledLinear struct { | 
					
						
							|  |  |  | 	Base uint64 `json:"base"` | 
					
						
							|  |  |  | 	Word uint64 `json:"word"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type AccountRangeResult struct { | 
					
						
							|  |  |  | 	AddressMap map[common.Hash]common.Address `json:"addressMap"` | 
					
						
							|  |  |  | 	NextKey    common.Hash                    `json:"nextKey"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type StorageRangeResult struct { | 
					
						
							|  |  |  | 	Complete bool                   `json:"complete"` | 
					
						
							|  |  |  | 	Storage  map[common.Hash]SRItem `json:"storage"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type SRItem struct { | 
					
						
							|  |  |  | 	Key   string `json:"key"` | 
					
						
							|  |  |  | 	Value string `json:"value"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type NoRewardEngine struct { | 
					
						
							|  |  |  | 	inner     consensus.Engine | 
					
						
							|  |  |  | 	rewardsOn bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) Author(header *types.Header) (common.Address, error) { | 
					
						
							|  |  |  | 	return e.inner.Author(header) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { | 
					
						
							|  |  |  | 	return e.inner.VerifyHeader(chain, header, seal) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { | 
					
						
							|  |  |  | 	return e.inner.VerifyHeaders(chain, headers, seals) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { | 
					
						
							|  |  |  | 	return e.inner.VerifyUncles(chain, block) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) VerifySeal(chain consensus.ChainReader, header *types.Header) error { | 
					
						
							|  |  |  | 	return e.inner.VerifySeal(chain, header) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) Prepare(chain consensus.ChainReader, header *types.Header) error { | 
					
						
							|  |  |  | 	return e.inner.Prepare(chain, header) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { | 
					
						
							|  |  |  | 	// Simply touch miner and uncle coinbase accounts | 
					
						
							|  |  |  | 	reward := big.NewInt(0) | 
					
						
							|  |  |  | 	for _, uncle := range uncles { | 
					
						
							|  |  |  | 		state.AddBalance(uncle.Coinbase, reward) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	state.AddBalance(header.Coinbase, reward) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) Finalize(chain consensus.ChainReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction, | 
					
						
							|  |  |  | 	uncles []*types.Header) { | 
					
						
							|  |  |  | 	if e.rewardsOn { | 
					
						
							|  |  |  | 		e.inner.Finalize(chain, header, statedb, txs, uncles) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		e.accumulateRewards(chain.Config(), statedb, header, uncles) | 
					
						
							|  |  |  | 		header.Root = statedb.IntermediateRoot(chain.Config().IsEIP158(header.Number)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction, | 
					
						
							|  |  |  | 	uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { | 
					
						
							|  |  |  | 	if e.rewardsOn { | 
					
						
							|  |  |  | 		return e.inner.FinalizeAndAssemble(chain, header, statedb, txs, uncles, receipts) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		e.accumulateRewards(chain.Config(), statedb, header, uncles) | 
					
						
							|  |  |  | 		header.Root = statedb.IntermediateRoot(chain.Config().IsEIP158(header.Number)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Header seems complete, assemble into a block and return | 
					
						
							|  |  |  | 		return types.NewBlock(header, txs, uncles, receipts), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { | 
					
						
							|  |  |  | 	return e.inner.Seal(chain, block, results, stop) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) SealHash(header *types.Header) common.Hash { | 
					
						
							|  |  |  | 	return e.inner.SealHash(header) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { | 
					
						
							|  |  |  | 	return e.inner.CalcDifficulty(chain, time, parent) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) APIs(chain consensus.ChainReader) []rpc.API { | 
					
						
							|  |  |  | 	return e.inner.APIs(chain) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (e *NoRewardEngine) Close() error { | 
					
						
							|  |  |  | 	return e.inner.Close() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainParams) (bool, error) { | 
					
						
							|  |  |  | 	// Clean up | 
					
						
							|  |  |  | 	if api.blockchain != nil { | 
					
						
							|  |  |  | 		api.blockchain.Stop() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if api.engine != nil { | 
					
						
							|  |  |  | 		api.engine.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if api.ethDb != nil { | 
					
						
							|  |  |  | 		api.ethDb.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ethDb := rawdb.NewMemoryDatabase() | 
					
						
							|  |  |  | 	accounts := make(core.GenesisAlloc) | 
					
						
							|  |  |  | 	for address, account := range chainParams.Accounts { | 
					
						
							|  |  |  | 		balance := big.NewInt(0) | 
					
						
							|  |  |  | 		if account.Balance != nil { | 
					
						
							|  |  |  | 			balance.Set((*big.Int)(account.Balance)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var nonce uint64 | 
					
						
							|  |  |  | 		if account.Nonce != nil { | 
					
						
							|  |  |  | 			nonce = uint64(*account.Nonce) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if account.Precompiled == nil || account.Balance != nil { | 
					
						
							|  |  |  | 			storage := make(map[common.Hash]common.Hash) | 
					
						
							|  |  |  | 			for k, v := range account.Storage { | 
					
						
							|  |  |  | 				storage[common.HexToHash(k)] = common.HexToHash(v) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			accounts[address] = core.GenesisAccount{ | 
					
						
							|  |  |  | 				Balance: balance, | 
					
						
							|  |  |  | 				Code:    account.Code, | 
					
						
							|  |  |  | 				Nonce:   nonce, | 
					
						
							|  |  |  | 				Storage: storage, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	chainId := big.NewInt(1) | 
					
						
							|  |  |  | 	if chainParams.Params.ChainID != nil { | 
					
						
							|  |  |  | 		chainId.Set((*big.Int)(chainParams.Params.ChainID)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		homesteadBlock      *big.Int | 
					
						
							|  |  |  | 		daoForkBlock        *big.Int | 
					
						
							|  |  |  | 		eip150Block         *big.Int | 
					
						
							|  |  |  | 		eip155Block         *big.Int | 
					
						
							|  |  |  | 		eip158Block         *big.Int | 
					
						
							|  |  |  | 		byzantiumBlock      *big.Int | 
					
						
							|  |  |  | 		constantinopleBlock *big.Int | 
					
						
							|  |  |  | 		petersburgBlock     *big.Int | 
					
						
							| 
									
										
										
										
											2019-08-23 09:48:40 +02:00
										 |  |  | 		istanbulBlock       *big.Int | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	if chainParams.Params.HomesteadForkBlock != nil { | 
					
						
							|  |  |  | 		homesteadBlock = big.NewInt(int64(*chainParams.Params.HomesteadForkBlock)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if chainParams.Params.DaoHardforkBlock != nil { | 
					
						
							|  |  |  | 		daoForkBlock = big.NewInt(int64(*chainParams.Params.DaoHardforkBlock)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if chainParams.Params.EIP150ForkBlock != nil { | 
					
						
							|  |  |  | 		eip150Block = big.NewInt(int64(*chainParams.Params.EIP150ForkBlock)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if chainParams.Params.EIP158ForkBlock != nil { | 
					
						
							|  |  |  | 		eip158Block = big.NewInt(int64(*chainParams.Params.EIP158ForkBlock)) | 
					
						
							|  |  |  | 		eip155Block = eip158Block | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if chainParams.Params.ByzantiumForkBlock != nil { | 
					
						
							|  |  |  | 		byzantiumBlock = big.NewInt(int64(*chainParams.Params.ByzantiumForkBlock)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if chainParams.Params.ConstantinopleForkBlock != nil { | 
					
						
							|  |  |  | 		constantinopleBlock = big.NewInt(int64(*chainParams.Params.ConstantinopleForkBlock)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if chainParams.Params.ConstantinopleFixForkBlock != nil { | 
					
						
							|  |  |  | 		petersburgBlock = big.NewInt(int64(*chainParams.Params.ConstantinopleFixForkBlock)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if constantinopleBlock != nil && petersburgBlock == nil { | 
					
						
							|  |  |  | 		petersburgBlock = big.NewInt(100000000000) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-23 09:48:40 +02:00
										 |  |  | 	if chainParams.Params.IstanbulBlock != nil { | 
					
						
							|  |  |  | 		istanbulBlock = big.NewInt(int64(*chainParams.Params.IstanbulBlock)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	genesis := &core.Genesis{ | 
					
						
							|  |  |  | 		Config: ¶ms.ChainConfig{ | 
					
						
							|  |  |  | 			ChainID:             chainId, | 
					
						
							|  |  |  | 			HomesteadBlock:      homesteadBlock, | 
					
						
							|  |  |  | 			DAOForkBlock:        daoForkBlock, | 
					
						
							| 
									
										
										
										
											2020-01-31 12:00:37 +01:00
										 |  |  | 			DAOForkSupport:      true, | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 			EIP150Block:         eip150Block, | 
					
						
							|  |  |  | 			EIP155Block:         eip155Block, | 
					
						
							|  |  |  | 			EIP158Block:         eip158Block, | 
					
						
							|  |  |  | 			ByzantiumBlock:      byzantiumBlock, | 
					
						
							|  |  |  | 			ConstantinopleBlock: constantinopleBlock, | 
					
						
							|  |  |  | 			PetersburgBlock:     petersburgBlock, | 
					
						
							| 
									
										
										
										
											2019-08-23 09:48:40 +02:00
										 |  |  | 			IstanbulBlock:       istanbulBlock, | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		Nonce:      uint64(chainParams.Genesis.Nonce), | 
					
						
							|  |  |  | 		Timestamp:  uint64(chainParams.Genesis.Timestamp), | 
					
						
							|  |  |  | 		ExtraData:  chainParams.Genesis.ExtraData, | 
					
						
							|  |  |  | 		GasLimit:   uint64(chainParams.Genesis.GasLimit), | 
					
						
							|  |  |  | 		Difficulty: big.NewInt(0).Set((*big.Int)(chainParams.Genesis.Difficulty)), | 
					
						
							|  |  |  | 		Mixhash:    common.BigToHash((*big.Int)(chainParams.Genesis.MixHash)), | 
					
						
							|  |  |  | 		Coinbase:   chainParams.Genesis.Author, | 
					
						
							|  |  |  | 		ParentHash: chainParams.Genesis.ParentHash, | 
					
						
							|  |  |  | 		Alloc:      accounts, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	chainConfig, genesisHash, err := core.SetupGenesisBlock(ethDb, genesis) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fmt.Printf("Chain config: %v\n", chainConfig) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var inner consensus.Engine | 
					
						
							|  |  |  | 	switch chainParams.SealEngine { | 
					
						
							|  |  |  | 	case "NoProof", "NoReward": | 
					
						
							|  |  |  | 		inner = ethash.NewFaker() | 
					
						
							|  |  |  | 	case "Ethash": | 
					
						
							|  |  |  | 		inner = ethash.New(ethash.Config{ | 
					
						
							| 
									
										
										
										
											2020-03-31 16:44:04 +08:00
										 |  |  | 			CacheDir:         "ethash", | 
					
						
							|  |  |  | 			CachesInMem:      2, | 
					
						
							|  |  |  | 			CachesOnDisk:     3, | 
					
						
							|  |  |  | 			CachesLockMmap:   false, | 
					
						
							|  |  |  | 			DatasetsInMem:    1, | 
					
						
							|  |  |  | 			DatasetsOnDisk:   2, | 
					
						
							|  |  |  | 			DatasetsLockMmap: false, | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 		}, nil, false) | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return false, fmt.Errorf("unrecognised seal engine: %s", chainParams.SealEngine) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	engine := &NoRewardEngine{inner: inner, rewardsOn: chainParams.SealEngine != "NoReward"} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	blockchain, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	api.chainConfig = chainConfig | 
					
						
							|  |  |  | 	api.genesisHash = genesisHash | 
					
						
							|  |  |  | 	api.author = chainParams.Genesis.Author | 
					
						
							|  |  |  | 	api.extraData = chainParams.Genesis.ExtraData | 
					
						
							|  |  |  | 	api.ethDb = ethDb | 
					
						
							|  |  |  | 	api.engine = engine | 
					
						
							|  |  |  | 	api.blockchain = blockchain | 
					
						
							|  |  |  | 	api.db = state.NewDatabase(api.ethDb) | 
					
						
							|  |  |  | 	api.txMap = make(map[common.Address]map[uint64]*types.Transaction) | 
					
						
							|  |  |  | 	api.txSenders = make(map[common.Address]struct{}) | 
					
						
							|  |  |  | 	api.blockInterval = 0 | 
					
						
							|  |  |  | 	return true, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error) { | 
					
						
							|  |  |  | 	tx := new(types.Transaction) | 
					
						
							|  |  |  | 	if err := rlp.DecodeBytes(rawTx, tx); err != nil { | 
					
						
							|  |  |  | 		// Return nil is not by mistake - some tests include sending transaction where gasLimit overflows uint64 | 
					
						
							|  |  |  | 		return common.Hash{}, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-23 20:55:56 +01:00
										 |  |  | 	signer := types.MakeSigner(api.chainConfig, big.NewInt(int64(api.currentNumber()))) | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	sender, err := types.Sender(signer, tx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return common.Hash{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if nonceMap, ok := api.txMap[sender]; ok { | 
					
						
							|  |  |  | 		nonceMap[tx.Nonce()] = tx | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		nonceMap = make(map[uint64]*types.Transaction) | 
					
						
							|  |  |  | 		nonceMap[tx.Nonce()] = tx | 
					
						
							|  |  |  | 		api.txMap[sender] = nonceMap | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	api.txSenders[sender] = struct{}{} | 
					
						
							|  |  |  | 	return tx.Hash(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) MineBlocks(ctx context.Context, number uint64) (bool, error) { | 
					
						
							|  |  |  | 	for i := 0; i < int(number); i++ { | 
					
						
							|  |  |  | 		if err := api.mineBlock(); err != nil { | 
					
						
							|  |  |  | 			return false, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fmt.Printf("Mined %d blocks\n", number) | 
					
						
							|  |  |  | 	return true, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-23 20:55:56 +01:00
										 |  |  | func (api *RetestethAPI) currentNumber() uint64 { | 
					
						
							|  |  |  | 	if current := api.blockchain.CurrentBlock(); current != nil { | 
					
						
							|  |  |  | 		return current.NumberU64() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | func (api *RetestethAPI) mineBlock() error { | 
					
						
							| 
									
										
										
										
											2020-01-23 20:55:56 +01:00
										 |  |  | 	number := api.currentNumber() | 
					
						
							|  |  |  | 	parentHash := rawdb.ReadCanonicalHash(api.ethDb, number) | 
					
						
							|  |  |  | 	parent := rawdb.ReadBlock(api.ethDb, parentHash, number) | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	var timestamp uint64 | 
					
						
							|  |  |  | 	if api.blockInterval == 0 { | 
					
						
							|  |  |  | 		timestamp = uint64(time.Now().Unix()) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		timestamp = parent.Time() + api.blockInterval | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gasLimit := core.CalcGasLimit(parent, 9223372036854775807, 9223372036854775807) | 
					
						
							|  |  |  | 	header := &types.Header{ | 
					
						
							|  |  |  | 		ParentHash: parent.Hash(), | 
					
						
							| 
									
										
										
										
											2020-01-23 20:55:56 +01:00
										 |  |  | 		Number:     big.NewInt(int64(number + 1)), | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 		GasLimit:   gasLimit, | 
					
						
							|  |  |  | 		Extra:      api.extraData, | 
					
						
							|  |  |  | 		Time:       timestamp, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	header.Coinbase = api.author | 
					
						
							|  |  |  | 	if api.engine != nil { | 
					
						
							|  |  |  | 		api.engine.Prepare(api.blockchain, header) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// If we are care about TheDAO hard-fork check whether to override the extra-data or not | 
					
						
							|  |  |  | 	if daoBlock := api.chainConfig.DAOForkBlock; daoBlock != nil { | 
					
						
							|  |  |  | 		// Check whether the block is among the fork extra-override range | 
					
						
							|  |  |  | 		limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) | 
					
						
							|  |  |  | 		if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 { | 
					
						
							|  |  |  | 			// Depending whether we support or oppose the fork, override differently | 
					
						
							|  |  |  | 			if api.chainConfig.DAOForkSupport { | 
					
						
							|  |  |  | 				header.Extra = common.CopyBytes(params.DAOForkBlockExtra) | 
					
						
							|  |  |  | 			} else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { | 
					
						
							|  |  |  | 				header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	statedb, err := api.blockchain.StateAt(parent.Root()) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if api.chainConfig.DAOForkSupport && api.chainConfig.DAOForkBlock != nil && api.chainConfig.DAOForkBlock.Cmp(header.Number) == 0 { | 
					
						
							|  |  |  | 		misc.ApplyDAOHardFork(statedb) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gasPool := new(core.GasPool).AddGas(header.GasLimit) | 
					
						
							|  |  |  | 	txCount := 0 | 
					
						
							|  |  |  | 	var txs []*types.Transaction | 
					
						
							|  |  |  | 	var receipts []*types.Receipt | 
					
						
							|  |  |  | 	var blockFull = gasPool.Gas() < params.TxGas | 
					
						
							|  |  |  | 	for address := range api.txSenders { | 
					
						
							|  |  |  | 		if blockFull { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		m := api.txMap[address] | 
					
						
							|  |  |  | 		for nonce := statedb.GetNonce(address); ; nonce++ { | 
					
						
							|  |  |  | 			if tx, ok := m[nonce]; ok { | 
					
						
							|  |  |  | 				// Try to apply transactions to the state | 
					
						
							|  |  |  | 				statedb.Prepare(tx.Hash(), common.Hash{}, txCount) | 
					
						
							|  |  |  | 				snap := statedb.Snapshot() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 14:22:22 -05:00
										 |  |  | 				receipt, err := core.ApplyTransaction( | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 					api.chainConfig, | 
					
						
							|  |  |  | 					api.blockchain, | 
					
						
							|  |  |  | 					&api.author, | 
					
						
							|  |  |  | 					gasPool, | 
					
						
							|  |  |  | 					statedb, | 
					
						
							|  |  |  | 					header, tx, &header.GasUsed, *api.blockchain.GetVMConfig(), | 
					
						
							|  |  |  | 				) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					statedb.RevertToSnapshot(snap) | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				txs = append(txs, tx) | 
					
						
							|  |  |  | 				receipts = append(receipts, receipt) | 
					
						
							|  |  |  | 				delete(m, nonce) | 
					
						
							|  |  |  | 				if len(m) == 0 { | 
					
						
							|  |  |  | 					// Last tx for the sender | 
					
						
							|  |  |  | 					delete(api.txMap, address) | 
					
						
							|  |  |  | 					delete(api.txSenders, address) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				txCount++ | 
					
						
							|  |  |  | 				if gasPool.Gas() < params.TxGas { | 
					
						
							|  |  |  | 					blockFull = true | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				break // Gap in the nonces | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	block, err := api.engine.FinalizeAndAssemble(api.blockchain, header, statedb, txs, []*types.Header{}, receipts) | 
					
						
							| 
									
										
										
										
											2019-07-22 09:51:13 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	return api.importBlock(block) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) importBlock(block *types.Block) error { | 
					
						
							|  |  |  | 	if _, err := api.blockchain.InsertChain([]*types.Block{block}); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-23 20:55:56 +01:00
										 |  |  | 	fmt.Printf("Imported block %d,  head is %d\n", block.NumberU64(), api.currentNumber()) | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) ModifyTimestamp(ctx context.Context, interval uint64) (bool, error) { | 
					
						
							|  |  |  | 	api.blockInterval = interval | 
					
						
							|  |  |  | 	return true, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) ImportRawBlock(ctx context.Context, rawBlock hexutil.Bytes) (common.Hash, error) { | 
					
						
							|  |  |  | 	block := new(types.Block) | 
					
						
							|  |  |  | 	if err := rlp.DecodeBytes(rawBlock, block); err != nil { | 
					
						
							|  |  |  | 		return common.Hash{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	fmt.Printf("Importing block %d with parent hash: %x, genesisHash: %x\n", block.NumberU64(), block.ParentHash(), api.genesisHash) | 
					
						
							|  |  |  | 	if err := api.importBlock(block); err != nil { | 
					
						
							|  |  |  | 		return common.Hash{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return block.Hash(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) RewindToBlock(ctx context.Context, newHead uint64) (bool, error) { | 
					
						
							|  |  |  | 	if err := api.blockchain.SetHead(newHead); err != nil { | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-31 12:00:37 +01:00
										 |  |  | 	// When we rewind, the transaction pool should be cleaned out. | 
					
						
							|  |  |  | 	api.txMap = make(map[common.Address]map[uint64]*types.Transaction) | 
					
						
							|  |  |  | 	api.txSenders = make(map[common.Address]struct{}) | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	return true, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var emptyListHash common.Hash = common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) GetLogHash(ctx context.Context, txHash common.Hash) (common.Hash, error) { | 
					
						
							|  |  |  | 	receipt, _, _, _ := rawdb.ReadReceipt(api.ethDb, txHash, api.chainConfig) | 
					
						
							|  |  |  | 	if receipt == nil { | 
					
						
							|  |  |  | 		return emptyListHash, nil | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if logListRlp, err := rlp.EncodeToBytes(receipt.Logs); err != nil { | 
					
						
							|  |  |  | 			return common.Hash{}, err | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			return common.BytesToHash(crypto.Keccak256(logListRlp)), nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) BlockNumber(ctx context.Context) (uint64, error) { | 
					
						
							| 
									
										
										
										
											2020-01-23 20:55:56 +01:00
										 |  |  | 	return api.currentNumber(), nil | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error) { | 
					
						
							|  |  |  | 	block := api.blockchain.GetBlockByNumber(uint64(blockNr)) | 
					
						
							|  |  |  | 	if block != nil { | 
					
						
							|  |  |  | 		response, err := RPCMarshalBlock(block, true, fullTx) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		response["author"] = response["miner"] | 
					
						
							|  |  |  | 		response["totalDifficulty"] = (*hexutil.Big)(api.blockchain.GetTd(block.Hash(), uint64(blockNr))) | 
					
						
							|  |  |  | 		return response, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil, fmt.Errorf("block %d not found", blockNr) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-11 10:54:05 +01:00
										 |  |  | func (api *RetestethAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { | 
					
						
							|  |  |  | 	block := api.blockchain.GetBlockByHash(blockHash) | 
					
						
							|  |  |  | 	if block != nil { | 
					
						
							|  |  |  | 		response, err := RPCMarshalBlock(block, true, fullTx) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		response["author"] = response["miner"] | 
					
						
							|  |  |  | 		response["totalDifficulty"] = (*hexutil.Big)(api.blockchain.GetTd(block.Hash(), block.Number().Uint64())) | 
					
						
							|  |  |  | 		return response, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil, fmt.Errorf("block 0x%x not found", blockHash) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-13 07:48:55 -06:00
										 |  |  | func (api *RetestethAPI) AccountRange(ctx context.Context, | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, | 
					
						
							|  |  |  | 	addressHash *math.HexOrDecimal256, maxResults uint64, | 
					
						
							|  |  |  | ) (AccountRangeResult, error) { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		header *types.Header | 
					
						
							|  |  |  | 		block  *types.Block | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if (*big.Int)(blockHashOrNumber).Cmp(big.NewInt(math.MaxInt64)) > 0 { | 
					
						
							|  |  |  | 		blockHash := common.BigToHash((*big.Int)(blockHashOrNumber)) | 
					
						
							|  |  |  | 		header = api.blockchain.GetHeaderByHash(blockHash) | 
					
						
							|  |  |  | 		block = api.blockchain.GetBlockByHash(blockHash) | 
					
						
							|  |  |  | 		//fmt.Printf("Account range: %x, txIndex %d, start: %x, maxResults: %d\n", blockHash, txIndex, common.BigToHash((*big.Int)(addressHash)), maxResults) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		blockNumber := (*big.Int)(blockHashOrNumber).Uint64() | 
					
						
							|  |  |  | 		header = api.blockchain.GetHeaderByNumber(blockNumber) | 
					
						
							|  |  |  | 		block = api.blockchain.GetBlockByNumber(blockNumber) | 
					
						
							|  |  |  | 		//fmt.Printf("Account range: %d, txIndex %d, start: %x, maxResults: %d\n", blockNumber, txIndex, common.BigToHash((*big.Int)(addressHash)), maxResults) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	parentHeader := api.blockchain.GetHeaderByHash(header.ParentHash) | 
					
						
							|  |  |  | 	var root common.Hash | 
					
						
							|  |  |  | 	var statedb *state.StateDB | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	if parentHeader == nil || int(txIndex) >= len(block.Transactions()) { | 
					
						
							|  |  |  | 		root = header.Root | 
					
						
							|  |  |  | 		statedb, err = api.blockchain.StateAt(root) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return AccountRangeResult{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		root = parentHeader.Root | 
					
						
							|  |  |  | 		statedb, err = api.blockchain.StateAt(root) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return AccountRangeResult{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Recompute transactions up to the target index. | 
					
						
							|  |  |  | 		signer := types.MakeSigner(api.blockchain.Config(), block.Number()) | 
					
						
							|  |  |  | 		for idx, tx := range block.Transactions() { | 
					
						
							|  |  |  | 			// Assemble the transaction call message and return if the requested offset | 
					
						
							|  |  |  | 			msg, _ := tx.AsMessage(signer) | 
					
						
							|  |  |  | 			context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil) | 
					
						
							|  |  |  | 			// Not yet the searched for transaction, execute on top of the current state | 
					
						
							|  |  |  | 			vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{}) | 
					
						
							|  |  |  | 			if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { | 
					
						
							|  |  |  | 				return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Ensure any modifications are committed to the state | 
					
						
							|  |  |  | 			// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect | 
					
						
							|  |  |  | 			root = statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number())) | 
					
						
							|  |  |  | 			if idx == int(txIndex) { | 
					
						
							|  |  |  | 				// This is to make sure root can be opened by OpenTrie | 
					
						
							|  |  |  | 				root, err = statedb.Commit(api.chainConfig.IsEIP158(block.Number())) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return AccountRangeResult{}, err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	accountTrie, err := statedb.Database().OpenTrie(root) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return AccountRangeResult{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	it := trie.NewIterator(accountTrie.NodeIterator(common.BigToHash((*big.Int)(addressHash)).Bytes())) | 
					
						
							|  |  |  | 	result := AccountRangeResult{AddressMap: make(map[common.Hash]common.Address)} | 
					
						
							| 
									
										
										
										
											2019-09-02 12:49:49 +03:00
										 |  |  | 	for i := 0; i < int(maxResults) && it.Next(); i++ { | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 		if preimage := accountTrie.GetKey(it.Key); preimage != nil { | 
					
						
							|  |  |  | 			result.AddressMap[common.BytesToHash(it.Key)] = common.BytesToAddress(preimage) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	//fmt.Printf("Number of entries returned: %d\n", len(result.AddressMap)) | 
					
						
							|  |  |  | 	// Add the 'next key' so clients can continue downloading. | 
					
						
							|  |  |  | 	if it.Next() { | 
					
						
							|  |  |  | 		next := common.BytesToHash(it.Key) | 
					
						
							|  |  |  | 		result.NextKey = next | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return result, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error) { | 
					
						
							|  |  |  | 	//fmt.Printf("GetBalance %x, block %d\n", address, blockNr) | 
					
						
							|  |  |  | 	header := api.blockchain.GetHeaderByNumber(uint64(blockNr)) | 
					
						
							|  |  |  | 	statedb, err := api.blockchain.StateAt(header.Root) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return (*math.HexOrDecimal256)(statedb.GetBalance(address)), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error) { | 
					
						
							|  |  |  | 	header := api.blockchain.GetHeaderByNumber(uint64(blockNr)) | 
					
						
							|  |  |  | 	statedb, err := api.blockchain.StateAt(header.Root) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return statedb.GetCode(address), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error) { | 
					
						
							|  |  |  | 	header := api.blockchain.GetHeaderByNumber(uint64(blockNr)) | 
					
						
							|  |  |  | 	statedb, err := api.blockchain.StateAt(header.Root) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return statedb.GetNonce(address), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) StorageRangeAt(ctx context.Context, | 
					
						
							|  |  |  | 	blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, | 
					
						
							|  |  |  | 	address common.Address, | 
					
						
							|  |  |  | 	begin *math.HexOrDecimal256, maxResults uint64, | 
					
						
							|  |  |  | ) (StorageRangeResult, error) { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		header *types.Header | 
					
						
							|  |  |  | 		block  *types.Block | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if (*big.Int)(blockHashOrNumber).Cmp(big.NewInt(math.MaxInt64)) > 0 { | 
					
						
							|  |  |  | 		blockHash := common.BigToHash((*big.Int)(blockHashOrNumber)) | 
					
						
							|  |  |  | 		header = api.blockchain.GetHeaderByHash(blockHash) | 
					
						
							|  |  |  | 		block = api.blockchain.GetBlockByHash(blockHash) | 
					
						
							|  |  |  | 		//fmt.Printf("Storage range: %x, txIndex %d, addr: %x, start: %x, maxResults: %d\n", | 
					
						
							|  |  |  | 		//	blockHash, txIndex, address, common.BigToHash((*big.Int)(begin)), maxResults) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		blockNumber := (*big.Int)(blockHashOrNumber).Uint64() | 
					
						
							|  |  |  | 		header = api.blockchain.GetHeaderByNumber(blockNumber) | 
					
						
							|  |  |  | 		block = api.blockchain.GetBlockByNumber(blockNumber) | 
					
						
							|  |  |  | 		//fmt.Printf("Storage range: %d, txIndex %d, addr: %x, start: %x, maxResults: %d\n", | 
					
						
							|  |  |  | 		//	blockNumber, txIndex, address, common.BigToHash((*big.Int)(begin)), maxResults) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	parentHeader := api.blockchain.GetHeaderByHash(header.ParentHash) | 
					
						
							|  |  |  | 	var root common.Hash | 
					
						
							|  |  |  | 	var statedb *state.StateDB | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	if parentHeader == nil || int(txIndex) >= len(block.Transactions()) { | 
					
						
							|  |  |  | 		root = header.Root | 
					
						
							|  |  |  | 		statedb, err = api.blockchain.StateAt(root) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return StorageRangeResult{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		root = parentHeader.Root | 
					
						
							|  |  |  | 		statedb, err = api.blockchain.StateAt(root) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return StorageRangeResult{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Recompute transactions up to the target index. | 
					
						
							|  |  |  | 		signer := types.MakeSigner(api.blockchain.Config(), block.Number()) | 
					
						
							|  |  |  | 		for idx, tx := range block.Transactions() { | 
					
						
							|  |  |  | 			// Assemble the transaction call message and return if the requested offset | 
					
						
							|  |  |  | 			msg, _ := tx.AsMessage(signer) | 
					
						
							|  |  |  | 			context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil) | 
					
						
							|  |  |  | 			// Not yet the searched for transaction, execute on top of the current state | 
					
						
							|  |  |  | 			vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{}) | 
					
						
							|  |  |  | 			if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { | 
					
						
							|  |  |  | 				return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Ensure any modifications are committed to the state | 
					
						
							|  |  |  | 			// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect | 
					
						
							| 
									
										
										
										
											2019-07-22 12:34:41 +02:00
										 |  |  | 			_ = statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number())) | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 			if idx == int(txIndex) { | 
					
						
							|  |  |  | 				// This is to make sure root can be opened by OpenTrie | 
					
						
							| 
									
										
										
										
											2019-07-22 12:34:41 +02:00
										 |  |  | 				_, err = statedb.Commit(vmenv.ChainConfig().IsEIP158(block.Number())) | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return StorageRangeResult{}, err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	storageTrie := statedb.StorageTrie(address) | 
					
						
							|  |  |  | 	it := trie.NewIterator(storageTrie.NodeIterator(common.BigToHash((*big.Int)(begin)).Bytes())) | 
					
						
							|  |  |  | 	result := StorageRangeResult{Storage: make(map[common.Hash]SRItem)} | 
					
						
							|  |  |  | 	for i := 0; /*i < int(maxResults) && */ it.Next(); i++ { | 
					
						
							|  |  |  | 		if preimage := storageTrie.GetKey(it.Key); preimage != nil { | 
					
						
							|  |  |  | 			key := (*math.HexOrDecimal256)(big.NewInt(0).SetBytes(preimage)) | 
					
						
							|  |  |  | 			v, _, err := rlp.SplitString(it.Value) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return StorageRangeResult{}, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			value := (*math.HexOrDecimal256)(big.NewInt(0).SetBytes(v)) | 
					
						
							|  |  |  | 			ks, _ := key.MarshalText() | 
					
						
							|  |  |  | 			vs, _ := value.MarshalText() | 
					
						
							|  |  |  | 			if len(ks)%2 != 0 { | 
					
						
							|  |  |  | 				ks = append(append(append([]byte{}, ks[:2]...), byte('0')), ks[2:]...) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if len(vs)%2 != 0 { | 
					
						
							|  |  |  | 				vs = append(append(append([]byte{}, vs[:2]...), byte('0')), vs[2:]...) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			result.Storage[common.BytesToHash(it.Key)] = SRItem{ | 
					
						
							|  |  |  | 				Key:   string(ks), | 
					
						
							|  |  |  | 				Value: string(vs), | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if it.Next() { | 
					
						
							|  |  |  | 		result.Complete = false | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		result.Complete = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return result, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (api *RetestethAPI) ClientVersion(ctx context.Context) (string, error) { | 
					
						
							|  |  |  | 	return "Geth-" + params.VersionWithCommit(gitCommit, gitDate), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // splitAndTrim splits input separated by a comma | 
					
						
							|  |  |  | // and trims excessive white space from the substrings. | 
					
						
							|  |  |  | func splitAndTrim(input string) []string { | 
					
						
							|  |  |  | 	result := strings.Split(input, ",") | 
					
						
							|  |  |  | 	for i, r := range result { | 
					
						
							|  |  |  | 		result[i] = strings.TrimSpace(r) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return result | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func retesteth(ctx *cli.Context) error { | 
					
						
							|  |  |  | 	log.Info("Welcome to retesteth!") | 
					
						
							|  |  |  | 	// register signer API with server | 
					
						
							|  |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2019-07-22 12:34:41 +02:00
										 |  |  | 		extapiURL string | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	apiImpl := &RetestethAPI{} | 
					
						
							|  |  |  | 	var testApi RetestethTestAPI = apiImpl | 
					
						
							|  |  |  | 	var ethApi RetestethEthAPI = apiImpl | 
					
						
							|  |  |  | 	var debugApi RetestethDebugAPI = apiImpl | 
					
						
							|  |  |  | 	var web3Api RetestWeb3API = apiImpl | 
					
						
							|  |  |  | 	rpcAPI := []rpc.API{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Namespace: "test", | 
					
						
							|  |  |  | 			Public:    true, | 
					
						
							|  |  |  | 			Service:   testApi, | 
					
						
							|  |  |  | 			Version:   "1.0", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Namespace: "eth", | 
					
						
							|  |  |  | 			Public:    true, | 
					
						
							|  |  |  | 			Service:   ethApi, | 
					
						
							|  |  |  | 			Version:   "1.0", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Namespace: "debug", | 
					
						
							|  |  |  | 			Public:    true, | 
					
						
							|  |  |  | 			Service:   debugApi, | 
					
						
							|  |  |  | 			Version:   "1.0", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Namespace: "web3", | 
					
						
							|  |  |  | 			Public:    true, | 
					
						
							|  |  |  | 			Service:   web3Api, | 
					
						
							|  |  |  | 			Version:   "1.0", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vhosts := splitAndTrim(ctx.GlobalString(utils.RPCVirtualHostsFlag.Name)) | 
					
						
							|  |  |  | 	cors := splitAndTrim(ctx.GlobalString(utils.RPCCORSDomainFlag.Name)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 13:33:12 +02:00
										 |  |  | 	// register apis and create handler stack | 
					
						
							|  |  |  | 	srv := rpc.NewServer() | 
					
						
							|  |  |  | 	err := node.RegisterApisFromWhitelist(rpcAPI, []string{"test", "eth", "debug", "web3"}, srv, false) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		utils.Fatalf("Could not register RPC apis: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	handler := node.NewHTTPHandlerStack(srv, cors, vhosts) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	// start http server | 
					
						
							| 
									
										
										
										
											2020-03-16 12:33:32 +03:00
										 |  |  | 	var RetestethHTTPTimeouts = rpc.HTTPTimeouts{ | 
					
						
							|  |  |  | 		ReadTimeout:  120 * time.Second, | 
					
						
							|  |  |  | 		WriteTimeout: 120 * time.Second, | 
					
						
							|  |  |  | 		IdleTimeout:  120 * time.Second, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	httpEndpoint := fmt.Sprintf("%s:%d", ctx.GlobalString(utils.RPCListenAddrFlag.Name), ctx.Int(rpcPortFlag.Name)) | 
					
						
							| 
									
										
										
										
											2020-04-08 13:33:12 +02:00
										 |  |  | 	listener, err := node.StartHTTPEndpoint(httpEndpoint, RetestethHTTPTimeouts, handler) | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		utils.Fatalf("Could not start RPC api: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	extapiURL = fmt.Sprintf("http://%s", httpEndpoint) | 
					
						
							|  |  |  | 	log.Info("HTTP endpoint opened", "url", extapiURL) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		listener.Close() | 
					
						
							|  |  |  | 		log.Info("HTTP endpoint closed", "url", httpEndpoint) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 11:38:34 +01:00
										 |  |  | 	abortChan := make(chan os.Signal, 11) | 
					
						
							| 
									
										
										
										
											2019-06-05 13:03:23 +01:00
										 |  |  | 	signal.Notify(abortChan, os.Interrupt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sig := <-abortChan | 
					
						
							|  |  |  | 	log.Info("Exiting...", "signal", sig) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |