mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 22:26:42 +00:00
Merge branch 'release/1.13' into upstream_merge_1.13
This commit is contained in:
commit
f4c41e2e70
355 changed files with 32235 additions and 8758 deletions
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
go-version: 1.21.4
|
go-version: 1.21.4
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: go test ./...
|
run: go test -short ./...
|
||||||
env:
|
env:
|
||||||
GOOS: linux
|
GOOS: linux
|
||||||
GOARCH: 386
|
GOARCH: 386
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ run:
|
||||||
linters:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
enable:
|
enable:
|
||||||
- goconst
|
|
||||||
- goimports
|
- goimports
|
||||||
- gosimple
|
- gosimple
|
||||||
- govet
|
- govet
|
||||||
|
|
@ -37,9 +36,6 @@ linters:
|
||||||
linters-settings:
|
linters-settings:
|
||||||
gofmt:
|
gofmt:
|
||||||
simplify: true
|
simplify: true
|
||||||
goconst:
|
|
||||||
min-len: 3 # minimum length of string constant
|
|
||||||
min-occurrences: 6 # minimum number of occurrences
|
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
exclude-files:
|
exclude-files:
|
||||||
|
|
|
||||||
4
Makefile
4
Makefile
|
|
@ -38,11 +38,13 @@ generate-mocks:
|
||||||
go generate mockgen -destination=./eth/filters/IBackend.go -package=filters ./eth/filters Backend
|
go generate mockgen -destination=./eth/filters/IBackend.go -package=filters ./eth/filters Backend
|
||||||
go generate mockgen -destination=../eth/filters/IDatabase.go -package=filters ./ethdb Database
|
go generate mockgen -destination=../eth/filters/IDatabase.go -package=filters ./ethdb Database
|
||||||
|
|
||||||
|
#? geth: Build geth
|
||||||
geth:
|
geth:
|
||||||
$(GORUN) build/ci.go install ./cmd/geth
|
$(GORUN) build/ci.go install ./cmd/geth
|
||||||
@echo "Done building."
|
@echo "Done building."
|
||||||
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
||||||
|
|
||||||
|
#? all: Build all packages and executables
|
||||||
all:
|
all:
|
||||||
$(GORUN) build/ci.go install
|
$(GORUN) build/ci.go install
|
||||||
|
|
||||||
|
|
@ -89,6 +91,7 @@ goimports:
|
||||||
docs:
|
docs:
|
||||||
$(GORUN) cmd/clidoc/main.go -d ./docs/cli
|
$(GORUN) cmd/clidoc/main.go -d ./docs/cli
|
||||||
|
|
||||||
|
#? clean: Clean go cache, built executables, and the auto generated folder
|
||||||
clean:
|
clean:
|
||||||
go clean -cache
|
go clean -cache
|
||||||
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
||||||
|
|
@ -96,6 +99,7 @@ clean:
|
||||||
# The devtools target installs tools required for 'go generate'.
|
# The devtools target installs tools required for 'go generate'.
|
||||||
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
||||||
|
|
||||||
|
#? devtools: Install recommended developer tools
|
||||||
devtools:
|
devtools:
|
||||||
# Notice! If you adding new binary - add it also to tests/deps/fake.go file
|
# Notice! If you adding new binary - add it also to tests/deps/fake.go file
|
||||||
$(GOBUILD) -o $(GOBIN)/stringer github.com/golang.org/x/tools/cmd/stringer
|
$(GOBUILD) -o $(GOBIN)/stringer github.com/golang.org/x/tools/cmd/stringer
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,11 @@ type BlockHashContractCaller interface {
|
||||||
// used when the user does not provide some needed values, but rather leaves it up
|
// used when the user does not provide some needed values, but rather leaves it up
|
||||||
// to the transactor to decide.
|
// to the transactor to decide.
|
||||||
type ContractTransactor interface {
|
type ContractTransactor interface {
|
||||||
|
ethereum.GasEstimator
|
||||||
|
ethereum.GasPricer
|
||||||
|
ethereum.GasPricer1559
|
||||||
|
ethereum.TransactionSender
|
||||||
|
|
||||||
// HeaderByNumber returns a block header from the current canonical chain. If
|
// HeaderByNumber returns a block header from the current canonical chain. If
|
||||||
// number is nil, the latest known header is returned.
|
// number is nil, the latest known header is returned.
|
||||||
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
||||||
|
|
@ -93,38 +98,6 @@ type ContractTransactor interface {
|
||||||
|
|
||||||
// PendingNonceAt retrieves the current pending nonce associated with an account.
|
// PendingNonceAt retrieves the current pending nonce associated with an account.
|
||||||
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
|
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
|
||||||
|
|
||||||
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
|
||||||
// execution of a transaction.
|
|
||||||
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
|
||||||
|
|
||||||
// SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
|
|
||||||
// a timely execution of a transaction.
|
|
||||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
|
||||||
|
|
||||||
// EstimateGas tries to estimate the gas needed to execute a specific
|
|
||||||
// transaction based on the current pending state of the backend blockchain.
|
|
||||||
// There is no guarantee that this is the true gas limit requirement as other
|
|
||||||
// transactions may be added or removed by miners, but it should provide a basis
|
|
||||||
// for setting a reasonable default.
|
|
||||||
EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)
|
|
||||||
|
|
||||||
// SendTransaction injects the transaction into the pending pool for execution.
|
|
||||||
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContractFilterer defines the methods needed to access log events using one-off
|
|
||||||
// queries or continuous event subscriptions.
|
|
||||||
type ContractFilterer interface {
|
|
||||||
// FilterLogs executes a log filter operation, blocking during execution and
|
|
||||||
// returning all the results in one batch.
|
|
||||||
//
|
|
||||||
// TODO(karalabe): Deprecate when the subscription one can return past data too.
|
|
||||||
FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error)
|
|
||||||
|
|
||||||
// SubscribeFilterLogs creates a background log filtering operation, returning
|
|
||||||
// a subscription immediately, which can be used to stream the found events.
|
|
||||||
SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
|
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
|
||||||
|
|
@ -133,6 +106,12 @@ type DeployBackend interface {
|
||||||
CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
|
CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContractFilterer defines the methods needed to access log events using one-off
|
||||||
|
// queries or continuous event subscriptions.
|
||||||
|
type ContractFilterer interface {
|
||||||
|
ethereum.LogFilterer
|
||||||
|
}
|
||||||
|
|
||||||
// ContractBackend defines the methods needed to work with contracts on a read-write basis.
|
// ContractBackend defines the methods needed to work with contracts on a read-write basis.
|
||||||
type ContractBackend interface {
|
type ContractBackend interface {
|
||||||
ContractCaller
|
ContractCaller
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,16 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (fb *filterBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
|
func (b *SimulatedBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
|
||||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
receipt, err := b.GetBorBlockReceipt(ctx, hash)
|
||||||
if number == nil {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
receipt := rawdb.ReadRawBorReceipt(fb.db, hash, *number)
|
|
||||||
if receipt == nil {
|
if receipt == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -24,12 +22,12 @@ func (fb *filterBackend) GetBorBlockReceipt(ctx context.Context, hash common.Has
|
||||||
return receipt, nil
|
return receipt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
|
func (b *SimulatedBackend) GetVoteOnHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64, hash string, milestoneId string) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
|
func (b *SimulatedBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
|
||||||
receipt, err := fb.GetBorBlockReceipt(ctx, hash)
|
receipt, err := b.GetBorBlockReceipt(ctx, hash)
|
||||||
if err != nil || receipt == nil {
|
if err != nil || receipt == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -38,6 +36,6 @@ func (fb *filterBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribeStateSyncEvent subscribes to state sync events
|
// SubscribeStateSyncEvent subscribes to state sync events
|
||||||
func (fb *filterBackend) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription {
|
func (b *SimulatedBackend) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription {
|
||||||
return fb.bc.SubscribeStateSyncEvent(ch)
|
return b.SubscribeStateSyncEvent(ch)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,958 +18,36 @@ package backends
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/ethash"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
|
||||||
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/ethclient/simulated"
|
||||||
"github.com/ethereum/go-ethereum/eth/filters"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This nil assignment ensures at compile time that SimulatedBackend implements bind.ContractBackend.
|
// SimulatedBackend is a simulated blockchain.
|
||||||
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
// Deprecated: use package github.com/ethereum/go-ethereum/ethclient/simulated instead.
|
||||||
|
|
||||||
var (
|
|
||||||
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
|
||||||
errBlockHashUnsupported = errors.New("simulatedBackend cannot access blocks by hash other than the latest block")
|
|
||||||
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
|
||||||
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
|
||||||
)
|
|
||||||
|
|
||||||
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
|
||||||
// the background. Its main purpose is to allow for easy testing of contract bindings.
|
|
||||||
// Simulated backend implements the following interfaces:
|
|
||||||
// ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
|
|
||||||
// DeployBackend, GasEstimator, GasPricer, LogFilterer, PendingContractCaller, TransactionReader, and TransactionSender
|
|
||||||
type SimulatedBackend struct {
|
type SimulatedBackend struct {
|
||||||
database ethdb.Database // In memory database to store our testing data
|
*simulated.Backend
|
||||||
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
|
simulated.Client
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
pendingBlock *types.Block // Currently pending block that will be imported on request
|
|
||||||
pendingState *state.StateDB // Currently pending state that will be the active on request
|
|
||||||
pendingReceipts types.Receipts // Currently receipts for the pending block
|
|
||||||
|
|
||||||
events *filters.EventSystem // for filtering log events live
|
|
||||||
filterSystem *filters.FilterSystem // for filtering database logs
|
|
||||||
|
|
||||||
config *params.ChainConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database
|
// Fork sets the head to a new block, which is based on the provided parentHash.
|
||||||
// and uses a simulated blockchain for testing purposes.
|
func (b *SimulatedBackend) Fork(ctx context.Context, parentHash common.Hash) error {
|
||||||
// A simulated backend always uses chainID 1337.
|
return b.Backend.Fork(parentHash)
|
||||||
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
|
||||||
genesis := core.Genesis{
|
|
||||||
Config: params.AllEthashProtocolChanges,
|
|
||||||
GasLimit: gasLimit,
|
|
||||||
Alloc: alloc,
|
|
||||||
}
|
|
||||||
blockchain, _ := core.NewBlockChain(database, nil, &genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
|
||||||
|
|
||||||
backend := &SimulatedBackend{
|
|
||||||
database: database,
|
|
||||||
blockchain: blockchain,
|
|
||||||
config: genesis.Config,
|
|
||||||
}
|
|
||||||
|
|
||||||
filterBackend := &filterBackend{database, blockchain, backend}
|
|
||||||
backend.filterSystem = filters.NewFilterSystem(filterBackend, filters.Config{})
|
|
||||||
backend.events = filters.NewEventSystem(backend.filterSystem, false)
|
|
||||||
|
|
||||||
header := backend.blockchain.CurrentBlock()
|
|
||||||
block := backend.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
|
|
||||||
|
|
||||||
backend.rollback(block)
|
|
||||||
return backend
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
||||||
// for testing purposes.
|
// for testing purposes.
|
||||||
|
//
|
||||||
// A simulated backend always uses chainID 1337.
|
// A simulated backend always uses chainID 1337.
|
||||||
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
|
||||||
return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close terminates the underlying blockchain's update loop.
|
|
||||||
func (b *SimulatedBackend) Close() error {
|
|
||||||
b.blockchain.Stop()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit imports all the pending transactions as a single block and starts a
|
|
||||||
// fresh new state.
|
|
||||||
func (b *SimulatedBackend) Commit() common.Hash {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
|
|
||||||
panic(err) // This cannot happen unless the simulator is wrong, fail in that case
|
|
||||||
}
|
|
||||||
blockHash := b.pendingBlock.Hash()
|
|
||||||
|
|
||||||
// Using the last inserted block here makes it possible to build on a side
|
|
||||||
// chain after a fork.
|
|
||||||
b.rollback(b.pendingBlock)
|
|
||||||
|
|
||||||
return blockHash
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rollback aborts all pending transactions, reverting to the last committed state.
|
|
||||||
func (b *SimulatedBackend) Rollback() {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
header := b.blockchain.CurrentBlock()
|
|
||||||
block := b.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
|
|
||||||
|
|
||||||
b.rollback(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *SimulatedBackend) rollback(parent *types.Block) {
|
|
||||||
blocks, _ := core.GenerateChain(b.config, parent, ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
|
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fork creates a side-chain that can be used to simulate reorgs.
|
|
||||||
//
|
//
|
||||||
// This function should be called with the ancestor block where the new side
|
// Deprecated: please use simulated.Backend from package
|
||||||
// chain should be started. Transactions (old and new) can then be applied on
|
// github.com/ethereum/go-ethereum/ethclient/simulated instead.
|
||||||
// top and Commit-ed.
|
func NewSimulatedBackend(alloc types.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||||
//
|
b := simulated.NewBackend(alloc, simulated.WithBlockGasLimit(gasLimit))
|
||||||
// Note, the side-chain will only become canonical (and trigger the events) when
|
|
||||||
// it becomes longer. Until then CallContract will still operate on the current
|
|
||||||
// canonical chain.
|
|
||||||
//
|
|
||||||
// There is a % chance that the side chain becomes canonical at the same length
|
|
||||||
// to simulate live network behavior.
|
|
||||||
func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if len(b.pendingBlock.Transactions()) != 0 {
|
return &SimulatedBackend{
|
||||||
return errors.New("pending block dirty")
|
Backend: b,
|
||||||
}
|
Client: b.Client(),
|
||||||
block, err := b.blockByHash(ctx, parent)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b.rollback(block)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// stateByBlockNumber retrieves a state by a given blocknumber.
|
|
||||||
func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
|
|
||||||
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number) == 0 {
|
|
||||||
return b.blockchain.State()
|
|
||||||
}
|
|
||||||
block, err := b.blockByNumber(ctx, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b.blockchain.StateAt(block.Root())
|
|
||||||
}
|
|
||||||
|
|
||||||
// CodeAt returns the code associated with a certain account in the blockchain.
|
|
||||||
func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return stateDB.GetCode(contract), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CodeAtHash returns the code associated with a certain account in the blockchain.
|
|
||||||
func (b *SimulatedBackend) CodeAtHash(ctx context.Context, contract common.Address, blockHash common.Hash) ([]byte, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
header, err := b.headerByHash(blockHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stateDB, err := b.blockchain.StateAt(header.Root)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateDB.GetCode(contract), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BalanceAt returns the wei balance of a certain account in the blockchain.
|
|
||||||
func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return stateDB.GetBalance(contract), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NonceAt returns the nonce of a certain account in the blockchain.
|
|
||||||
func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return stateDB.GetNonce(contract), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StorageAt returns the value of key in the storage of an account in the blockchain.
|
|
||||||
func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
val := stateDB.GetState(contract, key)
|
|
||||||
return val[:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionReceipt returns the receipt of a transaction.
|
|
||||||
func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
|
|
||||||
if receipt == nil {
|
|
||||||
return nil, ethereum.NotFound
|
|
||||||
}
|
|
||||||
return receipt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionByHash checks the pool of pending transactions in addition to the
|
|
||||||
// blockchain. The isPending return value indicates whether the transaction has been
|
|
||||||
// mined yet. Note that the transaction may not be part of the canonical chain even if
|
|
||||||
// it's not pending.
|
|
||||||
func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
tx := b.pendingBlock.Transaction(txHash)
|
|
||||||
if tx != nil {
|
|
||||||
return tx, true, nil
|
|
||||||
}
|
|
||||||
tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash)
|
|
||||||
if tx != nil {
|
|
||||||
return tx, false, nil
|
|
||||||
}
|
|
||||||
return nil, false, ethereum.NotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockByHash retrieves a block based on the block hash.
|
|
||||||
func (b *SimulatedBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
return b.blockByHash(ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockByHash retrieves a block based on the block hash without Locking.
|
|
||||||
func (b *SimulatedBackend) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
|
||||||
if hash == b.pendingBlock.Hash() {
|
|
||||||
return b.pendingBlock, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
block := b.blockchain.GetBlockByHash(hash)
|
|
||||||
if block != nil {
|
|
||||||
return block, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockByNumber retrieves a block from the database by number, caching it
|
|
||||||
// (associated with its hash) if found.
|
|
||||||
func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
return b.blockByNumber(ctx, number)
|
|
||||||
}
|
|
||||||
|
|
||||||
// blockByNumber retrieves a block from the database by number, caching it
|
|
||||||
// (associated with its hash) if found without Lock.
|
|
||||||
func (b *SimulatedBackend) blockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
|
||||||
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
|
|
||||||
return b.blockByHash(ctx, b.blockchain.CurrentBlock().Hash())
|
|
||||||
}
|
|
||||||
|
|
||||||
block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
|
|
||||||
if block == nil {
|
|
||||||
return nil, errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return block, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeaderByHash returns a block header from the current canonical chain.
|
|
||||||
func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
return b.headerByHash(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// headerByHash retrieves a header from the database by hash without Lock.
|
|
||||||
func (b *SimulatedBackend) headerByHash(hash common.Hash) (*types.Header, error) {
|
|
||||||
if hash == b.pendingBlock.Hash() {
|
|
||||||
return b.pendingBlock.Header(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
header := b.blockchain.GetHeaderByHash(hash)
|
|
||||||
if header == nil {
|
|
||||||
return nil, errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return header, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeaderByNumber returns a block header from the current canonical chain. If number is
|
|
||||||
// nil, the latest known header is returned.
|
|
||||||
func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if block == nil || block.Cmp(b.pendingBlock.Number()) == 0 {
|
|
||||||
return b.blockchain.CurrentHeader(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionCount returns the number of transactions in a given block.
|
|
||||||
func (b *SimulatedBackend) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if blockHash == b.pendingBlock.Hash() {
|
|
||||||
return uint(b.pendingBlock.Transactions().Len()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
block := b.blockchain.GetBlockByHash(blockHash)
|
|
||||||
if block == nil {
|
|
||||||
return uint(0), errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint(block.Transactions().Len()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionInBlock returns the transaction for a specific block at a specific index.
|
|
||||||
func (b *SimulatedBackend) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if blockHash == b.pendingBlock.Hash() {
|
|
||||||
transactions := b.pendingBlock.Transactions()
|
|
||||||
if uint(len(transactions)) < index+1 {
|
|
||||||
return nil, errTransactionDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return transactions[index], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
block := b.blockchain.GetBlockByHash(blockHash)
|
|
||||||
if block == nil {
|
|
||||||
return nil, errBlockDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
transactions := block.Transactions()
|
|
||||||
if uint(len(transactions)) < index+1 {
|
|
||||||
return nil, errTransactionDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
return transactions[index], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PendingCodeAt returns the code associated with an account in the pending state.
|
|
||||||
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
return b.pendingState.GetCode(contract), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRevertError(result *core.ExecutionResult) *revertError {
|
|
||||||
reason, errUnpack := abi.UnpackRevert(result.Revert())
|
|
||||||
err := errors.New("execution reverted")
|
|
||||||
if errUnpack == nil {
|
|
||||||
err = fmt.Errorf("execution reverted: %v", reason)
|
|
||||||
}
|
|
||||||
return &revertError{
|
|
||||||
error: err,
|
|
||||||
reason: hexutil.Encode(result.Revert()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// revertError is an API error that encompasses an EVM revert with JSON error
|
|
||||||
// code and a binary data blob.
|
|
||||||
type revertError struct {
|
|
||||||
error
|
|
||||||
reason string // revert reason hex encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorCode returns the JSON error code for a revert.
|
|
||||||
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
|
|
||||||
func (e *revertError) ErrorCode() int {
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorData returns the hex encoded revert reason.
|
|
||||||
func (e *revertError) ErrorData() interface{} {
|
|
||||||
return e.reason
|
|
||||||
}
|
|
||||||
|
|
||||||
// CallContract executes a contract call.
|
|
||||||
func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number) != 0 {
|
|
||||||
return nil, errBlockNumberUnsupported
|
|
||||||
}
|
|
||||||
return b.callContractAtHead(ctx, call)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CallContractAtHash executes a contract call on a specific block hash.
|
|
||||||
func (b *SimulatedBackend) CallContractAtHash(ctx context.Context, call ethereum.CallMsg, blockHash common.Hash) ([]byte, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if blockHash != b.blockchain.CurrentBlock().Hash() {
|
|
||||||
return nil, errBlockHashUnsupported
|
|
||||||
}
|
|
||||||
return b.callContractAtHead(ctx, call)
|
|
||||||
}
|
|
||||||
|
|
||||||
// callContractAtHead executes a contract call against the latest block state.
|
|
||||||
func (b *SimulatedBackend) callContractAtHead(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
|
||||||
stateDB, err := b.blockchain.State()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// If the result contains a revert reason, try to unpack and return it.
|
|
||||||
if len(res.Revert()) > 0 {
|
|
||||||
return nil, newRevertError(res)
|
|
||||||
}
|
|
||||||
return res.Return(), res.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PendingCallContract executes a contract call on the pending state.
|
|
||||||
func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
|
||||||
|
|
||||||
res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// If the result contains a revert reason, try to unpack and return it.
|
|
||||||
if len(res.Revert()) > 0 {
|
|
||||||
return nil, newRevertError(res)
|
|
||||||
}
|
|
||||||
return res.Return(), res.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
|
||||||
// the nonce currently pending for the account.
|
|
||||||
func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
|
|
||||||
// chain doesn't have miners, we just return a gas price of 1 for any call.
|
|
||||||
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if b.pendingBlock.Header().BaseFee != nil {
|
|
||||||
return b.pendingBlock.Header().BaseFee, nil
|
|
||||||
}
|
|
||||||
return big.NewInt(1), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated
|
|
||||||
// chain doesn't have miners, we just return a gas tip of 1 for any call.
|
|
||||||
func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
|
||||||
return big.NewInt(1), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EstimateGas executes the requested code against the currently pending block/state and
|
|
||||||
// returns the used amount of gas.
|
|
||||||
func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
// Determine the lowest and highest possible gas limits to binary search in between
|
|
||||||
var (
|
|
||||||
lo uint64 = params.TxGas - 1
|
|
||||||
hi uint64
|
|
||||||
cap uint64
|
|
||||||
)
|
|
||||||
if call.Gas >= params.TxGas {
|
|
||||||
hi = call.Gas
|
|
||||||
} else {
|
|
||||||
hi = b.pendingBlock.GasLimit()
|
|
||||||
}
|
|
||||||
// Normalize the max fee per gas the call is willing to spend.
|
|
||||||
var feeCap *big.Int
|
|
||||||
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
|
||||||
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
|
||||||
} else if call.GasPrice != nil {
|
|
||||||
feeCap = call.GasPrice
|
|
||||||
} else if call.GasFeeCap != nil {
|
|
||||||
feeCap = call.GasFeeCap
|
|
||||||
} else {
|
|
||||||
feeCap = common.Big0
|
|
||||||
}
|
|
||||||
// Recap the highest gas allowance with account's balance.
|
|
||||||
if feeCap.BitLen() != 0 {
|
|
||||||
balance := b.pendingState.GetBalance(call.From) // from can't be nil
|
|
||||||
available := new(big.Int).Set(balance)
|
|
||||||
if call.Value != nil {
|
|
||||||
if call.Value.Cmp(available) >= 0 {
|
|
||||||
return 0, core.ErrInsufficientFundsForTransfer
|
|
||||||
}
|
|
||||||
available.Sub(available, call.Value)
|
|
||||||
}
|
|
||||||
allowance := new(big.Int).Div(available, feeCap)
|
|
||||||
if allowance.IsUint64() && hi > allowance.Uint64() {
|
|
||||||
transfer := call.Value
|
|
||||||
if transfer == nil {
|
|
||||||
transfer = new(big.Int)
|
|
||||||
}
|
|
||||||
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
|
|
||||||
"sent", transfer, "feecap", feeCap, "fundable", allowance)
|
|
||||||
hi = allowance.Uint64()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cap = hi
|
|
||||||
|
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
|
||||||
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
|
||||||
call.Gas = gas
|
|
||||||
|
|
||||||
snapshot := b.pendingState.Snapshot()
|
|
||||||
res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
|
|
||||||
b.pendingState.RevertToSnapshot(snapshot)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, core.ErrIntrinsicGas) {
|
|
||||||
return true, nil, nil // Special case, raise gas limit
|
|
||||||
}
|
|
||||||
return true, nil, err // Bail out
|
|
||||||
}
|
|
||||||
return res.Failed(), res, nil
|
|
||||||
}
|
|
||||||
// Execute the binary search and hone in on an executable gas limit
|
|
||||||
for lo+1 < hi {
|
|
||||||
mid := (hi + lo) / 2
|
|
||||||
failed, _, err := executable(mid)
|
|
||||||
|
|
||||||
// If the error is not nil(consensus error), it means the provided message
|
|
||||||
// call or transaction will never be accepted no matter how much gas it is
|
|
||||||
// assigned. Return the error directly, don't struggle any more
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if failed {
|
|
||||||
lo = mid
|
|
||||||
} else {
|
|
||||||
hi = mid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Reject the transaction as invalid if it still fails at the highest allowance
|
|
||||||
if hi == cap {
|
|
||||||
failed, result, err := executable(hi)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if failed {
|
|
||||||
if result != nil && !errors.Is(result.Err, vm.ErrOutOfGas) {
|
|
||||||
if len(result.Revert()) > 0 {
|
|
||||||
return 0, newRevertError(result)
|
|
||||||
}
|
|
||||||
return 0, result.Err
|
|
||||||
}
|
|
||||||
// Otherwise, the specified gas cap is too low
|
|
||||||
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hi, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// callContract implements common code between normal and pending contract calls.
|
|
||||||
// state is modified during execution, make sure to copy it if necessary.
|
|
||||||
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, header *types.Header, stateDB *state.StateDB) (*core.ExecutionResult, error) {
|
|
||||||
// Gas prices post 1559 need to be initialized
|
|
||||||
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
|
||||||
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
|
||||||
}
|
|
||||||
if !b.blockchain.Config().IsLondon(header.Number) {
|
|
||||||
// If there's no basefee, then it must be a non-1559 execution
|
|
||||||
if call.GasPrice == nil {
|
|
||||||
call.GasPrice = new(big.Int)
|
|
||||||
}
|
|
||||||
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
|
||||||
} else {
|
|
||||||
// A basefee is provided, necessitating 1559-type execution
|
|
||||||
if call.GasPrice != nil {
|
|
||||||
// User specified the legacy gas field, convert to 1559 gas typing
|
|
||||||
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
|
||||||
} else {
|
|
||||||
// User specified 1559 gas fields (or none), use those
|
|
||||||
if call.GasFeeCap == nil {
|
|
||||||
call.GasFeeCap = new(big.Int)
|
|
||||||
}
|
|
||||||
if call.GasTipCap == nil {
|
|
||||||
call.GasTipCap = new(big.Int)
|
|
||||||
}
|
|
||||||
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
|
|
||||||
call.GasPrice = new(big.Int)
|
|
||||||
if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 {
|
|
||||||
call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, header.BaseFee), call.GasFeeCap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ensure message is initialized properly.
|
|
||||||
if call.Gas == 0 {
|
|
||||||
call.Gas = 10 * header.GasLimit
|
|
||||||
}
|
|
||||||
if call.Value == nil {
|
|
||||||
call.Value = new(big.Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set infinite balance to the fake caller account.
|
|
||||||
from := stateDB.GetOrNewStateObject(call.From)
|
|
||||||
from.SetBalance(math.MaxBig256)
|
|
||||||
|
|
||||||
// Execute the call.
|
|
||||||
msg := &core.Message{
|
|
||||||
From: call.From,
|
|
||||||
To: call.To,
|
|
||||||
Value: call.Value,
|
|
||||||
GasLimit: call.Gas,
|
|
||||||
GasPrice: call.GasPrice,
|
|
||||||
GasFeeCap: call.GasFeeCap,
|
|
||||||
GasTipCap: call.GasTipCap,
|
|
||||||
Data: call.Data,
|
|
||||||
AccessList: call.AccessList,
|
|
||||||
SkipAccountChecks: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new environment which holds all relevant information
|
|
||||||
// about the transaction and calling mechanisms.
|
|
||||||
txContext := core.NewEVMTxContext(msg)
|
|
||||||
evmContext := core.NewEVMBlockContext(header, b.blockchain, nil)
|
|
||||||
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
|
||||||
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
|
||||||
|
|
||||||
return core.ApplyMessage(vmEnv, msg, gasPool, context.Background())
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendTransaction updates the pending block to include the given transaction.
|
|
||||||
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
// Get the last block
|
|
||||||
block, err := b.blockByHash(ctx, b.pendingBlock.ParentHash())
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("could not fetch parent")
|
|
||||||
}
|
|
||||||
// Check transaction validity
|
|
||||||
signer := types.MakeSigner(b.blockchain.Config(), block.Number(), block.Time())
|
|
||||||
sender, err := types.Sender(signer, tx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid transaction: %v", err)
|
|
||||||
}
|
|
||||||
nonce := b.pendingState.GetNonce(sender)
|
|
||||||
if tx.Nonce() != nonce {
|
|
||||||
return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)
|
|
||||||
}
|
|
||||||
// Include tx in chain
|
|
||||||
blocks, receipts := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
|
||||||
for _, tx := range b.pendingBlock.Transactions() {
|
|
||||||
block.AddTxWithChain(b.blockchain, tx)
|
|
||||||
}
|
|
||||||
block.AddTxWithChain(b.blockchain, tx)
|
|
||||||
})
|
|
||||||
stateDB, err := b.blockchain.State()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b.pendingBlock = blocks[0]
|
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
|
||||||
b.pendingReceipts = receipts[0]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterLogs executes a log filter operation, blocking during execution and
|
|
||||||
// returning all the results in one batch.
|
|
||||||
//
|
|
||||||
// TODO(karalabe): Deprecate when the subscription one can return past data too.
|
|
||||||
func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) {
|
|
||||||
var filter *filters.Filter
|
|
||||||
if query.BlockHash != nil {
|
|
||||||
// Block filter requested, construct a single-shot filter
|
|
||||||
filter = b.filterSystem.NewBlockFilter(*query.BlockHash, query.Addresses, query.Topics)
|
|
||||||
} else {
|
|
||||||
// Initialize unset filter boundaries to run from genesis to chain head
|
|
||||||
from := int64(0)
|
|
||||||
if query.FromBlock != nil {
|
|
||||||
from = query.FromBlock.Int64()
|
|
||||||
}
|
|
||||||
to := int64(-1)
|
|
||||||
if query.ToBlock != nil {
|
|
||||||
to = query.ToBlock.Int64()
|
|
||||||
}
|
|
||||||
// Construct the range filter
|
|
||||||
filter = b.filterSystem.NewRangeFilter(from, to, query.Addresses, query.Topics)
|
|
||||||
}
|
|
||||||
// Run the filter and return all the logs
|
|
||||||
logs, err := filter.Logs(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res := make([]types.Log, len(logs))
|
|
||||||
for i, nLog := range logs {
|
|
||||||
res[i] = *nLog
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeFilterLogs creates a background log filtering operation, returning a
|
|
||||||
// subscription immediately, which can be used to stream the found events.
|
|
||||||
func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
|
|
||||||
// Subscribe to contract events
|
|
||||||
sink := make(chan []*types.Log)
|
|
||||||
|
|
||||||
sub, err := b.events.SubscribeLogs(query, sink)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Since we're getting logs in batches, we need to flatten them into a plain stream
|
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
|
||||||
defer sub.Unsubscribe()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case logs := <-sink:
|
|
||||||
for _, nlog := range logs {
|
|
||||||
select {
|
|
||||||
case ch <- *nlog:
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
case <-quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
case <-quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeNewHead returns an event subscription for a new header.
|
|
||||||
func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
|
|
||||||
// subscribe to a new head
|
|
||||||
sink := make(chan *types.Header)
|
|
||||||
sub := b.events.SubscribeNewHeads(sink)
|
|
||||||
|
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
|
||||||
defer sub.Unsubscribe()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case head := <-sink:
|
|
||||||
select {
|
|
||||||
case ch <- head:
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
case <-quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case err := <-sub.Err():
|
|
||||||
return err
|
|
||||||
case <-quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdjustTime adds a time shift to the simulated clock.
|
|
||||||
// It can only be called on empty blocks.
|
|
||||||
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
if len(b.pendingBlock.Transactions()) != 0 {
|
|
||||||
return errors.New("could not adjust time on non-empty block")
|
|
||||||
}
|
|
||||||
// Get the last block
|
|
||||||
block := b.blockchain.GetBlockByHash(b.pendingBlock.ParentHash())
|
|
||||||
if block == nil {
|
|
||||||
return errors.New("could not find parent")
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
|
||||||
block.OffsetTime(int64(adjustment.Seconds()))
|
|
||||||
})
|
|
||||||
stateDB, err := b.blockchain.State()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b.pendingBlock = blocks[0]
|
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blockchain returns the underlying blockchain.
|
|
||||||
func (b *SimulatedBackend) Blockchain() *core.BlockChain {
|
|
||||||
return b.blockchain
|
|
||||||
}
|
|
||||||
|
|
||||||
// filterBackend implements filters.Backend to support filtering for logs without
|
|
||||||
// taking bloom-bits acceleration structures into account.
|
|
||||||
type filterBackend struct {
|
|
||||||
db ethdb.Database
|
|
||||||
bc *core.BlockChain
|
|
||||||
backend *SimulatedBackend
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
|
|
||||||
|
|
||||||
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
|
|
||||||
|
|
||||||
func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
|
|
||||||
switch number {
|
|
||||||
case rpc.PendingBlockNumber:
|
|
||||||
if block := fb.backend.pendingBlock; block != nil {
|
|
||||||
return block.Header(), nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
case rpc.LatestBlockNumber:
|
|
||||||
return fb.bc.CurrentHeader(), nil
|
|
||||||
case rpc.FinalizedBlockNumber:
|
|
||||||
return fb.bc.CurrentFinalBlock(), nil
|
|
||||||
case rpc.SafeBlockNumber:
|
|
||||||
return fb.bc.CurrentSafeBlock(), nil
|
|
||||||
default:
|
|
||||||
return fb.bc.GetHeaderByNumber(uint64(number.Int64())), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
|
||||||
return fb.bc.GetHeaderByHash(hash), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
|
|
||||||
if body := fb.bc.GetBody(hash); body != nil {
|
|
||||||
return body, nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("block body not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
|
|
||||||
return fb.backend.pendingBlock, fb.backend.pendingReceipts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
|
||||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
|
||||||
if number == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
header := rawdb.ReadHeader(fb.db, hash, *number)
|
|
||||||
if header == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return rawdb.ReadReceipts(fb.db, hash, *number, header.Time, fb.bc.Config()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
|
|
||||||
logs := rawdb.ReadLogs(fb.db, hash, number)
|
|
||||||
return logs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
|
||||||
return nullSubscription()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
|
||||||
return fb.bc.SubscribeChainEvent(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
|
||||||
return fb.bc.SubscribeRemovedLogsEvent(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
|
||||||
return fb.bc.SubscribeLogsEvent(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
|
||||||
return nullSubscription()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
|
|
||||||
|
|
||||||
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
|
|
||||||
panic("not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) ChainConfig() *params.ChainConfig {
|
|
||||||
panic("not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) CurrentHeader() *types.Header {
|
|
||||||
panic("not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func nullSubscription() event.Subscription {
|
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
|
||||||
<-quit
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -289,7 +289,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -297,7 +297,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy an interaction tester contract and call a transaction on it
|
// Deploy an interaction tester contract and call a transaction on it
|
||||||
|
|
@ -305,6 +305,7 @@ var bindTests = []struct {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to deploy interactor contract: %v", err)
|
t.Fatalf("Failed to deploy interactor contract: %v", err)
|
||||||
}
|
}
|
||||||
|
sim.Commit()
|
||||||
if _, err := interactor.Transact(auth, "Transact string"); err != nil {
|
if _, err := interactor.Transact(auth, "Transact string"); err != nil {
|
||||||
t.Fatalf("Failed to transact with interactor contract: %v", err)
|
t.Fatalf("Failed to transact with interactor contract: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -344,7 +345,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -352,7 +353,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy a tuple tester contract and execute a structured call on it
|
// Deploy a tuple tester contract and execute a structured call on it
|
||||||
|
|
@ -390,7 +391,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -398,7 +399,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy a tuple tester contract and execute a structured call on it
|
// Deploy a tuple tester contract and execute a structured call on it
|
||||||
|
|
@ -448,7 +449,7 @@ var bindTests = []struct {
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -456,7 +457,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy a slice tester contract and execute a n array call on it
|
// Deploy a slice tester contract and execute a n array call on it
|
||||||
|
|
@ -496,7 +497,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -504,7 +505,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy a default method invoker contract and execute its default method
|
// Deploy a default method invoker contract and execute its default method
|
||||||
|
|
@ -512,6 +513,7 @@ var bindTests = []struct {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to deploy defaulter contract: %v", err)
|
t.Fatalf("Failed to deploy defaulter contract: %v", err)
|
||||||
}
|
}
|
||||||
|
sim.Commit()
|
||||||
if _, err := (&DefaulterRaw{defaulter}).Transfer(auth); err != nil {
|
if _, err := (&DefaulterRaw{defaulter}).Transfer(auth); err != nil {
|
||||||
t.Fatalf("Failed to invoke default method: %v", err)
|
t.Fatalf("Failed to invoke default method: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -562,7 +564,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -570,7 +572,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy a structs method invoker contract and execute its default method
|
// Deploy a structs method invoker contract and execute its default method
|
||||||
|
|
@ -608,12 +610,12 @@ var bindTests = []struct {
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
// Create a simulator and wrap a non-deployed contract
|
// Create a simulator and wrap a non-deployed contract
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000))
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{}, uint64(10000000000))
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
nonexistent, err := NewNonExistent(common.Address{}, sim)
|
nonexistent, err := NewNonExistent(common.Address{}, sim)
|
||||||
|
|
@ -647,12 +649,12 @@ var bindTests = []struct {
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
// Create a simulator and wrap a non-deployed contract
|
// Create a simulator and wrap a non-deployed contract
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000))
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{}, uint64(10000000000))
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
nonexistent, err := NewNonExistentStruct(common.Address{}, sim)
|
nonexistent, err := NewNonExistentStruct(common.Address{}, sim)
|
||||||
|
|
@ -694,7 +696,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -702,7 +704,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy a funky gas pattern contract
|
// Deploy a funky gas pattern contract
|
||||||
|
|
@ -744,7 +746,7 @@ var bindTests = []struct {
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -752,7 +754,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy a sender tester contract and execute a structured call on it
|
// Deploy a sender tester contract and execute a structured call on it
|
||||||
|
|
@ -819,7 +821,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -827,7 +829,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy a underscorer tester contract and execute a structured call on it
|
// Deploy a underscorer tester contract and execute a structured call on it
|
||||||
|
|
@ -913,7 +915,7 @@ var bindTests = []struct {
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -921,7 +923,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy an eventer contract
|
// Deploy an eventer contract
|
||||||
|
|
@ -1103,7 +1105,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -1111,7 +1113,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
//deploy the test contract
|
//deploy the test contract
|
||||||
|
|
@ -1238,7 +1240,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
|
|
||||||
|
|
@ -1246,7 +1248,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
_, _, contract, err := DeployTuple(auth, sim)
|
_, _, contract, err := DeployTuple(auth, sim)
|
||||||
|
|
@ -1380,7 +1382,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -1388,7 +1390,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
//deploy the test contract
|
//deploy the test contract
|
||||||
|
|
@ -1446,14 +1448,14 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
// Initialize test accounts
|
// Initialize test accounts
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// deploy the test contract
|
// deploy the test contract
|
||||||
|
|
@ -1535,7 +1537,7 @@ var bindTests = []struct {
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
// Initialize test accounts
|
// Initialize test accounts
|
||||||
|
|
@ -1543,7 +1545,7 @@ var bindTests = []struct {
|
||||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
|
||||||
// Deploy registrar contract
|
// Deploy registrar contract
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
@ -1598,14 +1600,14 @@ var bindTests = []struct {
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
|
||||||
// Deploy registrar contract
|
// Deploy registrar contract
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
@ -1659,7 +1661,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
|
@ -1667,7 +1669,7 @@ var bindTests = []struct {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
// Deploy a tester contract and execute a structured call on it
|
// Deploy a tester contract and execute a structured call on it
|
||||||
|
|
@ -1720,14 +1722,14 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 1000000)
|
sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 1000000)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
|
|
@ -1808,7 +1810,7 @@ var bindTests = []struct {
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
`,
|
`,
|
||||||
|
|
@ -1816,7 +1818,7 @@ var bindTests = []struct {
|
||||||
var (
|
var (
|
||||||
key, _ = crypto.GenerateKey()
|
key, _ = crypto.GenerateKey()
|
||||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||||
)
|
)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
|
|
@ -1874,11 +1876,12 @@ var bindTests = []struct {
|
||||||
[]string{"0x6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063726c638214602d575b600080fd5b60336035565b005b60405163024876cd60e61b815260016004820152600260248201526003604482015260640160405180910390fdfea264697066735822122093f786a1bc60216540cd999fbb4a6109e0fef20abcff6e9107fb2817ca968f3c64736f6c63430008070033"},
|
[]string{"0x6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063726c638214602d575b600080fd5b60336035565b005b60405163024876cd60e61b815260016004820152600260248201526003604482015260640160405180910390fdfea264697066735822122093f786a1bc60216540cd999fbb4a6109e0fef20abcff6e9107fb2817ca968f3c64736f6c63430008070033"},
|
||||||
[]string{`[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError1","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError2","type":"error"},{"inputs":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"},{"internalType":"uint256","name":"c","type":"uint256"}],"name":"MyError3","type":"error"},{"inputs":[],"name":"Error","outputs":[],"stateMutability":"pure","type":"function"}]`},
|
[]string{`[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError1","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError2","type":"error"},{"inputs":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"},{"internalType":"uint256","name":"c","type":"uint256"}],"name":"MyError3","type":"error"},{"inputs":[],"name":"Error","outputs":[],"stateMutability":"pure","type":"function"}]`},
|
||||||
`
|
`
|
||||||
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
`,
|
`,
|
||||||
|
|
@ -1886,7 +1889,7 @@ var bindTests = []struct {
|
||||||
var (
|
var (
|
||||||
key, _ = crypto.GenerateKey()
|
key, _ = crypto.GenerateKey()
|
||||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||||
)
|
)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
|
|
@ -1895,7 +1898,7 @@ var bindTests = []struct {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
_, err = bind.WaitDeployed(nil, sim, tx)
|
_, err = bind.WaitDeployed(context.Background(), sim, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
@ -1926,11 +1929,12 @@ var bindTests = []struct {
|
||||||
bytecode: []string{`0x608060405234801561001057600080fd5b506040516101c43803806101c48339818101604052810190610032919061014a565b50610177565b6000604051905090565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6100958261004c565b810181811067ffffffffffffffff821117156100b4576100b361005d565b5b80604052505050565b60006100c7610038565b90506100d3828261008c565b919050565b6000819050919050565b6100eb816100d8565b81146100f657600080fd5b50565b600081519050610108816100e2565b92915050565b60006020828403121561012457610123610047565b5b61012e60206100bd565b9050600061013e848285016100f9565b60008301525092915050565b6000602082840312156101605761015f610042565b5b600061016e8482850161010e565b91505092915050565b603f806101856000396000f3fe6080604052600080fdfea2646970667358221220cdffa667affecefac5561f65f4a4ba914204a8d4eb859d8cd426fb306e5c12a364736f6c634300080a0033`},
|
bytecode: []string{`0x608060405234801561001057600080fd5b506040516101c43803806101c48339818101604052810190610032919061014a565b50610177565b6000604051905090565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6100958261004c565b810181811067ffffffffffffffff821117156100b4576100b361005d565b5b80604052505050565b60006100c7610038565b90506100d3828261008c565b919050565b6000819050919050565b6100eb816100d8565b81146100f657600080fd5b50565b600081519050610108816100e2565b92915050565b60006020828403121561012457610123610047565b5b61012e60206100bd565b9050600061013e848285016100f9565b60008301525092915050565b6000602082840312156101605761015f610042565b5b600061016e8482850161010e565b91505092915050565b603f806101856000396000f3fe6080604052600080fdfea2646970667358221220cdffa667affecefac5561f65f4a4ba914204a8d4eb859d8cd426fb306e5c12a364736f6c634300080a0033`},
|
||||||
abi: []string{`[{"inputs":[{"components":[{"internalType":"uint256","name":"field","type":"uint256"}],"internalType":"struct ConstructorWithStructParam.StructType","name":"st","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"}]`},
|
abi: []string{`[{"inputs":[{"components":[{"internalType":"uint256","name":"field","type":"uint256"}],"internalType":"struct ConstructorWithStructParam.StructType","name":"st","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"}]`},
|
||||||
imports: `
|
imports: `
|
||||||
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
`,
|
`,
|
||||||
|
|
@ -1938,7 +1942,7 @@ var bindTests = []struct {
|
||||||
var (
|
var (
|
||||||
key, _ = crypto.GenerateKey()
|
key, _ = crypto.GenerateKey()
|
||||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||||
)
|
)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
|
|
@ -1948,7 +1952,7 @@ var bindTests = []struct {
|
||||||
}
|
}
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
if _, err = bind.WaitDeployed(nil, sim, tx); err != nil {
|
if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil {
|
||||||
t.Logf("Deployment tx: %+v", tx)
|
t.Logf("Deployment tx: %+v", tx)
|
||||||
t.Errorf("bind.WaitDeployed(nil, %T, <deployment tx>) got err %v; want nil err", sim, err)
|
t.Errorf("bind.WaitDeployed(nil, %T, <deployment tx>) got err %v; want nil err", sim, err)
|
||||||
}
|
}
|
||||||
|
|
@ -1974,11 +1978,12 @@ var bindTests = []struct {
|
||||||
bytecode: []string{"0x608060405234801561001057600080fd5b5061042b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063c2bb515f1461003b578063cce7b04814610059575b600080fd5b610043610075565b60405161005091906101af565b60405180910390f35b610073600480360381019061006e91906103ac565b6100b5565b005b61007d6100b8565b604051806040016040528060405180602001604052806000815250815260200160405180602001604052806000815250815250905090565b50565b604051806040016040528060608152602001606081525090565b600081519050919050565b600082825260208201905092915050565b60005b8381101561010c5780820151818401526020810190506100f1565b8381111561011b576000848401525b50505050565b6000601f19601f8301169050919050565b600061013d826100d2565b61014781856100dd565b93506101578185602086016100ee565b61016081610121565b840191505092915050565b600060408301600083015184820360008601526101888282610132565b915050602083015184820360208601526101a28282610132565b9150508091505092915050565b600060208201905081810360008301526101c9818461016b565b905092915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61022282610121565b810181811067ffffffffffffffff82111715610241576102406101ea565b5b80604052505050565b60006102546101d1565b90506102608282610219565b919050565b600080fd5b600080fd5b600080fd5b600067ffffffffffffffff82111561028f5761028e6101ea565b5b61029882610121565b9050602081019050919050565b82818337600083830152505050565b60006102c76102c284610274565b61024a565b9050828152602081018484840111156102e3576102e261026f565b5b6102ee8482856102a5565b509392505050565b600082601f83011261030b5761030a61026a565b5b813561031b8482602086016102b4565b91505092915050565b60006040828403121561033a576103396101e5565b5b610344604061024a565b9050600082013567ffffffffffffffff81111561036457610363610265565b5b610370848285016102f6565b600083015250602082013567ffffffffffffffff81111561039457610393610265565b5b6103a0848285016102f6565b60208301525092915050565b6000602082840312156103c2576103c16101db565b5b600082013567ffffffffffffffff8111156103e0576103df6101e0565b5b6103ec84828501610324565b9150509291505056fea264697066735822122033bca1606af9b6aeba1673f98c52003cec19338539fb44b86690ce82c51483b564736f6c634300080e0033"},
|
bytecode: []string{"0x608060405234801561001057600080fd5b5061042b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063c2bb515f1461003b578063cce7b04814610059575b600080fd5b610043610075565b60405161005091906101af565b60405180910390f35b610073600480360381019061006e91906103ac565b6100b5565b005b61007d6100b8565b604051806040016040528060405180602001604052806000815250815260200160405180602001604052806000815250815250905090565b50565b604051806040016040528060608152602001606081525090565b600081519050919050565b600082825260208201905092915050565b60005b8381101561010c5780820151818401526020810190506100f1565b8381111561011b576000848401525b50505050565b6000601f19601f8301169050919050565b600061013d826100d2565b61014781856100dd565b93506101578185602086016100ee565b61016081610121565b840191505092915050565b600060408301600083015184820360008601526101888282610132565b915050602083015184820360208601526101a28282610132565b9150508091505092915050565b600060208201905081810360008301526101c9818461016b565b905092915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61022282610121565b810181811067ffffffffffffffff82111715610241576102406101ea565b5b80604052505050565b60006102546101d1565b90506102608282610219565b919050565b600080fd5b600080fd5b600080fd5b600067ffffffffffffffff82111561028f5761028e6101ea565b5b61029882610121565b9050602081019050919050565b82818337600083830152505050565b60006102c76102c284610274565b61024a565b9050828152602081018484840111156102e3576102e261026f565b5b6102ee8482856102a5565b509392505050565b600082601f83011261030b5761030a61026a565b5b813561031b8482602086016102b4565b91505092915050565b60006040828403121561033a576103396101e5565b5b610344604061024a565b9050600082013567ffffffffffffffff81111561036457610363610265565b5b610370848285016102f6565b600083015250602082013567ffffffffffffffff81111561039457610393610265565b5b6103a0848285016102f6565b60208301525092915050565b6000602082840312156103c2576103c16101db565b5b600082013567ffffffffffffffff8111156103e0576103df6101e0565b5b6103ec84828501610324565b9150509291505056fea264697066735822122033bca1606af9b6aeba1673f98c52003cec19338539fb44b86690ce82c51483b564736f6c634300080e0033"},
|
||||||
abi: []string{`[ { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "int256", "name": "msg", "type": "int256" }, { "indexed": false, "internalType": "int256", "name": "_msg", "type": "int256" } ], "name": "log", "type": "event" }, { "inputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "req", "type": "tuple" } ], "name": "addRequest", "outputs": [], "stateMutability": "pure", "type": "function" }, { "inputs": [], "name": "getRequest", "outputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "", "type": "tuple" } ], "stateMutability": "pure", "type": "function" } ]`},
|
abi: []string{`[ { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "int256", "name": "msg", "type": "int256" }, { "indexed": false, "internalType": "int256", "name": "_msg", "type": "int256" } ], "name": "log", "type": "event" }, { "inputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "req", "type": "tuple" } ], "name": "addRequest", "outputs": [], "stateMutability": "pure", "type": "function" }, { "inputs": [], "name": "getRequest", "outputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "", "type": "tuple" } ], "stateMutability": "pure", "type": "function" } ]`},
|
||||||
imports: `
|
imports: `
|
||||||
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
`,
|
`,
|
||||||
|
|
@ -1986,7 +1991,7 @@ var bindTests = []struct {
|
||||||
var (
|
var (
|
||||||
key, _ = crypto.GenerateKey()
|
key, _ = crypto.GenerateKey()
|
||||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||||
)
|
)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
|
|
@ -1996,7 +2001,7 @@ var bindTests = []struct {
|
||||||
}
|
}
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
if _, err = bind.WaitDeployed(nil, sim, tx); err != nil {
|
if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil {
|
||||||
t.Logf("Deployment tx: %+v", tx)
|
t.Logf("Deployment tx: %+v", tx)
|
||||||
t.Errorf("bind.WaitDeployed(nil, %T, <deployment tx>) got err %v; want nil err", sim, err)
|
t.Errorf("bind.WaitDeployed(nil, %T, <deployment tx>) got err %v; want nil err", sim, err)
|
||||||
}
|
}
|
||||||
|
|
@ -2014,11 +2019,12 @@ var bindTests = []struct {
|
||||||
bytecode: []string{"0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063527a119f14602d575b600080fd5b60436004803603810190603f9190605b565b6045565b005b50565b6000813590506055816092565b92915050565b600060208284031215606e57606d608d565b5b6000607a848285016048565b91505092915050565b6000819050919050565b600080fd5b6099816083565b811460a357600080fd5b5056fea2646970667358221220d4f4525e2615516394055d369fb17df41c359e5e962734f27fd683ea81fd9db164736f6c63430008070033"},
|
bytecode: []string{"0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063527a119f14602d575b600080fd5b60436004803603810190603f9190605b565b6045565b005b50565b6000813590506055816092565b92915050565b600060208284031215606e57606d608d565b5b6000607a848285016048565b91505092915050565b6000819050919050565b600080fd5b6099816083565b811460a357600080fd5b5056fea2646970667358221220d4f4525e2615516394055d369fb17df41c359e5e962734f27fd683ea81fd9db164736f6c63430008070033"},
|
||||||
abi: []string{`[{"inputs":[{"internalType":"uint256","name":"range","type":"uint256"}],"name":"functionWithKeywordParameter","outputs":[],"stateMutability":"pure","type":"function"}]`},
|
abi: []string{`[{"inputs":[{"internalType":"uint256","name":"range","type":"uint256"}],"name":"functionWithKeywordParameter","outputs":[],"stateMutability":"pure","type":"function"}]`},
|
||||||
imports: `
|
imports: `
|
||||||
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
`,
|
`,
|
||||||
|
|
@ -2026,7 +2032,7 @@ var bindTests = []struct {
|
||||||
var (
|
var (
|
||||||
key, _ = crypto.GenerateKey()
|
key, _ = crypto.GenerateKey()
|
||||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||||
)
|
)
|
||||||
_, tx, _, err := DeployRangeKeyword(user, sim)
|
_, tx, _, err := DeployRangeKeyword(user, sim)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -2034,7 +2040,7 @@ var bindTests = []struct {
|
||||||
}
|
}
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
if _, err = bind.WaitDeployed(nil, sim, tx); err != nil {
|
if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil {
|
||||||
t.Errorf("error deploying the contract: %v", err)
|
t.Errorf("error deploying the contract: %v", err)
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient/simulated"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -56,20 +55,19 @@ var waitDeployedTests = map[string]struct {
|
||||||
func TestWaitDeployed(t *testing.T) {
|
func TestWaitDeployed(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
for name, test := range waitDeployedTests {
|
for name, test := range waitDeployedTests {
|
||||||
backend := backends.NewSimulatedBackend(
|
backend := simulated.NewBackend(
|
||||||
core.GenesisAlloc{
|
types.GenesisAlloc{
|
||||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
||||||
},
|
},
|
||||||
10000000,
|
|
||||||
)
|
)
|
||||||
defer backend.Close()
|
defer backend.Close()
|
||||||
|
|
||||||
// Create the transaction
|
// Create the transaction
|
||||||
head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
head, _ := backend.Client().HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei))
|
||||||
|
|
||||||
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
|
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
|
||||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
tx, _ = types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(1337)), testKey)
|
||||||
|
|
||||||
// Wait for it to get mined in the background.
|
// Wait for it to get mined in the background.
|
||||||
var (
|
var (
|
||||||
|
|
@ -80,13 +78,13 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
address, err = bind.WaitDeployed(ctx, backend, tx)
|
address, err = bind.WaitDeployed(ctx, backend.Client(), tx)
|
||||||
|
|
||||||
close(mined)
|
close(mined)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Send and mine the transaction.
|
// Send and mine the transaction.
|
||||||
backend.SendTransaction(ctx, tx)
|
backend.Client().SendTransaction(ctx, tx)
|
||||||
backend.Commit()
|
backend.Commit()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
@ -105,43 +103,41 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWaitDeployedCornerCases(t *testing.T) {
|
func TestWaitDeployedCornerCases(t *testing.T) {
|
||||||
t.Parallel()
|
backend := simulated.NewBackend(
|
||||||
backend := backends.NewSimulatedBackend(
|
types.GenesisAlloc{
|
||||||
core.GenesisAlloc{
|
|
||||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
||||||
},
|
},
|
||||||
10000000,
|
|
||||||
)
|
)
|
||||||
defer backend.Close()
|
defer backend.Close()
|
||||||
|
|
||||||
head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
head, _ := backend.Client().HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
// Create a transaction to an account.
|
// Create a transaction to an account.
|
||||||
code := "6060604052600a8060106000396000f360606040526008565b00"
|
code := "6060604052600a8060106000396000f360606040526008565b00"
|
||||||
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
|
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
|
||||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
tx, _ = types.SignTx(tx, types.LatestSigner(params.AllDevChainProtocolChanges), testKey)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
backend.SendTransaction(ctx, tx)
|
backend.Client().SendTransaction(ctx, tx)
|
||||||
backend.Commit()
|
backend.Commit()
|
||||||
notContractCreation := errors.New("tx is not contract creation")
|
notContractCreation := errors.New("tx is not contract creation")
|
||||||
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContractCreation.Error() {
|
if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != notContractCreation.Error() {
|
||||||
t.Errorf("error mismatch: want %q, got %q, ", notContractCreation, err)
|
t.Errorf("error mismatch: want %q, got %q, ", notContractCreation, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a transaction that is not mined.
|
// Create a transaction that is not mined.
|
||||||
tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
|
tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
|
||||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
tx, _ = types.SignTx(tx, types.LatestSigner(params.AllDevChainProtocolChanges), testKey)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
contextCanceled := errors.New("context canceled")
|
contextCanceled := errors.New("context canceled")
|
||||||
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != contextCanceled.Error() {
|
if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != contextCanceled.Error() {
|
||||||
t.Errorf("error mismatch: want %q, got %q, ", contextCanceled, err)
|
t.Errorf("error mismatch: want %q, got %q, ", contextCanceled, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
backend.SendTransaction(ctx, tx)
|
backend.Client().SendTransaction(ctx, tx)
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -42,8 +43,7 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||||
case common.Address:
|
case common.Address:
|
||||||
copy(topic[common.HashLength-common.AddressLength:], rule[:])
|
copy(topic[common.HashLength-common.AddressLength:], rule[:])
|
||||||
case *big.Int:
|
case *big.Int:
|
||||||
blob := rule.Bytes()
|
copy(topic[:], math.U256Bytes(rule))
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case bool:
|
case bool:
|
||||||
if rule {
|
if rule {
|
||||||
topic[common.HashLength-1] = 1
|
topic[common.HashLength-1] = 1
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -56,9 +57,27 @@ func TestMakeTopics(t *testing.T) {
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"support *big.Int types in topics",
|
"support positive *big.Int types in topics",
|
||||||
args{[][]interface{}{{big.NewInt(1).Lsh(big.NewInt(2), 254)}}},
|
args{[][]interface{}{
|
||||||
[][]common.Hash{{common.Hash{128}}},
|
{big.NewInt(1)},
|
||||||
|
{big.NewInt(1).Lsh(big.NewInt(2), 254)},
|
||||||
|
}},
|
||||||
|
[][]common.Hash{
|
||||||
|
{common.HexToHash("0000000000000000000000000000000000000000000000000000000000000001")},
|
||||||
|
{common.Hash{128}},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"support negative *big.Int types in topics",
|
||||||
|
args{[][]interface{}{
|
||||||
|
{big.NewInt(-1)},
|
||||||
|
{big.NewInt(math.MinInt64)},
|
||||||
|
}},
|
||||||
|
[][]common.Hash{
|
||||||
|
{common.MaxHash},
|
||||||
|
{common.HexToHash("ffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000")},
|
||||||
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ func (ks keyStorePassphrase) JoinPath(filename string) string {
|
||||||
return filepath.Join(ks.keysDirPath, filename)
|
return filepath.Join(ks.keysDirPath, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encryptdata encrypts the data given as 'data' with the password 'auth'.
|
// EncryptDataV3 encrypts the data given as 'data' with the password 'auth'.
|
||||||
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
|
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
|
||||||
salt := make([]byte, 32)
|
salt := make([]byte, 32)
|
||||||
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,9 @@ func NewManager(config *Config, backends ...Backend) *Manager {
|
||||||
|
|
||||||
// Close terminates the account manager's internal notification processes.
|
// Close terminates the account manager's internal notification processes.
|
||||||
func (am *Manager) Close() error {
|
func (am *Manager) Close() error {
|
||||||
|
for _, w := range am.wallets {
|
||||||
|
w.Close()
|
||||||
|
}
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
am.quit <- errc
|
am.quit <- errc
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,7 @@ func (hub *Hub) refreshWallets() {
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Card connected, start tracking in amongst the wallets
|
// Card connected, start tracking among the wallets
|
||||||
hub.wallets[reader] = wallet
|
hub.wallets[reader] = wallet
|
||||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
// This file contains the implementation for interacting with the Trezor hardware
|
// This file contains the implementation for interacting with the Trezor hardware
|
||||||
// wallets. The wire protocol spec can be found on the SatoshiLabs website:
|
// wallets. The wire protocol spec can be found on the SatoshiLabs website:
|
||||||
// https://wiki.trezor.io/Developers_guide-Message_Workflows
|
// https://docs.trezor.io/trezor-firmware/common/message-workflows.html
|
||||||
|
|
||||||
// !!! STAHP !!!
|
// !!! STAHP !!!
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -505,6 +505,10 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
||||||
w.stateLock.Lock()
|
w.stateLock.Lock()
|
||||||
defer w.stateLock.Unlock()
|
defer w.stateLock.Unlock()
|
||||||
|
|
||||||
|
if w.device == nil {
|
||||||
|
return accounts.Account{}, accounts.ErrWalletClosed
|
||||||
|
}
|
||||||
|
|
||||||
if _, ok := w.paths[address]; !ok {
|
if _, ok := w.paths[address]; !ok {
|
||||||
w.accounts = append(w.accounts, account)
|
w.accounts = append(w.accounts, account)
|
||||||
w.paths[address] = make(accounts.DerivationPath, len(path))
|
w.paths[address] = make(accounts.DerivationPath, len(path))
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,16 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PayloadVersion denotes the version of PayloadAttributes used to request the
|
||||||
|
// building of the payload to commence.
|
||||||
|
type PayloadVersion byte
|
||||||
|
|
||||||
|
var (
|
||||||
|
PayloadV1 PayloadVersion = 0x1
|
||||||
|
PayloadV2 PayloadVersion = 0x2
|
||||||
|
PayloadV3 PayloadVersion = 0x3
|
||||||
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go
|
//go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go
|
||||||
|
|
||||||
// PayloadAttributes describes the environment context in which a block should
|
// PayloadAttributes describes the environment context in which a block should
|
||||||
|
|
@ -115,6 +125,21 @@ type TransitionConfigurationV1 struct {
|
||||||
// PayloadID is an identifier of the payload build process
|
// PayloadID is an identifier of the payload build process
|
||||||
type PayloadID [8]byte
|
type PayloadID [8]byte
|
||||||
|
|
||||||
|
// Version returns the payload version associated with the identifier.
|
||||||
|
func (b PayloadID) Version() PayloadVersion {
|
||||||
|
return PayloadVersion(b[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is returns whether the identifier matches any of provided payload versions.
|
||||||
|
func (b PayloadID) Is(versions ...PayloadVersion) bool {
|
||||||
|
for _, v := range versions {
|
||||||
|
if v == b.Version() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (b PayloadID) String() string {
|
func (b PayloadID) String() string {
|
||||||
return hexutil.Encode(b[:])
|
return hexutil.Encode(b[:])
|
||||||
}
|
}
|
||||||
|
|
@ -289,3 +314,21 @@ type ExecutionPayloadBodyV1 struct {
|
||||||
TransactionData []hexutil.Bytes `json:"transactions"`
|
TransactionData []hexutil.Bytes `json:"transactions"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Client identifiers to support ClientVersionV1.
|
||||||
|
const (
|
||||||
|
ClientCode = "GE"
|
||||||
|
ClientName = "go-ethereum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientVersionV1 contains information which identifies a client implementation.
|
||||||
|
type ClientVersionV1 struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Name string `json:"clientName"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Commit string `json:"commit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *ClientVersionV1) String() string {
|
||||||
|
return fmt.Sprintf("%s-%s-%s-%s", v.Code, v.Name, v.Version, v.Commit)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
# This file contains sha256 checksums of optional build dependencies.
|
# This file contains sha256 checksums of optional build dependencies.
|
||||||
|
|
||||||
# version:spec-tests 1.0.6
|
# version:spec-tests 2.1.0
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases
|
# https://github.com/ethereum/execution-spec-tests/releases
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v1.0.6/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
||||||
485af7b66cf41eb3a8c1bd46632913b8eb95995df867cf665617bbc9b4beedd1 fixtures_develop.tar.gz
|
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
||||||
|
|
||||||
# version:golang 1.22.1
|
# version:golang 1.22.1
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
|
|
@ -22,35 +22,36 @@ ac775e19d93cc1668999b77cfe8c8964abfbc658718feccfe6e0eb87663cd668 go1.22.1.linux
|
||||||
cf9c66a208a106402a527f5b956269ca506cfe535fc388e828d249ea88ed28ba go1.22.1.windows-amd64.zip
|
cf9c66a208a106402a527f5b956269ca506cfe535fc388e828d249ea88ed28ba go1.22.1.windows-amd64.zip
|
||||||
85b8511b298c9f4199ecae26afafcc3d46155bac934d43f2357b9224bcaa310f go1.22.1.windows-arm64.zip
|
85b8511b298c9f4199ecae26afafcc3d46155bac934d43f2357b9224bcaa310f go1.22.1.windows-arm64.zip
|
||||||
|
|
||||||
# version:golangci 1.51.1
|
# version:golangci 1.55.2
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
# https://github.com/golangci/golangci-lint/releases/download/v1.51.1/
|
# https://github.com/golangci/golangci-lint/releases/download/v1.55.2/
|
||||||
fba08acc4027f69f07cef48fbff70b8a7ecdfaa1c2aba9ad3fb31d60d9f5d4bc golangci-lint-1.51.1-darwin-amd64.tar.gz
|
632e96e6d5294fbbe7b2c410a49c8fa01c60712a0af85a567de85bcc1623ea21 golangci-lint-1.55.2-darwin-amd64.tar.gz
|
||||||
75b8f0ff3a4e68147156be4161a49d4576f1be37a0b506473f8c482140c1e7f2 golangci-lint-1.51.1-darwin-arm64.tar.gz
|
234463f059249f82045824afdcdd5db5682d0593052f58f6a3039a0a1c3899f6 golangci-lint-1.55.2-darwin-arm64.tar.gz
|
||||||
e06b3459aaed356e1667580be00b05f41f3b2e29685d12cdee571c23e1edb414 golangci-lint-1.51.1-freebsd-386.tar.gz
|
2bdd105e2d4e003a9058c33a22bb191a1e0f30fa0790acca0d8fbffac1d6247c golangci-lint-1.55.2-freebsd-386.tar.gz
|
||||||
623ce2d0fa4d35cc2e8d69fa7334227ab592380962a13b4d9cdc77cf41db2008 golangci-lint-1.51.1-freebsd-amd64.tar.gz
|
e75056e8b082386676ce23eba455cf893931a792c0d87e1e3743c0aec33c7fb5 golangci-lint-1.55.2-freebsd-amd64.tar.gz
|
||||||
131365feb0584cc2736c43192fa673ca50e5b6b765456990cb379ecfb787e568 golangci-lint-1.51.1-freebsd-armv6.tar.gz
|
5789b933facaf6136bd23f1d50add67b79bbcf8dfdfc9069a37f729395940a66 golangci-lint-1.55.2-freebsd-armv6.tar.gz
|
||||||
98fb627927cbb654f5bf85dcffc5f646666b2ce96ea0fed977c9fb28abd51532 golangci-lint-1.51.1-freebsd-armv7.tar.gz
|
7f21ab1008d05f32c954f99470fc86a83a059e530fe2add1d0b7d8ed4d8992a7 golangci-lint-1.55.2-freebsd-armv7.tar.gz
|
||||||
b36a99702fa762c15840261bc0fb41b4b1b16b8b19b8c0941bae98c85bb0f8b8 golangci-lint-1.51.1-linux-386.tar.gz
|
33ab06139b9219a28251f10821da94423db30285cc2af97494cbb2a281927de9 golangci-lint-1.55.2-illumos-amd64.tar.gz
|
||||||
17aeb26c76820c22efa0e1838b0ab93e90cfedef43fbfc9a2f33f27eb9e5e070 golangci-lint-1.51.1-linux-amd64.tar.gz
|
57ce6f8ce3ad6ee45d7cc3d9a047545a851c2547637834a3fcb086c7b40b1e6b golangci-lint-1.55.2-linux-386.tar.gz
|
||||||
9744bc34e7b8d82ca788b667bfb7155a39b4be9aef43bf9f10318b1372cea338 golangci-lint-1.51.1-linux-arm64.tar.gz
|
ca21c961a33be3bc15e4292dc40c98c8dcc5463a7b6768a3afc123761630c09c golangci-lint-1.55.2-linux-amd64.tar.gz
|
||||||
0dda8dbeb2ff7455a044ec8e347f2fc6d655d2e99d281b3b95e88167031c673d golangci-lint-1.51.1-linux-armv6.tar.gz
|
8eb0cee9b1dbf0eaa49871798c7f8a5b35f2960c52d776a5f31eb7d886b92746 golangci-lint-1.55.2-linux-arm64.tar.gz
|
||||||
0512f311b11d43b8b22989d929f0fe8a2e1e5ebe497f1eb0ff73a0fc3d188fd1 golangci-lint-1.51.1-linux-armv7.tar.gz
|
3195f3e0f37d353fd5bd415cabcd4e263f5c29d3d0ffb176c26ff3d2c75eb3bb golangci-lint-1.55.2-linux-armv6.tar.gz
|
||||||
d767108dcf84a8eaa844df3454cb0f75a492f4e7102ecc2b0a3545cfe073a566 golangci-lint-1.51.1-linux-loong64.tar.gz
|
c823ee36eb1a719e171de1f2f5ca3068033dce8d9817232fd10ed71fd6650406 golangci-lint-1.55.2-linux-armv7.tar.gz
|
||||||
3bd56c54daec16585b2668e0dfabb27af2c2b38cc0fdb46923e2521e1634846b golangci-lint-1.51.1-linux-mips64.tar.gz
|
758a5d2a356dc494bd13ed4c0d4bf5a54a4dc91267ea5ecdd87b86c7ca0624e7 golangci-lint-1.55.2-linux-loong64.tar.gz
|
||||||
f72f5adfa2219e15d2414c9a2966f86e74556cf17a85c727a7fb7770a16cf814 golangci-lint-1.51.1-linux-mips64le.tar.gz
|
2c7b9abdce7cae802a67d583cd7c6dca520bff6d0e17c8535a918e2f2b437aa0 golangci-lint-1.55.2-linux-mips64.tar.gz
|
||||||
e605521dac98096d8737e1997c954f41f1d0d8275b8731f62783d410c23574b9 golangci-lint-1.51.1-linux-ppc64le.tar.gz
|
024e0a15b85352cc27271285526e16a4ab66d3e67afbbe446c9808c06cb8dbed golangci-lint-1.55.2-linux-mips64le.tar.gz
|
||||||
2f683217b814339e74d61ca700922d8407f15addd6d4c5e8b156fbab79f26a87 golangci-lint-1.51.1-linux-riscv64.tar.gz
|
6b00f89ba5506c1de1efdd9fa17c54093013a294fefd8b9b31534db626a672ee golangci-lint-1.55.2-linux-ppc64le.tar.gz
|
||||||
d98528292b65971a3594e5880530e7624597dc9806fcfccdfbe39be411713d63 golangci-lint-1.51.1-linux-s390x.tar.gz
|
0faa0d047d9bf7b703ed3ea65b6117043c93504f9ca1de25ae929d3901c73d4a golangci-lint-1.55.2-linux-riscv64.tar.gz
|
||||||
9bb2d0fe9e692ed0aea4f2537e3e6862b2f6768fe2849a84f4a6ad09da9fd971 golangci-lint-1.51.1-netbsd-386.tar.gz
|
30dec9b22e7d5bb4e9d5ccea96da20f71cd7db3c8cf30b8ddc7cb9174c4d742a golangci-lint-1.55.2-linux-s390x.tar.gz
|
||||||
34cafdcd11ae73ae88d66c33eb8449f5c976fc3e37b44774dbe9c71caa95e592 golangci-lint-1.51.1-netbsd-amd64.tar.gz
|
5a0ede48f79ad707902fdb29be8cd2abd8302dc122b65ebae3fdfc86751c7698 golangci-lint-1.55.2-netbsd-386.tar.gz
|
||||||
f8b4e1e47ac17caafe8a5f32f975a2b6a7cb14c27c0f73c1fb15c20ca91c2e03 golangci-lint-1.51.1-netbsd-armv6.tar.gz
|
95af20a2e617126dd5b08122ece7819101070e1582a961067ce8c41172f901ad golangci-lint-1.55.2-netbsd-amd64.tar.gz
|
||||||
c4f58b7e227b9fd41f0e9310dc83f4a4e7d026598e2f6e95b78761081a6d9bd2 golangci-lint-1.51.1-netbsd-armv7.tar.gz
|
94fb7dacb7527847cc95d7120904e19a2a0a81a0d50d61766c9e0251da72ab9d golangci-lint-1.55.2-netbsd-armv6.tar.gz
|
||||||
6710e2f5375dc75521c1a17980a6cbbe6ff76c2f8b852964a8af558899a97cf5 golangci-lint-1.51.1-windows-386.zip
|
ca906bce5fee9619400e4a321c56476fe4a4efb6ac4fc989d340eb5563348873 golangci-lint-1.55.2-netbsd-armv7.tar.gz
|
||||||
722d7b87b9cdda0a3835d5030b3fc5385c2eba4c107f63f6391cfb2ac35f051d golangci-lint-1.51.1-windows-amd64.zip
|
45b442f69fc8915c4500201c0247b7f3f69544dbc9165403a61f9095f2c57355 golangci-lint-1.55.2-windows-386.zip
|
||||||
eb57f9bcb56646f2e3d6ccaf02ec227815fb05077b2e0b1bf9e755805acdc2b9 golangci-lint-1.51.1-windows-arm64.zip
|
f57d434d231d43417dfa631587522f8c1991220b43c8ffadb9c7bd279508bf81 golangci-lint-1.55.2-windows-amd64.zip
|
||||||
bce02f7232723cb727755ee11f168a700a00896a25d37f87c4b173bce55596b4 golangci-lint-1.51.1-windows-armv6.zip
|
fd7dc8f4c6829ee6fafb252a4d81d2155cd35da7833665cbb25d53ce7cecd990 golangci-lint-1.55.2-windows-arm64.zip
|
||||||
cf6403f84707ce8c98664736772271bc8874f2e760c2fd0f00cf3e85963507e9 golangci-lint-1.51.1-windows-armv7.zip
|
1892c3c24f9e7ef44b02f6750c703864b6dc350129f3ec39510300007b2376f1 golangci-lint-1.55.2-windows-armv6.zip
|
||||||
|
a5e68ae73d38748b5269fad36ac7575e3c162a5dc63ef58abdea03cc5da4522a golangci-lint-1.55.2-windows-armv7.zip
|
||||||
|
|
||||||
# This is the builder on PPA that will build Go itself (inception-y), don't modify!
|
# This is the builder on PPA that will build Go itself (inception-y), don't modify!
|
||||||
#
|
#
|
||||||
|
|
|
||||||
16
build/ci.go
16
build/ci.go
|
|
@ -121,14 +121,14 @@ var (
|
||||||
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
||||||
// Note: the following Ubuntu releases have been officially deprecated on Launchpad:
|
// Note: the following Ubuntu releases have been officially deprecated on Launchpad:
|
||||||
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish,
|
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish,
|
||||||
// kinetic
|
// kinetic, lunar
|
||||||
debDistroGoBoots = map[string]string{
|
debDistroGoBoots = map[string]string{
|
||||||
"trusty": "golang-1.11", // EOL: 04/2024
|
"trusty": "golang-1.11", // 14.04, EOL: 04/2024
|
||||||
"xenial": "golang-go", // EOL: 04/2026
|
"xenial": "golang-go", // 16.04, EOL: 04/2026
|
||||||
"bionic": "golang-go", // EOL: 04/2028
|
"bionic": "golang-go", // 18.04, EOL: 04/2028
|
||||||
"focal": "golang-go", // EOL: 04/2030
|
"focal": "golang-go", // 20.04, EOL: 04/2030
|
||||||
"jammy": "golang-go", // EOL: 04/2032
|
"jammy": "golang-go", // 22.04, EOL: 04/2032
|
||||||
"lunar": "golang-go", // EOL: 01/2024
|
"mantic": "golang-go", // 23.10, EOL: 07/2024
|
||||||
}
|
}
|
||||||
|
|
||||||
debGoBootPaths = map[string]string{
|
debGoBootPaths = map[string]string{
|
||||||
|
|
@ -366,7 +366,7 @@ func doLint(cmdline []string) {
|
||||||
|
|
||||||
linter := downloadLinter(*cachedir)
|
linter := downloadLinter(*cachedir)
|
||||||
lflags := []string{"run", "--config", ".golangci.yml"}
|
lflags := []string{"run", "--config", ".golangci.yml"}
|
||||||
build.MustRunCommand(linter, append(lflags, packages...)...)
|
build.MustRunCommandWithOutput(linter, append(lflags, packages...)...)
|
||||||
fmt.Println("You have achieved perfection.")
|
fmt.Println("You have achieved perfection.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -916,7 +916,7 @@ There are a couple of implementation for a UI. We'll try to keep this list up to
|
||||||
|
|
||||||
| Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters|
|
| Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters|
|
||||||
| ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- |
|
| ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- |
|
||||||
| QtSigner| https://github.com/holiman/qtsigner/| Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)|
|
| QtSigner| https://github.com/holiman/qtsigner/ | Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)|
|
||||||
| GtkSigner| https://github.com/holiman/gtksigner| Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: |
|
| GtkSigner| https://github.com/holiman/gtksigner | Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: |
|
||||||
| Frame | https://github.com/floating/frame/commits/go-signer| Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: |
|
| Frame | https://github.com/floating/frame/commits/go-signer | Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: |
|
||||||
| Clef UI| https://github.com/ethereum/clef-ui| Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: | :+1: (approve tx only)|
|
| Clef UI| https://github.com/ethereum/clef-ui | Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: | :+1: (approve tx only)|
|
||||||
|
|
|
||||||
|
|
@ -784,6 +784,7 @@ func signer(c *cli.Context) error {
|
||||||
"light-kdf", lightKdf, "advanced", advanced)
|
"light-kdf", lightKdf, "advanced", advanced)
|
||||||
|
|
||||||
am := core.StartClefAccountManager(ksLoc, nousb, lightKdf, scpath)
|
am := core.StartClefAccountManager(ksLoc, nousb, lightKdf, scpath)
|
||||||
|
defer am.Close()
|
||||||
apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced, pwStorage)
|
apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced, pwStorage)
|
||||||
|
|
||||||
// Establish the bidirectional communication, by creating a new UI backend and registering
|
// Establish the bidirectional communication, by creating a new UI backend and registering
|
||||||
|
|
@ -817,7 +818,7 @@ func signer(c *cli.Context) error {
|
||||||
vhosts := utils.SplitAndTrim(c.String(utils.HTTPVirtualHostsFlag.Name))
|
vhosts := utils.SplitAndTrim(c.String(utils.HTTPVirtualHostsFlag.Name))
|
||||||
cors := utils.SplitAndTrim(c.String(utils.HTTPCORSDomainFlag.Name))
|
cors := utils.SplitAndTrim(c.String(utils.HTTPCORSDomainFlag.Name))
|
||||||
|
|
||||||
srv := rpc.NewServer("", 0, 0)
|
srv := rpc.NewServer()
|
||||||
srv.SetBatchLimits(node.DefaultConfig.BatchRequestLimit, node.DefaultConfig.BatchResponseMaxSize)
|
srv.SetBatchLimits(node.DefaultConfig.BatchRequestLimit, node.DefaultConfig.BatchResponseMaxSize)
|
||||||
err := node.RegisterApis(rpcAPI, []string{"account"}, srv)
|
err := node.RegisterApis(rpcAPI, []string{"account"}, srv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -108,31 +108,32 @@ Start the test by running `devp2p discv5 test -listen1 127.0.0.1 -listen2 127.0.
|
||||||
|
|
||||||
The Eth Protocol test suite is a conformance test suite for the [eth protocol][eth].
|
The Eth Protocol test suite is a conformance test suite for the [eth protocol][eth].
|
||||||
|
|
||||||
To run the eth protocol test suite against your implementation, the node needs to be initialized as such:
|
To run the eth protocol test suite against your implementation, the node needs to be initialized
|
||||||
|
with our test chain. The chain files are located in `./cmd/devp2p/internal/ethtest/testdata`.
|
||||||
|
|
||||||
1. initialize the geth node with the `genesis.json` file contained in the `testdata` directory
|
1. initialize the geth node with the `genesis.json` file
|
||||||
2. import the `halfchain.rlp` file in the `testdata` directory
|
2. import blocks from `chain.rlp`
|
||||||
3. run geth with the following flags:
|
3. run the client using the resulting database. For geth, use a command like the one below:
|
||||||
```
|
|
||||||
geth --datadir <datadir> --nodiscover --nat=none --networkid 19763 --verbosity 5
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, run the following command, replacing `<enode>` with the enode of the geth node:
|
geth \
|
||||||
```
|
--datadir <datadir> \
|
||||||
devp2p rlpx eth-test <enode> cmd/devp2p/internal/ethtest/testdata/chain.rlp cmd/devp2p/internal/ethtest/testdata/genesis.json
|
--nodiscover \
|
||||||
```
|
--nat=none \
|
||||||
|
--networkid 3503995874084926 \
|
||||||
|
--verbosity 5 \
|
||||||
|
--authrpc.jwtsecret 0x7365637265747365637265747365637265747365637265747365637265747365
|
||||||
|
|
||||||
|
Note that the tests also require access to the engine API.
|
||||||
|
The test suite can now be executed using the devp2p tool.
|
||||||
|
|
||||||
|
devp2p rlpx eth-test \
|
||||||
|
--chain internal/ethtest/testdata \
|
||||||
|
--node enode://.... \
|
||||||
|
--engineapi http://127.0.0.1:8551 \
|
||||||
|
--jwtsecret 0x7365637265747365637265747365637265747365637265747365637265747365
|
||||||
|
|
||||||
Repeat the above process (re-initialising the node) in order to run the Eth Protocol test suite again.
|
Repeat the above process (re-initialising the node) in order to run the Eth Protocol test suite again.
|
||||||
|
|
||||||
#### Eth66 Test Suite
|
|
||||||
|
|
||||||
The Eth66 test suite is also a conformance test suite for the eth 66 protocol version specifically.
|
|
||||||
To run the eth66 protocol test suite, initialize a geth node as described above and run the following command,
|
|
||||||
replacing `<enode>` with the enode of the geth node:
|
|
||||||
|
|
||||||
```
|
|
||||||
devp2p rlpx eth66-test <enode> cmd/devp2p/internal/ethtest/testdata/chain.rlp cmd/devp2p/internal/ethtest/testdata/genesis.json
|
|
||||||
```
|
|
||||||
|
|
||||||
[eth]: https://github.com/ethereum/devp2p/blob/master/caps/eth.md
|
[eth]: https://github.com/ethereum/devp2p/blob/master/caps/eth.md
|
||||||
[dns-tutorial]: https://geth.ethereum.org/docs/developers/geth-developer/dns-discovery-setup
|
[dns-tutorial]: https://geth.ethereum.org/docs/developers/geth-developer/dns-discovery-setup
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ func discv4Listen(ctx *cli.Context) error {
|
||||||
|
|
||||||
api := &discv4API{disc}
|
api := &discv4API{disc}
|
||||||
log.Info("Starting RPC API server", "addr", httpAddr)
|
log.Info("Starting RPC API server", "addr", httpAddr)
|
||||||
srv := rpc.NewServer("", 0, 0)
|
srv := rpc.NewServer()
|
||||||
srv.RegisterName("discv4", api)
|
srv.RegisterName("discv4", api)
|
||||||
http.DefaultServeMux.Handle("/", srv)
|
http.DefaultServeMux.Handle("/", srv)
|
||||||
httpsrv := http.Server{Addr: httpAddr, Handler: http.DefaultServeMux}
|
httpsrv := http.Server{Addr: httpAddr, Handler: http.DefaultServeMux}
|
||||||
|
|
|
||||||
|
|
@ -17,27 +17,118 @@
|
||||||
package ethtest
|
package ethtest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/forkid"
|
"github.com/ethereum/go-ethereum/core/forkid"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Chain is a lightweight blockchain-like store which can read a hivechain
|
||||||
|
// created chain.
|
||||||
type Chain struct {
|
type Chain struct {
|
||||||
genesis core.Genesis
|
genesis core.Genesis
|
||||||
blocks []*types.Block
|
blocks []*types.Block
|
||||||
chainConfig *params.ChainConfig
|
state map[common.Address]state.DumpAccount // state of head block
|
||||||
|
senders map[common.Address]*senderInfo
|
||||||
|
config *params.ChainConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChain takes the given chain.rlp file, and decodes and returns
|
||||||
|
// the blocks from the file.
|
||||||
|
func NewChain(dir string) (*Chain, error) {
|
||||||
|
gen, err := loadGenesis(path.Join(dir, "genesis.json"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
gblock := gen.ToBlock()
|
||||||
|
|
||||||
|
blocks, err := blocksFromFile(path.Join(dir, "chain.rlp"), gblock)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
state, err := readState(path.Join(dir, "headstate.json"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
accounts, err := readAccounts(path.Join(dir, "accounts.json"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Chain{
|
||||||
|
genesis: gen,
|
||||||
|
blocks: blocks,
|
||||||
|
state: state,
|
||||||
|
senders: accounts,
|
||||||
|
config: gen.Config,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// senderInfo is an account record as output in the "accounts.json" file from
|
||||||
|
// hivechain.
|
||||||
|
type senderInfo struct {
|
||||||
|
Key *ecdsa.PrivateKey `json:"key"`
|
||||||
|
Nonce uint64 `json:"nonce"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head returns the chain head.
|
||||||
|
func (c *Chain) Head() *types.Block {
|
||||||
|
return c.blocks[c.Len()-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountsInHashOrder returns all accounts of the head state, ordered by hash of address.
|
||||||
|
func (c *Chain) AccountsInHashOrder() []state.DumpAccount {
|
||||||
|
list := make([]state.DumpAccount, len(c.state))
|
||||||
|
i := 0
|
||||||
|
for addr, acc := range c.state {
|
||||||
|
addr := addr
|
||||||
|
list[i] = acc
|
||||||
|
list[i].Address = &addr
|
||||||
|
if len(acc.AddressHash) != 32 {
|
||||||
|
panic(fmt.Errorf("missing/invalid SecureKey in dump account %v", addr))
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
slices.SortFunc(list, func(x, y state.DumpAccount) int {
|
||||||
|
return bytes.Compare(x.AddressHash, y.AddressHash)
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeHashes returns all bytecode hashes contained in the head state.
|
||||||
|
func (c *Chain) CodeHashes() []common.Hash {
|
||||||
|
var hashes []common.Hash
|
||||||
|
seen := make(map[common.Hash]struct{})
|
||||||
|
seen[types.EmptyCodeHash] = struct{}{}
|
||||||
|
for _, acc := range c.state {
|
||||||
|
h := common.BytesToHash(acc.CodeHash)
|
||||||
|
if _, ok := seen[h]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hashes = append(hashes, h)
|
||||||
|
seen[h] = struct{}{}
|
||||||
|
}
|
||||||
|
slices.SortFunc(hashes, (common.Hash).Cmp)
|
||||||
|
return hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the length of the chain.
|
// Len returns the length of the chain.
|
||||||
|
|
@ -45,6 +136,11 @@ func (c *Chain) Len() int {
|
||||||
return len(c.blocks)
|
return len(c.blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForkID gets the fork id of the chain.
|
||||||
|
func (c *Chain) ForkID() forkid.ID {
|
||||||
|
return forkid.NewID(c.config, c.blocks[0], uint64(c.Len()), c.blocks[c.Len()-1].Time())
|
||||||
|
}
|
||||||
|
|
||||||
// TD calculates the total difficulty of the chain at the
|
// TD calculates the total difficulty of the chain at the
|
||||||
// chain head.
|
// chain head.
|
||||||
func (c *Chain) TD() *big.Int {
|
func (c *Chain) TD() *big.Int {
|
||||||
|
|
@ -56,63 +152,71 @@ func (c *Chain) TD() *big.Int {
|
||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
// TotalDifficultyAt calculates the total difficulty of the chain
|
// GetBlock returns the block at the specified number.
|
||||||
// at the given block height.
|
func (c *Chain) GetBlock(number int) *types.Block {
|
||||||
func (c *Chain) TotalDifficultyAt(height int) *big.Int {
|
return c.blocks[number]
|
||||||
sum := new(big.Int)
|
|
||||||
if height >= c.Len() {
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, block := range c.blocks[:height+1] {
|
|
||||||
sum.Add(sum, block.Difficulty())
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RootAt returns the state root for the block at the given height.
|
||||||
func (c *Chain) RootAt(height int) common.Hash {
|
func (c *Chain) RootAt(height int) common.Hash {
|
||||||
if height < c.Len() {
|
if height < c.Len() {
|
||||||
return c.blocks[height].Root()
|
return c.blocks[height].Root()
|
||||||
}
|
}
|
||||||
|
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForkID gets the fork id of the chain.
|
// GetSender returns the address associated with account at the index in the
|
||||||
func (c *Chain) ForkID() forkid.ID {
|
// pre-funded accounts list.
|
||||||
return forkid.NewID(c.chainConfig, c.blocks[0], uint64(c.Len()), c.blocks[0].Time())
|
func (c *Chain) GetSender(idx int) (common.Address, uint64) {
|
||||||
}
|
var accounts Addresses
|
||||||
|
for addr := range c.senders {
|
||||||
// Shorten returns a copy chain of a desired height from the imported
|
accounts = append(accounts, addr)
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
|
sort.Sort(accounts)
|
||||||
|
addr := accounts[idx]
|
||||||
|
return addr, c.senders[addr].Nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
// Head returns the chain head.
|
// IncNonce increases the specified signing account's pending nonce.
|
||||||
func (c *Chain) Head() *types.Block {
|
func (c *Chain) IncNonce(addr common.Address, amt uint64) {
|
||||||
return c.blocks[c.Len()-1]
|
if _, ok := c.senders[addr]; !ok {
|
||||||
|
panic("nonce increment for non-signer")
|
||||||
|
}
|
||||||
|
c.senders[addr].Nonce += amt
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:typecheck
|
// Balance returns the balance of an account at the head of the chain.
|
||||||
func (c *Chain) GetHeaders(req *GetBlockHeaders) ([]*types.Header, error) {
|
func (c *Chain) Balance(addr common.Address) *big.Int {
|
||||||
|
bal := new(big.Int)
|
||||||
|
if acc, ok := c.state[addr]; ok {
|
||||||
|
bal, _ = bal.SetString(acc.Balance, 10)
|
||||||
|
}
|
||||||
|
return bal
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignTx signs a transaction for the specified from account, so long as that
|
||||||
|
// account was in the hivechain accounts dump.
|
||||||
|
func (c *Chain) SignTx(from common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||||
|
signer := types.LatestSigner(c.config)
|
||||||
|
acc, ok := c.senders[from]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("account not available for signing: %s", from)
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.SignTx(tx, signer, acc.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeaders returns the headers base on an ethGetPacketHeadersPacket.
|
||||||
|
func (c *Chain) GetHeaders(req *eth.GetBlockHeadersPacket) ([]*types.Header, error) {
|
||||||
if req.Amount < 1 {
|
if req.Amount < 1 {
|
||||||
return nil, errors.New("no block headers requested")
|
return nil, errors.New("no block headers requested")
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
|
headers = make([]*types.Header, req.Amount)
|
||||||
|
blockNumber uint64
|
||||||
|
)
|
||||||
|
|
||||||
headers := make([]*types.Header, req.Amount)
|
// Range over blocks to check if our chain has the requested header.
|
||||||
|
|
||||||
var blockNumber uint64
|
|
||||||
|
|
||||||
// range over blocks to check if our chain has the requested header
|
|
||||||
for _, block := range c.blocks {
|
for _, block := range c.blocks {
|
||||||
if block.Hash() == req.Origin.Hash || block.Number().Uint64() == req.Origin.Number {
|
if block.Hash() == req.Origin.Hash || block.Number().Uint64() == req.Origin.Number {
|
||||||
headers[0] = block.Header()
|
headers[0] = block.Header()
|
||||||
|
|
@ -123,42 +227,31 @@ func (c *Chain) GetHeaders(req *GetBlockHeaders) ([]*types.Header, error) {
|
||||||
if headers[0] == nil {
|
if headers[0] == nil {
|
||||||
return nil, fmt.Errorf("no headers found for given origin number %v, hash %v", req.Origin.Number, req.Origin.Hash)
|
return nil, fmt.Errorf("no headers found for given origin number %v, hash %v", req.Origin.Number, req.Origin.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Reverse {
|
if req.Reverse {
|
||||||
for i := 1; i < int(req.Amount); i++ {
|
for i := 1; i < int(req.Amount); i++ {
|
||||||
blockNumber -= (1 - req.Skip)
|
blockNumber -= (1 - req.Skip)
|
||||||
headers[i] = c.blocks[blockNumber].Header()
|
headers[i] = c.blocks[blockNumber].Header()
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers, nil
|
return headers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 1; i < int(req.Amount); i++ {
|
for i := 1; i < int(req.Amount); i++ {
|
||||||
blockNumber += (1 + req.Skip)
|
blockNumber += (1 + req.Skip)
|
||||||
headers[i] = c.blocks[blockNumber].Header()
|
headers[i] = c.blocks[blockNumber].Header()
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers, nil
|
return headers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadChain takes the given chain.rlp file, and decodes and returns
|
// Shorten returns a copy chain of a desired height from the imported
|
||||||
// the blocks from the file.
|
func (c *Chain) Shorten(height int) *Chain {
|
||||||
func loadChain(chainfile string, genesis string) (*Chain, error) {
|
blocks := make([]*types.Block, height)
|
||||||
gen, err := loadGenesis(genesis)
|
copy(blocks, c.blocks[:height])
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
config := *c.config
|
||||||
|
|
||||||
|
return &Chain{
|
||||||
|
blocks: blocks,
|
||||||
|
config: &config,
|
||||||
}
|
}
|
||||||
|
|
||||||
gblock := gen.ToBlock()
|
|
||||||
|
|
||||||
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) {
|
func loadGenesis(genesisFile string) (core.Genesis, error) {
|
||||||
|
|
@ -175,6 +268,22 @@ func loadGenesis(genesisFile string) (core.Genesis, error) {
|
||||||
return gen, nil
|
return gen, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Addresses []common.Address
|
||||||
|
|
||||||
|
func (a Addresses) Len() int {
|
||||||
|
return len(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Addresses) Less(i, j int) bool {
|
||||||
|
return bytes.Compare(a[i][:], a[j][:]) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Addresses) Swap(i, j int) {
|
||||||
|
tmp := a[i]
|
||||||
|
a[i] = a[j]
|
||||||
|
a[j] = tmp
|
||||||
|
}
|
||||||
|
|
||||||
func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, error) {
|
func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, error) {
|
||||||
// Load chain.rlp.
|
// Load chain.rlp.
|
||||||
fh, err := os.Open(chainfile)
|
fh, err := os.Open(chainfile)
|
||||||
|
|
@ -213,3 +322,47 @@ func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, erro
|
||||||
|
|
||||||
return blocks, nil
|
return blocks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readState(file string) (map[common.Address]state.DumpAccount, error) {
|
||||||
|
f, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to read state: %v", err)
|
||||||
|
}
|
||||||
|
var dump state.Dump
|
||||||
|
if err := json.Unmarshal(f, &dump); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to unmarshal state: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
state := make(map[common.Address]state.DumpAccount)
|
||||||
|
for key, acct := range dump.Accounts {
|
||||||
|
var addr common.Address
|
||||||
|
if err := addr.UnmarshalText([]byte(key)); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid address %q", key)
|
||||||
|
}
|
||||||
|
state[addr] = acct
|
||||||
|
}
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAccounts(file string) (map[common.Address]*senderInfo, error) {
|
||||||
|
f, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to read state: %v", err)
|
||||||
|
}
|
||||||
|
type account struct {
|
||||||
|
Key hexutil.Bytes `json:"key"`
|
||||||
|
}
|
||||||
|
keys := make(map[common.Address]account)
|
||||||
|
if err := json.Unmarshal(f, &keys); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to unmarshal accounts: %v", err)
|
||||||
|
}
|
||||||
|
accounts := make(map[common.Address]*senderInfo)
|
||||||
|
for addr, acc := range keys {
|
||||||
|
pk, err := crypto.HexToECDSA(common.Bytes2Hex(acc.Key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to read private key for %s: %v", err, addr)
|
||||||
|
}
|
||||||
|
accounts[addr] = &senderInfo{Key: pk, Nonce: 0}
|
||||||
|
}
|
||||||
|
return accounts, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,31 +123,26 @@ func TestEthProtocolNegotiation(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestChain_GetHeaders tests whether the test suite can correctly
|
// TestChainGetHeaders tests whether the test suite can correctly
|
||||||
// respond to a GetBlockHeaders request from a node.
|
// respond to a GetBlockHeaders request from a node.
|
||||||
func TestChain_GetHeaders(t *testing.T) {
|
func TestChainGetHeaders(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
chainFile, err := filepath.Abs("./testdata/chain.rlp")
|
|
||||||
|
dir, err := filepath.Abs("./testdata")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
chain, err := NewChain(dir)
|
||||||
genesisFile, err := filepath.Abs("./testdata/genesis.json")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
chain, err := loadChain(chainFile, genesisFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
req GetBlockHeaders
|
req eth.GetBlockHeadersPacket
|
||||||
expected []*types.Header
|
expected []*types.Header
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
req: GetBlockHeaders{
|
req: eth.GetBlockHeadersPacket{
|
||||||
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
||||||
Origin: eth.HashOrNumber{Number: uint64(2)},
|
Origin: eth.HashOrNumber{Number: uint64(2)},
|
||||||
Amount: uint64(5),
|
Amount: uint64(5),
|
||||||
|
|
@ -164,7 +159,7 @@ func TestChain_GetHeaders(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
req: GetBlockHeaders{
|
req: eth.GetBlockHeadersPacket{
|
||||||
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
||||||
Origin: eth.HashOrNumber{Number: uint64(chain.Len() - 1)},
|
Origin: eth.HashOrNumber{Number: uint64(chain.Len() - 1)},
|
||||||
Amount: uint64(3),
|
Amount: uint64(3),
|
||||||
|
|
@ -179,7 +174,7 @@ func TestChain_GetHeaders(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
req: GetBlockHeaders{
|
req: eth.GetBlockHeadersPacket{
|
||||||
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
||||||
Origin: eth.HashOrNumber{Hash: chain.Head().Hash()},
|
Origin: eth.HashOrNumber{Hash: chain.Head().Hash()},
|
||||||
Amount: uint64(1),
|
Amount: uint64(1),
|
||||||
|
|
|
||||||
361
cmd/devp2p/internal/ethtest/conn.go
Normal file
361
cmd/devp2p/internal/ethtest/conn.go
Normal file
|
|
@ -0,0 +1,361 @@
|
||||||
|
// Copyright 2023 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 ethtest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/rlpx"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pretty = spew.ConfigState{
|
||||||
|
Indent: " ",
|
||||||
|
DisableCapacities: true,
|
||||||
|
DisablePointerAddresses: true,
|
||||||
|
SortKeys: true,
|
||||||
|
}
|
||||||
|
timeout = 2 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// dial attempts to dial the given node and perform a handshake, returning the
|
||||||
|
// created Conn if successful.
|
||||||
|
func (s *Suite) dial() (*Conn, error) {
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
return s.dialAs(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialAs attempts to dial a given node and perform a handshake using the given
|
||||||
|
// private key.
|
||||||
|
func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) {
|
||||||
|
fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn := Conn{Conn: rlpx.NewConn(fd, s.Dest.Pubkey())}
|
||||||
|
conn.ourKey = key
|
||||||
|
_, err = conn.Handshake(conn.ourKey)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn.caps = []p2p.Cap{
|
||||||
|
{Name: "eth", Version: 67},
|
||||||
|
{Name: "eth", Version: 68},
|
||||||
|
}
|
||||||
|
conn.ourHighestProtoVersion = 68
|
||||||
|
return &conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialSnap creates a connection with snap/1 capability.
|
||||||
|
func (s *Suite) dialSnap() (*Conn, error) {
|
||||||
|
conn, err := s.dial()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("dial failed: %v", err)
|
||||||
|
}
|
||||||
|
conn.caps = append(conn.caps, p2p.Cap{Name: "snap", Version: 1})
|
||||||
|
conn.ourHighestSnapProtoVersion = 1
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn represents an individual connection with a peer
|
||||||
|
type Conn struct {
|
||||||
|
*rlpx.Conn
|
||||||
|
ourKey *ecdsa.PrivateKey
|
||||||
|
negotiatedProtoVersion uint
|
||||||
|
negotiatedSnapProtoVersion uint
|
||||||
|
ourHighestProtoVersion uint
|
||||||
|
ourHighestSnapProtoVersion uint
|
||||||
|
caps []p2p.Cap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads a packet from the connection.
|
||||||
|
func (c *Conn) Read() (uint64, []byte, error) {
|
||||||
|
c.SetReadDeadline(time.Now().Add(timeout))
|
||||||
|
code, data, _, err := c.Conn.Read()
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
return code, data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMsg attempts to read a devp2p message with a specific code.
|
||||||
|
func (c *Conn) ReadMsg(proto Proto, code uint64, msg any) error {
|
||||||
|
c.SetReadDeadline(time.Now().Add(timeout))
|
||||||
|
for {
|
||||||
|
got, data, err := c.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if protoOffset(proto)+code == got {
|
||||||
|
return rlp.DecodeBytes(data, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes a eth packet to the connection.
|
||||||
|
func (c *Conn) Write(proto Proto, code uint64, msg any) error {
|
||||||
|
c.SetWriteDeadline(time.Now().Add(timeout))
|
||||||
|
payload, err := rlp.EncodeToBytes(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = c.Conn.Write(protoOffset(proto)+code, payload)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadEth reads an Eth sub-protocol wire message.
|
||||||
|
func (c *Conn) ReadEth() (any, error) {
|
||||||
|
c.SetReadDeadline(time.Now().Add(timeout))
|
||||||
|
for {
|
||||||
|
code, data, _, err := c.Conn.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if code == pingMsg {
|
||||||
|
c.Write(baseProto, pongMsg, []byte{})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if getProto(code) != ethProto {
|
||||||
|
// Read until eth message.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
code -= baseProtoLen
|
||||||
|
|
||||||
|
var msg any
|
||||||
|
switch int(code) {
|
||||||
|
case eth.StatusMsg:
|
||||||
|
msg = new(eth.StatusPacket)
|
||||||
|
case eth.GetBlockHeadersMsg:
|
||||||
|
msg = new(eth.GetBlockHeadersPacket)
|
||||||
|
case eth.BlockHeadersMsg:
|
||||||
|
msg = new(eth.BlockHeadersPacket)
|
||||||
|
case eth.GetBlockBodiesMsg:
|
||||||
|
msg = new(eth.GetBlockBodiesPacket)
|
||||||
|
case eth.BlockBodiesMsg:
|
||||||
|
msg = new(eth.BlockBodiesPacket)
|
||||||
|
case eth.NewBlockMsg:
|
||||||
|
msg = new(eth.NewBlockPacket)
|
||||||
|
case eth.NewBlockHashesMsg:
|
||||||
|
msg = new(eth.NewBlockHashesPacket)
|
||||||
|
case eth.TransactionsMsg:
|
||||||
|
msg = new(eth.TransactionsPacket)
|
||||||
|
case eth.NewPooledTransactionHashesMsg:
|
||||||
|
msg = new(eth.NewPooledTransactionHashesPacket)
|
||||||
|
case eth.GetPooledTransactionsMsg:
|
||||||
|
msg = new(eth.GetPooledTransactionsPacket)
|
||||||
|
case eth.PooledTransactionsMsg:
|
||||||
|
msg = new(eth.PooledTransactionsPacket)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unhandled eth msg code %d", code))
|
||||||
|
}
|
||||||
|
if err := rlp.DecodeBytes(data, msg); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode eth msg: %v", err)
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadSnap reads a snap/1 response with the given id from the connection.
|
||||||
|
func (c *Conn) ReadSnap() (any, error) {
|
||||||
|
c.SetReadDeadline(time.Now().Add(timeout))
|
||||||
|
for {
|
||||||
|
code, data, _, err := c.Conn.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if getProto(code) != snapProto {
|
||||||
|
// Read until snap message.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
code -= baseProtoLen + ethProtoLen
|
||||||
|
|
||||||
|
var msg any
|
||||||
|
switch int(code) {
|
||||||
|
case snap.GetAccountRangeMsg:
|
||||||
|
msg = new(snap.GetAccountRangePacket)
|
||||||
|
case snap.AccountRangeMsg:
|
||||||
|
msg = new(snap.AccountRangePacket)
|
||||||
|
case snap.GetStorageRangesMsg:
|
||||||
|
msg = new(snap.GetStorageRangesPacket)
|
||||||
|
case snap.StorageRangesMsg:
|
||||||
|
msg = new(snap.StorageRangesPacket)
|
||||||
|
case snap.GetByteCodesMsg:
|
||||||
|
msg = new(snap.GetByteCodesPacket)
|
||||||
|
case snap.ByteCodesMsg:
|
||||||
|
msg = new(snap.ByteCodesPacket)
|
||||||
|
case snap.GetTrieNodesMsg:
|
||||||
|
msg = new(snap.GetTrieNodesPacket)
|
||||||
|
case snap.TrieNodesMsg:
|
||||||
|
msg = new(snap.TrieNodesPacket)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unhandled snap code: %d", code))
|
||||||
|
}
|
||||||
|
if err := rlp.DecodeBytes(data, msg); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not rlp decode message: %v", err)
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// peer performs both the protocol handshake and the status message
|
||||||
|
// exchange with the node in order to peer with it.
|
||||||
|
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket) error {
|
||||||
|
if err := c.handshake(); err != nil {
|
||||||
|
return fmt.Errorf("handshake failed: %v", err)
|
||||||
|
}
|
||||||
|
if err := c.statusExchange(chain, status); err != nil {
|
||||||
|
return fmt.Errorf("status exchange failed: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handshake performs a protocol handshake with the node.
|
||||||
|
func (c *Conn) handshake() error {
|
||||||
|
// Write hello to client.
|
||||||
|
pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:]
|
||||||
|
ourHandshake := &protoHandshake{
|
||||||
|
Version: 5,
|
||||||
|
Caps: c.caps,
|
||||||
|
ID: pub0,
|
||||||
|
}
|
||||||
|
if err := c.Write(baseProto, handshakeMsg, ourHandshake); err != nil {
|
||||||
|
return fmt.Errorf("write to connection failed: %v", err)
|
||||||
|
}
|
||||||
|
// Read hello from client.
|
||||||
|
code, data, err := c.Read()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("erroring reading handshake: %v", err)
|
||||||
|
}
|
||||||
|
switch code {
|
||||||
|
case handshakeMsg:
|
||||||
|
msg := new(protoHandshake)
|
||||||
|
if err := rlp.DecodeBytes(data, &msg); err != nil {
|
||||||
|
return fmt.Errorf("error decoding handshake msg: %v", err)
|
||||||
|
}
|
||||||
|
// Set snappy if version is at least 5.
|
||||||
|
if msg.Version >= 5 {
|
||||||
|
c.SetSnappy(true)
|
||||||
|
}
|
||||||
|
c.negotiateEthProtocol(msg.Caps)
|
||||||
|
if c.negotiatedProtoVersion == 0 {
|
||||||
|
return fmt.Errorf("could not negotiate eth protocol (remote caps: %v, local eth version: %v)", msg.Caps, c.ourHighestProtoVersion)
|
||||||
|
}
|
||||||
|
// If we require snap, verify that it was negotiated.
|
||||||
|
if c.ourHighestSnapProtoVersion != c.negotiatedSnapProtoVersion {
|
||||||
|
return fmt.Errorf("could not negotiate snap protocol (remote caps: %v, local snap version: %v)", msg.Caps, c.ourHighestSnapProtoVersion)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("bad handshake: got msg code %d", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// negotiateEthProtocol sets the Conn's eth protocol version to highest
|
||||||
|
// advertised capability from peer.
|
||||||
|
func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
|
||||||
|
var highestEthVersion uint
|
||||||
|
var highestSnapVersion uint
|
||||||
|
for _, capability := range caps {
|
||||||
|
switch capability.Name {
|
||||||
|
case "eth":
|
||||||
|
if capability.Version > highestEthVersion && capability.Version <= c.ourHighestProtoVersion {
|
||||||
|
highestEthVersion = capability.Version
|
||||||
|
}
|
||||||
|
case "snap":
|
||||||
|
if capability.Version > highestSnapVersion && capability.Version <= c.ourHighestSnapProtoVersion {
|
||||||
|
highestSnapVersion = capability.Version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.negotiatedProtoVersion = highestEthVersion
|
||||||
|
c.negotiatedSnapProtoVersion = highestSnapVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// statusExchange performs a `Status` message exchange with the given node.
|
||||||
|
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket) error {
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
code, data, err := c.Read()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read from connection: %w", err)
|
||||||
|
}
|
||||||
|
switch code {
|
||||||
|
case eth.StatusMsg + protoOffset(ethProto):
|
||||||
|
msg := new(eth.StatusPacket)
|
||||||
|
if err := rlp.DecodeBytes(data, &msg); err != nil {
|
||||||
|
return fmt.Errorf("error decoding status packet: %w", err)
|
||||||
|
}
|
||||||
|
if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want {
|
||||||
|
return fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x",
|
||||||
|
want, chain.blocks[chain.Len()-1].NumberU64(), have)
|
||||||
|
}
|
||||||
|
if have, want := msg.TD.Cmp(chain.TD()), 0; have != want {
|
||||||
|
return fmt.Errorf("wrong TD in status: have %v want %v", have, want)
|
||||||
|
}
|
||||||
|
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
|
||||||
|
return fmt.Errorf("wrong fork ID in status: have %v, want %v", have, want)
|
||||||
|
}
|
||||||
|
if have, want := msg.ProtocolVersion, c.ourHighestProtoVersion; have != uint32(want) {
|
||||||
|
return fmt.Errorf("wrong protocol version: have %v, want %v", have, want)
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
case discMsg:
|
||||||
|
var msg []p2p.DiscReason
|
||||||
|
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
|
||||||
|
return errors.New("invalid disconnect message")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("disconnect received: %v", pretty.Sdump(msg))
|
||||||
|
case pingMsg:
|
||||||
|
// TODO (renaynay): in the future, this should be an error
|
||||||
|
// (PINGs should not be a response upon fresh connection)
|
||||||
|
c.Write(baseProto, pongMsg, nil)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("bad status message: code %d", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make sure eth protocol version is set for negotiation
|
||||||
|
if c.negotiatedProtoVersion == 0 {
|
||||||
|
return errors.New("eth protocol version must be set in Conn")
|
||||||
|
}
|
||||||
|
if status == nil {
|
||||||
|
// default status message
|
||||||
|
status = ð.StatusPacket{
|
||||||
|
ProtocolVersion: uint32(c.negotiatedProtoVersion),
|
||||||
|
NetworkID: chain.config.ChainID.Uint64(),
|
||||||
|
TD: chain.TD(),
|
||||||
|
Head: chain.blocks[chain.Len()-1].Hash(),
|
||||||
|
Genesis: chain.blocks[0].Hash(),
|
||||||
|
ForkID: chain.ForkID(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := c.Write(ethProto, eth.StatusMsg, status); err != nil {
|
||||||
|
return fmt.Errorf("write to connection failed: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
69
cmd/devp2p/internal/ethtest/engine.go
Normal file
69
cmd/devp2p/internal/ethtest/engine.go
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright 2023 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 ethtest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EngineClient is a wrapper around engine-related data.
|
||||||
|
type EngineClient struct {
|
||||||
|
url string
|
||||||
|
jwt [32]byte
|
||||||
|
headfcu []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEngineClient creates a new engine client.
|
||||||
|
func NewEngineClient(dir, url, jwt string) (*EngineClient, error) {
|
||||||
|
headfcu, err := os.ReadFile(path.Join(dir, "headfcu.json"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read headfcu: %w", err)
|
||||||
|
}
|
||||||
|
return &EngineClient{url, common.HexToHash(jwt), headfcu}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// token returns the jwt claim token for authorization.
|
||||||
|
func (ec *EngineClient) token() string {
|
||||||
|
claims := jwt.RegisteredClaims{IssuedAt: jwt.NewNumericDate(time.Now())}
|
||||||
|
token, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(ec.jwt[:])
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendForkchoiceUpdated sends an fcu for the head of the generated chain.
|
||||||
|
func (ec *EngineClient) sendForkchoiceUpdated() error {
|
||||||
|
var (
|
||||||
|
req, _ = http.NewRequest(http.MethodPost, ec.url, io.NopCloser(bytes.NewReader(ec.headfcu)))
|
||||||
|
header = make(http.Header)
|
||||||
|
)
|
||||||
|
// Set header
|
||||||
|
header.Set("accept", "application/json")
|
||||||
|
header.Set("content-type", "application/json")
|
||||||
|
header.Set("Authorization", fmt.Sprintf("Bearer %v", ec.token()))
|
||||||
|
req.Header = header
|
||||||
|
|
||||||
|
_, err := new(http.Client).Do(req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
@ -1,711 +0,0 @@
|
||||||
// Copyright 2021 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 ethtest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/rlpx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
pretty = spew.ConfigState{
|
|
||||||
Indent: " ",
|
|
||||||
DisableCapacities: true,
|
|
||||||
DisablePointerAddresses: true,
|
|
||||||
SortKeys: true,
|
|
||||||
}
|
|
||||||
timeout = 20 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
// dial attempts to dial the given node and perform a handshake,
|
|
||||||
// returning the created Conn if successful.
|
|
||||||
func (s *Suite) dial() (*Conn, error) {
|
|
||||||
// dial
|
|
||||||
fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
conn := Conn{Conn: rlpx.NewConn(fd, s.Dest.Pubkey())}
|
|
||||||
// do encHandshake
|
|
||||||
conn.ourKey, _ = crypto.GenerateKey()
|
|
||||||
_, err = conn.Handshake(conn.ourKey)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// set default p2p capabilities
|
|
||||||
conn.caps = []p2p.Cap{
|
|
||||||
{Name: "eth", Version: 67},
|
|
||||||
{Name: "eth", Version: 68},
|
|
||||||
}
|
|
||||||
conn.ourHighestProtoVersion = 68
|
|
||||||
|
|
||||||
return &conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// dialSnap creates a connection with snap/1 capability.
|
|
||||||
func (s *Suite) dialSnap() (*Conn, error) {
|
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.caps = append(conn.caps, p2p.Cap{Name: "snap", Version: 1})
|
|
||||||
conn.ourHighestSnapProtoVersion = 1
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// peer performs both the protocol handshake and the status message
|
|
||||||
// exchange with the node in order to peer with it.
|
|
||||||
func (c *Conn) peer(chain *Chain, status *Status) error {
|
|
||||||
if err := c.handshake(); err != nil {
|
|
||||||
return fmt.Errorf("handshake failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := c.statusExchange(chain, status); err != nil {
|
|
||||||
return fmt.Errorf("status exchange failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// handshake performs a protocol handshake with the node.
|
|
||||||
func (c *Conn) handshake() error {
|
|
||||||
defer c.SetDeadline(time.Time{})
|
|
||||||
c.SetDeadline(time.Now().Add(10 * time.Second))
|
|
||||||
// write hello to client
|
|
||||||
pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:]
|
|
||||||
|
|
||||||
ourHandshake := &Hello{
|
|
||||||
Version: 5,
|
|
||||||
Caps: c.caps,
|
|
||||||
ID: pub0,
|
|
||||||
}
|
|
||||||
if err := c.Write(ourHandshake); err != nil {
|
|
||||||
return fmt.Errorf("write to connection failed: %v", err)
|
|
||||||
}
|
|
||||||
// read hello from client
|
|
||||||
switch msg := c.Read().(type) {
|
|
||||||
case *Hello:
|
|
||||||
// set snappy if version is at least 5
|
|
||||||
if msg.Version >= 5 {
|
|
||||||
c.SetSnappy(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.negotiateEthProtocol(msg.Caps)
|
|
||||||
|
|
||||||
if c.negotiatedProtoVersion == 0 {
|
|
||||||
return fmt.Errorf("could not negotiate eth protocol (remote caps: %v, local eth version: %v)", msg.Caps, c.ourHighestProtoVersion)
|
|
||||||
}
|
|
||||||
// If we require snap, verify that it was negotiated
|
|
||||||
if c.ourHighestSnapProtoVersion != c.negotiatedSnapProtoVersion {
|
|
||||||
return fmt.Errorf("could not negotiate snap protocol (remote caps: %v, local snap version: %v)", msg.Caps, c.ourHighestSnapProtoVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("bad handshake: %#v", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// negotiateEthProtocol sets the Conn's eth protocol version to highest
|
|
||||||
// advertised capability from peer.
|
|
||||||
func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
|
|
||||||
var highestEthVersion uint
|
|
||||||
|
|
||||||
var highestSnapVersion uint
|
|
||||||
|
|
||||||
for _, capability := range caps {
|
|
||||||
switch capability.Name {
|
|
||||||
case "eth":
|
|
||||||
if capability.Version > highestEthVersion && capability.Version <= c.ourHighestProtoVersion {
|
|
||||||
highestEthVersion = capability.Version
|
|
||||||
}
|
|
||||||
case "snap":
|
|
||||||
if capability.Version > highestSnapVersion && capability.Version <= c.ourHighestSnapProtoVersion {
|
|
||||||
highestSnapVersion = capability.Version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.negotiatedProtoVersion = highestEthVersion
|
|
||||||
c.negotiatedSnapProtoVersion = highestSnapVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
// statusExchange performs a `Status` message exchange with the given node.
|
|
||||||
func (c *Conn) statusExchange(chain *Chain, status *Status) (Message, error) {
|
|
||||||
defer c.SetDeadline(time.Time{})
|
|
||||||
c.SetDeadline(time.Now().Add(20 * time.Second))
|
|
||||||
|
|
||||||
// read status message from client
|
|
||||||
var message Message
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
switch msg := c.Read().(type) {
|
|
||||||
case *Status:
|
|
||||||
if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want {
|
|
||||||
return nil, fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x",
|
|
||||||
want, chain.blocks[chain.Len()-1].NumberU64(), have)
|
|
||||||
}
|
|
||||||
if have, want := msg.TD.Cmp(chain.TD()), 0; have != want {
|
|
||||||
return nil, fmt.Errorf("wrong TD in status: have %v want %v", have, want)
|
|
||||||
}
|
|
||||||
if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) {
|
|
||||||
return nil, fmt.Errorf("wrong fork ID in status: have %v, want %v", have, want)
|
|
||||||
}
|
|
||||||
if have, want := msg.ProtocolVersion, c.ourHighestProtoVersion; have != uint32(want) {
|
|
||||||
return nil, fmt.Errorf("wrong protocol version: have %v, want %v", have, want)
|
|
||||||
}
|
|
||||||
message = msg
|
|
||||||
break loop
|
|
||||||
case *Disconnect:
|
|
||||||
return nil, fmt.Errorf("disconnect received: %v", msg.Reason)
|
|
||||||
case *Ping:
|
|
||||||
c.Write(&Pong{}) // TODO (renaynay): in the future, this should be an error
|
|
||||||
// (PINGs should not be a response upon fresh connection)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("bad status message: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// make sure eth protocol version is set for negotiation
|
|
||||||
if c.negotiatedProtoVersion == 0 {
|
|
||||||
return nil, errors.New("eth protocol version must be set in Conn")
|
|
||||||
}
|
|
||||||
|
|
||||||
if status == nil {
|
|
||||||
// default status message
|
|
||||||
status = &Status{
|
|
||||||
ProtocolVersion: uint32(c.negotiatedProtoVersion),
|
|
||||||
NetworkID: chain.chainConfig.ChainID.Uint64(),
|
|
||||||
TD: chain.TD(),
|
|
||||||
Head: chain.blocks[chain.Len()-1].Hash(),
|
|
||||||
Genesis: chain.blocks[0].Hash(),
|
|
||||||
ForkID: chain.ForkID(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Write(status); err != nil {
|
|
||||||
return nil, fmt.Errorf("write to connection failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return message, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// createSendAndRecvConns creates two connections, one for sending messages to the
|
|
||||||
// node, and one for receiving messages from the node.
|
|
||||||
func (s *Suite) createSendAndRecvConns() (*Conn, *Conn, error) {
|
|
||||||
sendConn, err := s.dial()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
recvConn, err := s.dial()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
sendConn.Close()
|
|
||||||
return nil, nil, fmt.Errorf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sendConn, recvConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readAndServe serves GetBlockHeaders requests while waiting
|
|
||||||
// on another message from the node.
|
|
||||||
func (c *Conn) readAndServe(chain *Chain, timeout time.Duration) Message {
|
|
||||||
start := time.Now()
|
|
||||||
for time.Since(start) < timeout {
|
|
||||||
c.SetReadDeadline(time.Now().Add(10 * time.Second))
|
|
||||||
|
|
||||||
msg := c.Read()
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case *Ping:
|
|
||||||
c.Write(&Pong{})
|
|
||||||
case *GetBlockHeaders:
|
|
||||||
headers, err := chain.GetHeaders(msg)
|
|
||||||
if err != nil {
|
|
||||||
return errorf("could not get headers for inbound header request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp := &BlockHeaders{
|
|
||||||
RequestId: msg.ReqID(),
|
|
||||||
BlockHeadersRequest: eth.BlockHeadersRequest(headers),
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Write(resp); err != nil {
|
|
||||||
return errorf("could not write to connection: %v", err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errorf("no message received within %v", timeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
// headersRequest executes the given `GetBlockHeaders` request.
|
|
||||||
func (c *Conn) headersRequest(request *GetBlockHeaders, chain *Chain, reqID uint64) ([]*types.Header, error) {
|
|
||||||
defer c.SetReadDeadline(time.Time{})
|
|
||||||
c.SetReadDeadline(time.Now().Add(20 * time.Second))
|
|
||||||
|
|
||||||
// write request
|
|
||||||
request.RequestId = reqID
|
|
||||||
if err := c.Write(request); err != nil {
|
|
||||||
return nil, fmt.Errorf("could not write to connection: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for response
|
|
||||||
msg := c.waitForResponse(chain, timeout, request.RequestId)
|
|
||||||
resp, ok := msg.(*BlockHeaders)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unexpected message received: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
headers := []*types.Header(resp.BlockHeadersRequest)
|
|
||||||
return headers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) snapRequest(msg Message, id uint64, chain *Chain) (Message, error) {
|
|
||||||
defer c.SetReadDeadline(time.Time{})
|
|
||||||
c.SetReadDeadline(time.Now().Add(5 * time.Second))
|
|
||||||
|
|
||||||
if err := c.Write(msg); err != nil {
|
|
||||||
return nil, fmt.Errorf("could not write to connection: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.ReadSnap(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// headersMatch returns whether the received headers match the given request
|
|
||||||
func headersMatch(expected []*types.Header, headers []*types.Header) bool {
|
|
||||||
return reflect.DeepEqual(expected, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// waitForResponse reads from the connection until a response with the expected
|
|
||||||
// request ID is received.
|
|
||||||
func (c *Conn) waitForResponse(chain *Chain, timeout time.Duration, requestID uint64) Message {
|
|
||||||
for {
|
|
||||||
msg := c.readAndServe(chain, timeout)
|
|
||||||
if msg.ReqID() == requestID {
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendNextBlock broadcasts the next block in the chain and waits
|
|
||||||
// for the node to propagate the block and import it into its chain.
|
|
||||||
func (s *Suite) sendNextBlock() error {
|
|
||||||
// set up sending and receiving connections
|
|
||||||
sendConn, recvConn, err := s.createSendAndRecvConns()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer sendConn.Close()
|
|
||||||
defer recvConn.Close()
|
|
||||||
|
|
||||||
if err = sendConn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = recvConn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
// create new block announcement
|
|
||||||
nextBlock := s.fullChain.blocks[s.chain.Len()]
|
|
||||||
blockAnnouncement := &NewBlock{
|
|
||||||
Block: nextBlock,
|
|
||||||
TD: s.fullChain.TotalDifficultyAt(s.chain.Len()),
|
|
||||||
}
|
|
||||||
// send announcement and wait for node to request the header
|
|
||||||
if err = s.testAnnounce(sendConn, recvConn, blockAnnouncement); err != nil {
|
|
||||||
return fmt.Errorf("failed to announce block: %v", err)
|
|
||||||
}
|
|
||||||
// wait for client to update its chain
|
|
||||||
if err = s.waitForBlockImport(recvConn, nextBlock); err != nil {
|
|
||||||
return fmt.Errorf("failed to receive confirmation of block import: %v", err)
|
|
||||||
}
|
|
||||||
// update test suite chain
|
|
||||||
s.chain.blocks = append(s.chain.blocks, nextBlock)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// testAnnounce writes a block announcement to the node and waits for the node
|
|
||||||
// to propagate it.
|
|
||||||
func (s *Suite) testAnnounce(sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) error {
|
|
||||||
if err := sendConn.Write(blockAnnouncement); err != nil {
|
|
||||||
return fmt.Errorf("could not write to connection: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.waitAnnounce(receiveConn, blockAnnouncement)
|
|
||||||
}
|
|
||||||
|
|
||||||
// waitAnnounce waits for a NewBlock or NewBlockHashes announcement from the node.
|
|
||||||
func (s *Suite) waitAnnounce(conn *Conn, blockAnnouncement *NewBlock) error {
|
|
||||||
for {
|
|
||||||
switch msg := conn.readAndServe(s.chain, timeout).(type) {
|
|
||||||
case *NewBlock:
|
|
||||||
if !reflect.DeepEqual(blockAnnouncement.Block.Header(), msg.Block.Header()) {
|
|
||||||
return fmt.Errorf("wrong header in block announcement: \nexpected %v "+
|
|
||||||
"\ngot %v", blockAnnouncement.Block.Header(), msg.Block.Header())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(blockAnnouncement.TD, msg.TD) {
|
|
||||||
return fmt.Errorf("wrong TD in announcement: expected %v, got %v", blockAnnouncement.TD, msg.TD)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
case *NewBlockHashes:
|
|
||||||
hashes := *msg
|
|
||||||
if blockAnnouncement.Block.Hash() != hashes[0].Hash {
|
|
||||||
return fmt.Errorf("wrong block hash in announcement: expected %v, got %v", blockAnnouncement.Block.Hash(), hashes[0].Hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
// ignore tx announcements from previous tests
|
|
||||||
case *NewPooledTransactionHashes66:
|
|
||||||
continue
|
|
||||||
case *NewPooledTransactionHashes:
|
|
||||||
continue
|
|
||||||
case *Transactions:
|
|
||||||
continue
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unexpected: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) waitForBlockImport(conn *Conn, block *types.Block) error {
|
|
||||||
defer conn.SetReadDeadline(time.Time{})
|
|
||||||
conn.SetReadDeadline(time.Now().Add(20 * time.Second))
|
|
||||||
// create request
|
|
||||||
req := &GetBlockHeaders{
|
|
||||||
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
|
||||||
Origin: eth.HashOrNumber{Hash: block.Hash()},
|
|
||||||
Amount: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// loop until BlockHeaders response contains desired block, confirming the
|
|
||||||
// node imported the block
|
|
||||||
for {
|
|
||||||
requestID := uint64(54)
|
|
||||||
|
|
||||||
headers, err := conn.headersRequest(req, s.chain, requestID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("GetBlockHeader request failed: %v", err)
|
|
||||||
}
|
|
||||||
// if headers response is empty, node hasn't imported block yet, try again
|
|
||||||
if len(headers) == 0 {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(block.Header(), headers[0]) {
|
|
||||||
return fmt.Errorf("wrong header returned: wanted %v, got %v", block.Header(), headers[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) oldAnnounce() error {
|
|
||||||
sendConn, receiveConn, err := s.createSendAndRecvConns()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer sendConn.Close()
|
|
||||||
defer receiveConn.Close()
|
|
||||||
|
|
||||||
if err := sendConn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := receiveConn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
// create old block announcement
|
|
||||||
oldBlockAnnounce := &NewBlock{
|
|
||||||
Block: s.chain.blocks[len(s.chain.blocks)/2],
|
|
||||||
TD: s.chain.blocks[len(s.chain.blocks)/2].Difficulty(),
|
|
||||||
}
|
|
||||||
if err := sendConn.Write(oldBlockAnnounce); err != nil {
|
|
||||||
return fmt.Errorf("could not write to connection: %v", err)
|
|
||||||
}
|
|
||||||
// wait to see if the announcement is propagated
|
|
||||||
switch msg := receiveConn.readAndServe(s.chain, time.Second*8).(type) {
|
|
||||||
case *NewBlock:
|
|
||||||
block := *msg
|
|
||||||
if block.Block.Hash() == oldBlockAnnounce.Block.Hash() {
|
|
||||||
return fmt.Errorf("unexpected: block propagated: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
case *NewBlockHashes:
|
|
||||||
hashes := *msg
|
|
||||||
for _, hash := range hashes {
|
|
||||||
if hash.Hash == oldBlockAnnounce.Block.Hash() {
|
|
||||||
return fmt.Errorf("unexpected: block announced: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *Error:
|
|
||||||
errMsg := *msg
|
|
||||||
// check to make sure error is timeout (propagation didn't come through == test successful)
|
|
||||||
if !strings.Contains(errMsg.String(), "timeout") {
|
|
||||||
return fmt.Errorf("unexpected error: %v", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unexpected: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) maliciousHandshakes(t *utesting.T) error {
|
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// write hello to client
|
|
||||||
pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:]
|
|
||||||
|
|
||||||
handshakes := []*Hello{
|
|
||||||
{
|
|
||||||
Version: 5,
|
|
||||||
Caps: []p2p.Cap{
|
|
||||||
{Name: largeString(2), Version: 64},
|
|
||||||
},
|
|
||||||
ID: pub0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Version: 5,
|
|
||||||
Caps: []p2p.Cap{
|
|
||||||
{Name: "eth", Version: 64},
|
|
||||||
{Name: "eth", Version: 65},
|
|
||||||
},
|
|
||||||
ID: append(pub0, byte(0)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Version: 5,
|
|
||||||
Caps: []p2p.Cap{
|
|
||||||
{Name: "eth", Version: 64},
|
|
||||||
{Name: "eth", Version: 65},
|
|
||||||
},
|
|
||||||
ID: append(pub0, pub0...),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Version: 5,
|
|
||||||
Caps: []p2p.Cap{
|
|
||||||
{Name: "eth", Version: 64},
|
|
||||||
{Name: "eth", Version: 65},
|
|
||||||
},
|
|
||||||
ID: largeBuffer(2),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Version: 5,
|
|
||||||
Caps: []p2p.Cap{
|
|
||||||
{Name: largeString(2), Version: 64},
|
|
||||||
},
|
|
||||||
ID: largeBuffer(2),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i, handshake := range handshakes {
|
|
||||||
t.Logf("Testing malicious handshake %v\n", i)
|
|
||||||
|
|
||||||
if err := conn.Write(handshake); err != nil {
|
|
||||||
return fmt.Errorf("could not write to connection: %v", err)
|
|
||||||
}
|
|
||||||
// check that the peer disconnected
|
|
||||||
for i := 0; i < 2; i++ {
|
|
||||||
switch msg := conn.readAndServe(s.chain, 20*time.Second).(type) {
|
|
||||||
case *Disconnect:
|
|
||||||
case *Error:
|
|
||||||
case *Hello:
|
|
||||||
// Discard one hello as Hello's are sent concurrently
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unexpected: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// dial for the next round
|
|
||||||
conn, err = s.dial()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) maliciousStatus(conn *Conn) error {
|
|
||||||
if err := conn.handshake(); err != nil {
|
|
||||||
return fmt.Errorf("handshake failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
status := &Status{
|
|
||||||
ProtocolVersion: uint32(conn.negotiatedProtoVersion),
|
|
||||||
NetworkID: s.chain.chainConfig.ChainID.Uint64(),
|
|
||||||
TD: largeNumber(2),
|
|
||||||
Head: s.chain.blocks[s.chain.Len()-1].Hash(),
|
|
||||||
Genesis: s.chain.blocks[0].Hash(),
|
|
||||||
ForkID: s.chain.ForkID(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// get status
|
|
||||||
msg, err := conn.statusExchange(s.chain, status)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("status exchange failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case *Status:
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("expected status, got: %#v ", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for disconnect
|
|
||||||
switch msg := conn.readAndServe(s.chain, timeout).(type) {
|
|
||||||
case *Disconnect:
|
|
||||||
return nil
|
|
||||||
case *Error:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("expected disconnect, got: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:typecheck
|
|
||||||
func (s *Suite) hashAnnounce() error {
|
|
||||||
// create connections
|
|
||||||
sendConn, recvConn, err := s.createSendAndRecvConns()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create connections: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer sendConn.Close()
|
|
||||||
defer recvConn.Close()
|
|
||||||
|
|
||||||
if err := sendConn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := recvConn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create NewBlockHashes announcement
|
|
||||||
type anno struct {
|
|
||||||
Hash common.Hash // Hash of one particular block being announced
|
|
||||||
Number uint64 // Number of one particular block being announced
|
|
||||||
}
|
|
||||||
|
|
||||||
nextBlock := s.fullChain.blocks[s.chain.Len()]
|
|
||||||
announcement := anno{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()}
|
|
||||||
|
|
||||||
newBlockHash := &NewBlockHashes{announcement}
|
|
||||||
if err := sendConn.Write(newBlockHash); err != nil {
|
|
||||||
return fmt.Errorf("failed to write to connection: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Announcement sent, now wait for a header request
|
|
||||||
msg := sendConn.Read()
|
|
||||||
blockHeaderReq, ok := msg.(*GetBlockHeaders)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unexpected %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
if blockHeaderReq.Amount != 1 {
|
|
||||||
return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
if blockHeaderReq.Origin.Hash != announcement.Hash {
|
|
||||||
return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v",
|
|
||||||
pretty.Sdump(announcement),
|
|
||||||
pretty.Sdump(blockHeaderReq))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = sendConn.Write(&BlockHeaders{
|
|
||||||
RequestId: blockHeaderReq.ReqID(),
|
|
||||||
BlockHeadersRequest: eth.BlockHeadersRequest{nextBlock.Header()},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to write to connection: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for block announcement
|
|
||||||
msg = recvConn.readAndServe(s.chain, timeout)
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case *NewBlockHashes:
|
|
||||||
hashes := *msg
|
|
||||||
if len(hashes) != 1 {
|
|
||||||
return fmt.Errorf("unexpected new block hash announcement: wanted 1 announcement, got %d", len(hashes))
|
|
||||||
}
|
|
||||||
|
|
||||||
if nextBlock.Hash() != hashes[0].Hash {
|
|
||||||
return fmt.Errorf("unexpected block hash announcement, wanted %v, got %v", nextBlock.Hash(),
|
|
||||||
hashes[0].Hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *NewBlock:
|
|
||||||
// node should only propagate NewBlock without having requested the body if the body is empty
|
|
||||||
nextBlockBody := nextBlock.Body()
|
|
||||||
if len(nextBlockBody.Transactions) != 0 || len(nextBlockBody.Uncles) != 0 {
|
|
||||||
return fmt.Errorf("unexpected non-empty new block propagated: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.Block.Hash() != nextBlock.Hash() {
|
|
||||||
return fmt.Errorf("mismatched hash of propagated new block: wanted %v, got %v",
|
|
||||||
nextBlock.Hash(), msg.Block.Hash())
|
|
||||||
}
|
|
||||||
// check to make sure header matches header that was sent to the node
|
|
||||||
if !reflect.DeepEqual(nextBlock.Header(), msg.Block.Header()) {
|
|
||||||
return fmt.Errorf("incorrect header received: wanted %v, got %v", nextBlock.Header(), msg.Block.Header())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unexpected: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
// confirm node imported block
|
|
||||||
if err := s.waitForBlockImport(recvConn, nextBlock); err != nil {
|
|
||||||
return fmt.Errorf("error waiting for node to import new block: %v", err)
|
|
||||||
}
|
|
||||||
// update the chain
|
|
||||||
s.chain.blocks = append(s.chain.blocks, nextBlock)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
// Copyright 2020 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 ethtest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// largeNumber returns a very large big.Int.
|
|
||||||
func largeNumber(megabytes int) *big.Int {
|
|
||||||
buf := make([]byte, megabytes*1024*1024)
|
|
||||||
rand.Read(buf)
|
|
||||||
|
|
||||||
bigint := new(big.Int)
|
|
||||||
bigint.SetBytes(buf)
|
|
||||||
|
|
||||||
return bigint
|
|
||||||
}
|
|
||||||
|
|
||||||
// largeBuffer returns a very large buffer.
|
|
||||||
func largeBuffer(megabytes int) []byte {
|
|
||||||
buf := make([]byte, megabytes*1024*1024)
|
|
||||||
rand.Read(buf)
|
|
||||||
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// largeString returns a very large string.
|
|
||||||
func largeString(megabytes int) string {
|
|
||||||
buf := make([]byte, megabytes*1024*1024)
|
|
||||||
rand.Read(buf)
|
|
||||||
|
|
||||||
return hexutil.Encode(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func largeBlock() *types.Block {
|
|
||||||
return types.NewBlockWithHeader(largeHeader())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a random hash
|
|
||||||
func randHash() common.Hash {
|
|
||||||
var h common.Hash
|
|
||||||
|
|
||||||
rand.Read(h[:])
|
|
||||||
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func largeHeader() *types.Header {
|
|
||||||
return &types.Header{
|
|
||||||
MixDigest: randHash(),
|
|
||||||
ReceiptHash: randHash(),
|
|
||||||
TxHash: randHash(),
|
|
||||||
Nonce: types.BlockNonce{},
|
|
||||||
Extra: []byte{},
|
|
||||||
Bloom: types.Bloom{},
|
|
||||||
GasUsed: 0,
|
|
||||||
Coinbase: common.Address{},
|
|
||||||
GasLimit: 0,
|
|
||||||
UncleHash: types.EmptyUncleHash,
|
|
||||||
Time: 1337,
|
|
||||||
ParentHash: randHash(),
|
|
||||||
Root: randHash(),
|
|
||||||
Number: largeNumber(2),
|
|
||||||
Difficulty: largeNumber(2),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9
cmd/devp2p/internal/ethtest/mkchain.sh
Normal file
9
cmd/devp2p/internal/ethtest/mkchain.sh
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
hivechain generate \
|
||||||
|
--fork-interval 6 \
|
||||||
|
--tx-interval 1 \
|
||||||
|
--length 500 \
|
||||||
|
--outdir testdata \
|
||||||
|
--lastfork cancun \
|
||||||
|
--outputs accounts,genesis,chain,headstate,txinfo,headblock,headfcu,newpayload,forkenv
|
||||||
87
cmd/devp2p/internal/ethtest/protocol.go
Normal file
87
cmd/devp2p/internal/ethtest/protocol.go
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright 2023 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 ethtest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unexported devp2p message codes from p2p/peer.go.
|
||||||
|
const (
|
||||||
|
handshakeMsg = 0x00
|
||||||
|
discMsg = 0x01
|
||||||
|
pingMsg = 0x02
|
||||||
|
pongMsg = 0x03
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unexported devp2p protocol lengths from p2p package.
|
||||||
|
const (
|
||||||
|
baseProtoLen = 16
|
||||||
|
ethProtoLen = 17
|
||||||
|
snapProtoLen = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unexported handshake structure from p2p/peer.go.
|
||||||
|
type protoHandshake struct {
|
||||||
|
Version uint64
|
||||||
|
Name string
|
||||||
|
Caps []p2p.Cap
|
||||||
|
ListenPort uint64
|
||||||
|
ID []byte
|
||||||
|
Rest []rlp.RawValue `rlp:"tail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hello = protoHandshake
|
||||||
|
|
||||||
|
// Proto is an enum representing devp2p protocol types.
|
||||||
|
type Proto int
|
||||||
|
|
||||||
|
const (
|
||||||
|
baseProto Proto = iota
|
||||||
|
ethProto
|
||||||
|
snapProto
|
||||||
|
)
|
||||||
|
|
||||||
|
// getProto returns the protocol a certain message code is associated with
|
||||||
|
// (assuming the negotiated capabilities are exactly {eth,snap})
|
||||||
|
func getProto(code uint64) Proto {
|
||||||
|
switch {
|
||||||
|
case code < baseProtoLen:
|
||||||
|
return baseProto
|
||||||
|
case code < baseProtoLen+ethProtoLen:
|
||||||
|
return ethProto
|
||||||
|
case code < baseProtoLen+ethProtoLen+snapProtoLen:
|
||||||
|
return snapProto
|
||||||
|
default:
|
||||||
|
panic("unhandled msg code beyond last protocol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// protoOffset will return the offset at which the specified protocol's messages
|
||||||
|
// begin.
|
||||||
|
func protoOffset(proto Proto) uint64 {
|
||||||
|
switch proto {
|
||||||
|
case baseProto:
|
||||||
|
return 0
|
||||||
|
case ethProto:
|
||||||
|
return baseProtoLen
|
||||||
|
case snapProto:
|
||||||
|
return baseProtoLen + ethProtoLen
|
||||||
|
default:
|
||||||
|
panic("unhandled protocol")
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,60 +0,0 @@
|
||||||
// Copyright 2022 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 ethtest
|
|
||||||
|
|
||||||
import "github.com/ethereum/go-ethereum/eth/protocols/snap"
|
|
||||||
|
|
||||||
// GetAccountRange represents an account range query.
|
|
||||||
type GetAccountRange snap.GetAccountRangePacket
|
|
||||||
|
|
||||||
func (msg GetAccountRange) Code() int { return 33 }
|
|
||||||
func (msg GetAccountRange) ReqID() uint64 { return msg.ID }
|
|
||||||
|
|
||||||
type AccountRange snap.AccountRangePacket
|
|
||||||
|
|
||||||
func (msg AccountRange) Code() int { return 34 }
|
|
||||||
func (msg AccountRange) ReqID() uint64 { return msg.ID }
|
|
||||||
|
|
||||||
type GetStorageRanges snap.GetStorageRangesPacket
|
|
||||||
|
|
||||||
func (msg GetStorageRanges) Code() int { return 35 }
|
|
||||||
func (msg GetStorageRanges) ReqID() uint64 { return msg.ID }
|
|
||||||
|
|
||||||
type StorageRanges snap.StorageRangesPacket
|
|
||||||
|
|
||||||
func (msg StorageRanges) Code() int { return 36 }
|
|
||||||
func (msg StorageRanges) ReqID() uint64 { return msg.ID }
|
|
||||||
|
|
||||||
type GetByteCodes snap.GetByteCodesPacket
|
|
||||||
|
|
||||||
func (msg GetByteCodes) Code() int { return 37 }
|
|
||||||
func (msg GetByteCodes) ReqID() uint64 { return msg.ID }
|
|
||||||
|
|
||||||
type ByteCodes snap.ByteCodesPacket
|
|
||||||
|
|
||||||
func (msg ByteCodes) Code() int { return 38 }
|
|
||||||
func (msg ByteCodes) ReqID() uint64 { return msg.ID }
|
|
||||||
|
|
||||||
type GetTrieNodes snap.GetTrieNodesPacket
|
|
||||||
|
|
||||||
func (msg GetTrieNodes) Code() int { return 39 }
|
|
||||||
func (msg GetTrieNodes) ReqID() uint64 { return msg.ID }
|
|
||||||
|
|
||||||
type TrieNodes snap.TrieNodesPacket
|
|
||||||
|
|
||||||
func (msg TrieNodes) Code() int { return 40 }
|
|
||||||
func (msg TrieNodes) ReqID() uint64 { return msg.ID }
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -17,32 +17,47 @@
|
||||||
package ethtest
|
package ethtest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
crand "crypto/rand"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func makeJWTSecret() (string, [32]byte, error) {
|
||||||
genesisFile = "./testdata/genesis.json"
|
var secret [32]byte
|
||||||
halfchainFile = "./testdata/halfchain.rlp"
|
if _, err := crand.Read(secret[:]); err != nil {
|
||||||
fullchainFile = "./testdata/chain.rlp"
|
return "", secret, fmt.Errorf("failed to create jwt secret: %v", err)
|
||||||
)
|
}
|
||||||
|
jwtPath := path.Join(os.TempDir(), "jwt_secret")
|
||||||
|
if err := os.WriteFile(jwtPath, []byte(hexutil.Encode(secret[:])), 0600); err != nil {
|
||||||
|
return "", secret, fmt.Errorf("failed to prepare jwt secret file: %v", err)
|
||||||
|
}
|
||||||
|
return jwtPath, secret, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestEthSuite(t *testing.T) {
|
func TestEthSuite(t *testing.T) {
|
||||||
t.Parallel()
|
jwtPath, secret, err := makeJWTSecret()
|
||||||
geth, err := runGeth()
|
if err != nil {
|
||||||
|
t.Fatalf("could not make jwt secret: %v", err)
|
||||||
|
}
|
||||||
|
geth, err := runGeth("./testdata", jwtPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not run geth: %v", err)
|
t.Fatalf("could not run geth: %v", err)
|
||||||
}
|
}
|
||||||
defer geth.Close()
|
defer geth.Close()
|
||||||
|
|
||||||
suite, err := NewSuite(geth.Server().Self(), fullchainFile, genesisFile)
|
suite, err := NewSuite(geth.Server().Self(), "./testdata", geth.HTTPAuthEndpoint(), common.Bytes2Hex(secret[:]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create new test suite: %v", err)
|
t.Fatalf("could not create new test suite: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +67,10 @@ func TestEthSuite(t *testing.T) {
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
|
if test.Slow && testing.Short() {
|
||||||
|
t.Skipf("%s: skipping in -short mode", test.Name)
|
||||||
|
}
|
||||||
|
result := utesting.RunTests([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
|
||||||
if result[0].Failed {
|
if result[0].Failed {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
|
|
@ -61,21 +79,24 @@ func TestEthSuite(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSnapSuite(t *testing.T) {
|
func TestSnapSuite(t *testing.T) {
|
||||||
t.Parallel()
|
jwtPath, secret, err := makeJWTSecret()
|
||||||
geth, err := runGeth()
|
if err != nil {
|
||||||
|
t.Fatalf("could not make jwt secret: %v", err)
|
||||||
|
}
|
||||||
|
geth, err := runGeth("./testdata", jwtPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not run geth: %v", err)
|
t.Fatalf("could not run geth: %v", err)
|
||||||
}
|
}
|
||||||
defer geth.Close()
|
defer geth.Close()
|
||||||
|
|
||||||
suite, err := NewSuite(geth.Server().Self(), fullchainFile, genesisFile)
|
suite, err := NewSuite(geth.Server().Self(), "./testdata", geth.HTTPAuthEndpoint(), common.Bytes2Hex(secret[:]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create new test suite: %v", err)
|
t.Fatalf("could not create new test suite: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range suite.SnapTests() {
|
for _, test := range suite.SnapTests() {
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
|
result := utesting.RunTests([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
|
||||||
if result[0].Failed {
|
if result[0].Failed {
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
|
|
@ -84,20 +105,23 @@ func TestSnapSuite(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// runGeth creates and starts a geth node
|
// runGeth creates and starts a geth node
|
||||||
func runGeth() (*node.Node, error) {
|
func runGeth(dir string, jwtPath string) (*node.Node, error) {
|
||||||
stack, err := node.New(&node.Config{
|
stack, err := node.New(&node.Config{
|
||||||
|
AuthAddr: "127.0.0.1",
|
||||||
|
AuthPort: 0,
|
||||||
P2P: p2p.Config{
|
P2P: p2p.Config{
|
||||||
ListenAddr: "127.0.0.1:0",
|
ListenAddr: "127.0.0.1:0",
|
||||||
NoDiscovery: true,
|
NoDiscovery: true,
|
||||||
MaxPeers: 10, // in case a test requires multiple connections, can be changed in the future
|
MaxPeers: 10, // in case a test requires multiple connections, can be changed in the future
|
||||||
NoDial: true,
|
NoDial: true,
|
||||||
},
|
},
|
||||||
|
JWTSecret: jwtPath,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = setupGeth(stack)
|
err = setupGeth(stack, dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stack.Close()
|
stack.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -111,12 +135,11 @@ func runGeth() (*node.Node, error) {
|
||||||
return stack, nil
|
return stack, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupGeth(stack *node.Node) error {
|
func setupGeth(stack *node.Node, dir string) error {
|
||||||
chain, err := loadChain(halfchainFile, genesisFile)
|
chain, err := NewChain(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
backend, err := eth.New(stack, ðconfig.Config{
|
backend, err := eth.New(stack, ðconfig.Config{
|
||||||
Genesis: &chain.genesis,
|
Genesis: &chain.genesis,
|
||||||
NetworkId: chain.genesis.Config.ChainID.Uint64(), // 19763
|
NetworkId: chain.genesis.Config.ChainID.Uint64(), // 19763
|
||||||
|
|
@ -129,8 +152,9 @@ func setupGeth(stack *node.Node) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
backend.SetSynced()
|
if err := catalyst.Register(stack, backend); err != nil {
|
||||||
|
return fmt.Errorf("failed to register catalyst service: %v", err)
|
||||||
|
}
|
||||||
_, err = backend.BlockChain().InsertChain(chain.blocks[1:])
|
_, err = backend.BlockChain().InsertChain(chain.blocks[1:])
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
62
cmd/devp2p/internal/ethtest/testdata/accounts.json
vendored
Normal file
62
cmd/devp2p/internal/ethtest/testdata/accounts.json
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"0x0c2c51a0990aee1d73c1228de158688341557508": {
|
||||||
|
"key": "0xbfcd0e032489319f4e5ca03e643b2025db624be6cf99cbfed90c4502e3754850"
|
||||||
|
},
|
||||||
|
"0x14e46043e63d0e3cdcf2530519f4cfaf35058cb2": {
|
||||||
|
"key": "0x457075f6822ac29481154792f65c5f1ec335b4fea9ca20f3fea8fa1d78a12c68"
|
||||||
|
},
|
||||||
|
"0x16c57edf7fa9d9525378b0b81bf8a3ced0620c1c": {
|
||||||
|
"key": "0x865898edcf43206d138c93f1bbd86311f4657b057658558888aa5ac4309626a6"
|
||||||
|
},
|
||||||
|
"0x1f4924b14f34e24159387c0a4cdbaa32f3ddb0cf": {
|
||||||
|
"key": "0xee7f7875d826d7443ccc5c174e38b2c436095018774248a8074ee92d8914dcdb"
|
||||||
|
},
|
||||||
|
"0x1f5bde34b4afc686f136c7a3cb6ec376f7357759": {
|
||||||
|
"key": "0x25e6ce8611cefb5cd338aeaa9292ed2139714668d123a4fb156cabb42051b5b7"
|
||||||
|
},
|
||||||
|
"0x2d389075be5be9f2246ad654ce152cf05990b209": {
|
||||||
|
"key": "0x19168cd7767604b3d19b99dc3da1302b9ccb6ee9ad61660859e07acd4a2625dd"
|
||||||
|
},
|
||||||
|
"0x3ae75c08b4c907eb63a8960c45b86e1e9ab6123c": {
|
||||||
|
"key": "0x71aa7d299c7607dabfc3d0e5213d612b5e4a97455b596c2f642daac43fa5eeaa"
|
||||||
|
},
|
||||||
|
"0x4340ee1b812acb40a1eb561c019c327b243b92df": {
|
||||||
|
"key": "0x47f666f20e2175606355acec0ea1b37870c15e5797e962340da7ad7972a537e8"
|
||||||
|
},
|
||||||
|
"0x4a0f1452281bcec5bd90c3dce6162a5995bfe9df": {
|
||||||
|
"key": "0xa88293fefc623644969e2ce6919fb0dbd0fd64f640293b4bf7e1a81c97e7fc7f"
|
||||||
|
},
|
||||||
|
"0x4dde844b71bcdf95512fb4dc94e84fb67b512ed8": {
|
||||||
|
"key": "0x6e1e16a9c15641c73bf6e237f9293ab1d4e7c12b9adf83cfc94bcf969670f72d"
|
||||||
|
},
|
||||||
|
"0x5f552da00dfb4d3749d9e62dcee3c918855a86a0": {
|
||||||
|
"key": "0x41be4e00aac79f7ffbb3455053ec05e971645440d594c047cdcc56a3c7458bd6"
|
||||||
|
},
|
||||||
|
"0x654aa64f5fbefb84c270ec74211b81ca8c44a72e": {
|
||||||
|
"key": "0xc825f31cd8792851e33a290b3d749e553983111fc1f36dfbbdb45f101973f6a9"
|
||||||
|
},
|
||||||
|
"0x717f8aa2b982bee0e29f573d31df288663e1ce16": {
|
||||||
|
"key": "0x8d0faa04ae0f9bc3cd4c890aa025d5f40916f4729538b19471c0beefe11d9e19"
|
||||||
|
},
|
||||||
|
"0x7435ed30a8b4aeb0877cef0c6e8cffe834eb865f": {
|
||||||
|
"key": "0x4552dbe6ca4699322b5d923d0c9bcdd24644f5db8bf89a085b67c6c49b8a1b91"
|
||||||
|
},
|
||||||
|
"0x83c7e323d189f18725ac510004fdc2941f8c4a78": {
|
||||||
|
"key": "0x34391cbbf06956bb506f45ec179cdd84df526aa364e27bbde65db9c15d866d00"
|
||||||
|
},
|
||||||
|
"0x84e75c28348fb86acea1a93a39426d7d60f4cc46": {
|
||||||
|
"key": "0xf6a8f1603b8368f3ca373292b7310c53bec7b508aecacd442554ebc1c5d0c856"
|
||||||
|
},
|
||||||
|
"0xc7b99a164efd027a93f147376cc7da7c67c6bbe0": {
|
||||||
|
"key": "0x8d56bcbcf2c1b7109e1396a28d7a0234e33544ade74ea32c460ce4a443b239b1"
|
||||||
|
},
|
||||||
|
"0xd803681e487e6ac18053afc5a6cd813c86ec3e4d": {
|
||||||
|
"key": "0xfc39d1c9ddbba176d806ebb42d7460189fe56ca163ad3eb6143bfc6beb6f6f72"
|
||||||
|
},
|
||||||
|
"0xe7d13f7aa2a838d24c59b40186a0aca1e21cffcc": {
|
||||||
|
"key": "0x9ee3fd550664b246ad7cdba07162dd25530a3b1d51476dd1d85bbc29f0592684"
|
||||||
|
},
|
||||||
|
"0xeda8645ba6948855e3b3cd596bbb07596d59c603": {
|
||||||
|
"key": "0x14cdde09d1640eb8c3cda063891b0453073f57719583381ff78811efa6d4199f"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
cmd/devp2p/internal/ethtest/testdata/chain.rlp
vendored
BIN
cmd/devp2p/internal/ethtest/testdata/chain.rlp
vendored
Binary file not shown.
20
cmd/devp2p/internal/ethtest/testdata/forkenv.json
vendored
Normal file
20
cmd/devp2p/internal/ethtest/testdata/forkenv.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"HIVE_CANCUN_TIMESTAMP": "840",
|
||||||
|
"HIVE_CHAIN_ID": "3503995874084926",
|
||||||
|
"HIVE_FORK_ARROW_GLACIER": "60",
|
||||||
|
"HIVE_FORK_BERLIN": "48",
|
||||||
|
"HIVE_FORK_BYZANTIUM": "18",
|
||||||
|
"HIVE_FORK_CONSTANTINOPLE": "24",
|
||||||
|
"HIVE_FORK_GRAY_GLACIER": "66",
|
||||||
|
"HIVE_FORK_HOMESTEAD": "0",
|
||||||
|
"HIVE_FORK_ISTANBUL": "36",
|
||||||
|
"HIVE_FORK_LONDON": "54",
|
||||||
|
"HIVE_FORK_MUIR_GLACIER": "42",
|
||||||
|
"HIVE_FORK_PETERSBURG": "30",
|
||||||
|
"HIVE_FORK_SPURIOUS": "12",
|
||||||
|
"HIVE_FORK_TANGERINE": "6",
|
||||||
|
"HIVE_MERGE_BLOCK_ID": "72",
|
||||||
|
"HIVE_NETWORK_ID": "3503995874084926",
|
||||||
|
"HIVE_SHANGHAI_TIMESTAMP": "780",
|
||||||
|
"HIVE_TERMINAL_TOTAL_DIFFICULTY": "9454784"
|
||||||
|
}
|
||||||
133
cmd/devp2p/internal/ethtest/testdata/genesis.json
vendored
133
cmd/devp2p/internal/ethtest/testdata/genesis.json
vendored
|
|
@ -1,27 +1,112 @@
|
||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"chainId": 19763,
|
"chainId": 3503995874084926,
|
||||||
"homesteadBlock": 0,
|
"homesteadBlock": 0,
|
||||||
"eip150Block": 0,
|
"eip150Block": 6,
|
||||||
"eip155Block": 0,
|
"eip155Block": 12,
|
||||||
"eip158Block": 0,
|
"eip158Block": 12,
|
||||||
"byzantiumBlock": 0,
|
"byzantiumBlock": 18,
|
||||||
"terminalTotalDifficultyPassed": true,
|
"constantinopleBlock": 24,
|
||||||
"ethash": {}
|
"petersburgBlock": 30,
|
||||||
|
"istanbulBlock": 36,
|
||||||
|
"muirGlacierBlock": 42,
|
||||||
|
"berlinBlock": 48,
|
||||||
|
"londonBlock": 54,
|
||||||
|
"arrowGlacierBlock": 60,
|
||||||
|
"grayGlacierBlock": 66,
|
||||||
|
"mergeNetsplitBlock": 72,
|
||||||
|
"shanghaiTime": 780,
|
||||||
|
"cancunTime": 840,
|
||||||
|
"terminalTotalDifficulty": 9454784,
|
||||||
|
"terminalTotalDifficultyPassed": true,
|
||||||
|
"ethash": {}
|
||||||
|
},
|
||||||
|
"nonce": "0x0",
|
||||||
|
"timestamp": "0x0",
|
||||||
|
"extraData": "0x68697665636861696e",
|
||||||
|
"gasLimit": "0x23f3e20",
|
||||||
|
"difficulty": "0x20000",
|
||||||
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
|
"alloc": {
|
||||||
|
"000f3df6d732807ef1319fb7b8bb8522d0beac02": {
|
||||||
|
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
|
||||||
|
"balance": "0x2a"
|
||||||
},
|
},
|
||||||
"nonce": "0xdeadbeefdeadbeef",
|
"0c2c51a0990aee1d73c1228de158688341557508": {
|
||||||
"timestamp": "0x0",
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
"gasLimit": "0x80000000",
|
|
||||||
"difficulty": "0x20000",
|
|
||||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
|
||||||
"alloc": {
|
|
||||||
"71562b71999873db5b286df957af199ec94617f7": {
|
|
||||||
"balance": "0xffffffffffffffffffffffffff"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"number": "0x0",
|
"14e46043e63d0e3cdcf2530519f4cfaf35058cb2": {
|
||||||
"gasUsed": "0x0",
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
},
|
||||||
}
|
"16c57edf7fa9d9525378b0b81bf8a3ced0620c1c": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"1f4924b14f34e24159387c0a4cdbaa32f3ddb0cf": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"1f5bde34b4afc686f136c7a3cb6ec376f7357759": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"2d389075be5be9f2246ad654ce152cf05990b209": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"3ae75c08b4c907eb63a8960c45b86e1e9ab6123c": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"4340ee1b812acb40a1eb561c019c327b243b92df": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"4a0f1452281bcec5bd90c3dce6162a5995bfe9df": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"4dde844b71bcdf95512fb4dc94e84fb67b512ed8": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"5f552da00dfb4d3749d9e62dcee3c918855a86a0": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"654aa64f5fbefb84c270ec74211b81ca8c44a72e": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"717f8aa2b982bee0e29f573d31df288663e1ce16": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"7435ed30a8b4aeb0877cef0c6e8cffe834eb865f": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"83c7e323d189f18725ac510004fdc2941f8c4a78": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"84e75c28348fb86acea1a93a39426d7d60f4cc46": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"8bebc8ba651aee624937e7d897853ac30c95a067": {
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000003"
|
||||||
|
},
|
||||||
|
"balance": "0x1",
|
||||||
|
"nonce": "0x1"
|
||||||
|
},
|
||||||
|
"c7b99a164efd027a93f147376cc7da7c67c6bbe0": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"d803681e487e6ac18053afc5a6cd813c86ec3e4d": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"e7d13f7aa2a838d24c59b40186a0aca1e21cffcc": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
},
|
||||||
|
"eda8645ba6948855e3b3cd596bbb07596d59c603": {
|
||||||
|
"balance": "0xc097ce7bc90715b34b9f1000000000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"number": "0x0",
|
||||||
|
"gasUsed": "0x0",
|
||||||
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"baseFeePerGas": null,
|
||||||
|
"excessBlobGas": null,
|
||||||
|
"blobGasUsed": null
|
||||||
|
}
|
||||||
BIN
cmd/devp2p/internal/ethtest/testdata/halfchain.rlp
vendored
BIN
cmd/devp2p/internal/ethtest/testdata/halfchain.rlp
vendored
Binary file not shown.
23
cmd/devp2p/internal/ethtest/testdata/headblock.json
vendored
Normal file
23
cmd/devp2p/internal/ethtest/testdata/headblock.json
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"parentHash": "0x96a73007443980c5e0985dfbb45279aa496dadea16918ad42c65c0bf8122ec39",
|
||||||
|
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"miner": "0x0000000000000000000000000000000000000000",
|
||||||
|
"stateRoot": "0xea4c1f4d9fa8664c22574c5b2f948a78c4b1a753cebc1861e7fb5b1aa21c5a94",
|
||||||
|
"transactionsRoot": "0xecda39025fc4c609ce778d75eed0aa53b65ce1e3d1373b34bad8578cc31e5b48",
|
||||||
|
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"difficulty": "0x0",
|
||||||
|
"number": "0x1f4",
|
||||||
|
"gasLimit": "0x47e7c40",
|
||||||
|
"gasUsed": "0x5208",
|
||||||
|
"timestamp": "0x1388",
|
||||||
|
"extraData": "0x",
|
||||||
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"nonce": "0x0000000000000000",
|
||||||
|
"baseFeePerGas": "0x7",
|
||||||
|
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"blobGasUsed": "0x0",
|
||||||
|
"excessBlobGas": "0x0",
|
||||||
|
"parentBeaconBlockRoot": "0xf653da50cdff4733f13f7a5e338290e883bdf04adf3f112709728063ea965d6c",
|
||||||
|
"hash": "0x36a166f0dcd160fc5e5c61c9a7c2d7f236d9175bf27f43aaa2150e291f092ef7"
|
||||||
|
}
|
||||||
13
cmd/devp2p/internal/ethtest/testdata/headfcu.json
vendored
Normal file
13
cmd/devp2p/internal/ethtest/testdata/headfcu.json
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": "fcu500",
|
||||||
|
"method": "engine_forkchoiceUpdatedV3",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"headBlockHash": "0x36a166f0dcd160fc5e5c61c9a7c2d7f236d9175bf27f43aaa2150e291f092ef7",
|
||||||
|
"safeBlockHash": "0x36a166f0dcd160fc5e5c61c9a7c2d7f236d9175bf27f43aaa2150e291f092ef7",
|
||||||
|
"finalizedBlockHash": "0x36a166f0dcd160fc5e5c61c9a7c2d7f236d9175bf27f43aaa2150e291f092ef7"
|
||||||
|
},
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
4204
cmd/devp2p/internal/ethtest/testdata/headstate.json
vendored
Normal file
4204
cmd/devp2p/internal/ethtest/testdata/headstate.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
13268
cmd/devp2p/internal/ethtest/testdata/newpayload.json
vendored
Normal file
13268
cmd/devp2p/internal/ethtest/testdata/newpayload.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
3018
cmd/devp2p/internal/ethtest/testdata/txinfo.json
vendored
Normal file
3018
cmd/devp2p/internal/ethtest/testdata/txinfo.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -19,485 +19,161 @@ package ethtest
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// var faucetAddr = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7")
|
// sendTxs sends the given transactions to the node and
|
||||||
var faucetKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
// expects the node to accept and propagate them.
|
||||||
|
func (s *Suite) sendTxs(t *utesting.T, txs []*types.Transaction) error {
|
||||||
func (s *Suite) sendSuccessfulTxs(t *utesting.T) error {
|
// Open sending conn.
|
||||||
tests := []*types.Transaction{
|
sendConn, err := s.dial()
|
||||||
getNextTxFromChain(s),
|
|
||||||
unknownTx(s),
|
|
||||||
}
|
|
||||||
for i, tx := range tests {
|
|
||||||
if tx == nil {
|
|
||||||
return errors.New("could not find tx to send")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Testing tx propagation %d: sending tx %v %v %v\n", i, tx.Hash().String(), tx.GasPrice(), tx.Gas())
|
|
||||||
// get previous tx if exists for reference in case of old tx propagation
|
|
||||||
var prevTx *types.Transaction
|
|
||||||
if i != 0 {
|
|
||||||
prevTx = tests[i-1]
|
|
||||||
}
|
|
||||||
// write tx to connection
|
|
||||||
if err := sendSuccessfulTx(s, tx, prevTx); err != nil {
|
|
||||||
return fmt.Errorf("send successful tx test failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:typecheck, gocognit
|
|
||||||
func sendSuccessfulTx(s *Suite, tx *types.Transaction, prevTx *types.Transaction) error {
|
|
||||||
sendConn, recvConn, err := s.createSendAndRecvConns()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer sendConn.Close()
|
defer sendConn.Close()
|
||||||
defer recvConn.Close()
|
|
||||||
|
|
||||||
if err = sendConn.peer(s.chain, nil); err != nil {
|
if err = sendConn.peer(s.chain, nil); err != nil {
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
return fmt.Errorf("peering failed: %v", err)
|
||||||
}
|
}
|
||||||
// Send the transaction
|
|
||||||
if err = sendConn.Write(&Transactions{tx}); err != nil {
|
|
||||||
return fmt.Errorf("failed to write to connection: %v", err)
|
|
||||||
}
|
|
||||||
// peer receiving connection to node
|
|
||||||
if err = recvConn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// update last nonce seen
|
// Open receiving conn.
|
||||||
nonce = tx.Nonce()
|
|
||||||
|
|
||||||
// Wait for the transaction announcement
|
|
||||||
for {
|
|
||||||
switch msg := recvConn.readAndServe(s.chain, timeout).(type) {
|
|
||||||
case *Transactions:
|
|
||||||
recTxs := *msg
|
|
||||||
// if you receive an old tx propagation, read from connection again
|
|
||||||
if len(recTxs) == 1 && prevTx != nil {
|
|
||||||
if recTxs[0] == prevTx {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, gotTx := range recTxs {
|
|
||||||
if gotTx.Hash() == tx.Hash() {
|
|
||||||
// Ok
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("missing transaction: got %v missing %v", recTxs, tx.Hash())
|
|
||||||
case *NewPooledTransactionHashes66:
|
|
||||||
txHashes := *msg
|
|
||||||
// if you receive an old tx propagation, read from connection again
|
|
||||||
if len(txHashes) == 1 && prevTx != nil {
|
|
||||||
if txHashes[0] == prevTx.Hash() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, gotHash := range txHashes {
|
|
||||||
if gotHash == tx.Hash() {
|
|
||||||
// Ok
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("missing transaction announcement: got %v missing %v", txHashes, tx.Hash())
|
|
||||||
case *NewPooledTransactionHashes:
|
|
||||||
txHashes := msg.Hashes
|
|
||||||
if len(txHashes) != len(msg.Sizes) {
|
|
||||||
return fmt.Errorf("invalid msg size lengths: hashes: %v sizes: %v", len(txHashes), len(msg.Sizes))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(txHashes) != len(msg.Types) {
|
|
||||||
return fmt.Errorf("invalid msg type lengths: hashes: %v types: %v", len(txHashes), len(msg.Types))
|
|
||||||
}
|
|
||||||
// if you receive an old tx propagation, read from connection again
|
|
||||||
if len(txHashes) == 1 && prevTx != nil {
|
|
||||||
if txHashes[0] == prevTx.Hash() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for index, gotHash := range txHashes {
|
|
||||||
if gotHash == tx.Hash() {
|
|
||||||
if msg.Sizes[index] != uint32(tx.Size()) {
|
|
||||||
return fmt.Errorf("invalid tx size: got %v want %v", msg.Sizes[index], tx.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.Types[index] != tx.Type() {
|
|
||||||
return fmt.Errorf("invalid tx type: got %v want %v", msg.Types[index], tx.Type())
|
|
||||||
}
|
|
||||||
// Ok
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("missing transaction announcement: got %v missing %v", txHashes, tx.Hash())
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unexpected message in sendSuccessfulTx: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) sendMaliciousTxs(t *utesting.T) error {
|
|
||||||
badTxs := []*types.Transaction{
|
|
||||||
getOldTxFromChain(s),
|
|
||||||
invalidNonceTx(s),
|
|
||||||
hugeAmount(s),
|
|
||||||
hugeGasPrice(s),
|
|
||||||
hugeData(s),
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup receiving connection before sending malicious txs
|
|
||||||
recvConn, err := s.dial()
|
recvConn, err := s.dial()
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer recvConn.Close()
|
|
||||||
|
|
||||||
if err = recvConn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tx := range badTxs {
|
|
||||||
t.Logf("Testing malicious tx propagation: %v\n", i)
|
|
||||||
|
|
||||||
if err = sendMaliciousTx(s, tx); err != nil {
|
|
||||||
return fmt.Errorf("malicious tx test failed:\ntx: %v\nerror: %v", tx, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// check to make sure bad txs aren't propagated
|
|
||||||
return checkMaliciousTxPropagation(s, badTxs, recvConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendMaliciousTx(s *Suite, tx *types.Transaction) error {
|
|
||||||
conn, err := s.dial()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("dial failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
if err = conn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// write malicious tx
|
|
||||||
if err = conn.Write(&Transactions{tx}); err != nil {
|
|
||||||
return fmt.Errorf("failed to write to connection: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var nonce = uint64(99)
|
|
||||||
|
|
||||||
// sendMultipleSuccessfulTxs sends the given transactions to the node and
|
|
||||||
// expects the node to accept and propagate them.
|
|
||||||
func sendMultipleSuccessfulTxs(t *utesting.T, s *Suite, txs []*types.Transaction) error {
|
|
||||||
txMsg := Transactions(txs)
|
|
||||||
t.Logf("sending %d txs\n", len(txs))
|
|
||||||
|
|
||||||
sendConn, recvConn, err := s.createSendAndRecvConns()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer sendConn.Close()
|
|
||||||
defer recvConn.Close()
|
defer recvConn.Close()
|
||||||
|
|
||||||
if err = sendConn.peer(s.chain, nil); err != nil {
|
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = recvConn.peer(s.chain, nil); err != nil {
|
if err = recvConn.peer(s.chain, nil); err != nil {
|
||||||
return fmt.Errorf("peering failed: %v", err)
|
return fmt.Errorf("peering failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the transactions
|
if err = sendConn.Write(ethProto, eth.TransactionsMsg, eth.TransactionsPacket(txs)); err != nil {
|
||||||
if err = sendConn.Write(&txMsg); err != nil {
|
|
||||||
return fmt.Errorf("failed to write message to connection: %v", err)
|
return fmt.Errorf("failed to write message to connection: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update nonce
|
var (
|
||||||
nonce = txs[len(txs)-1].Nonce()
|
got = make(map[common.Hash]bool)
|
||||||
|
end = time.Now().Add(timeout)
|
||||||
|
)
|
||||||
|
|
||||||
// Wait for the transaction announcement(s) and make sure all sent txs are being propagated.
|
// Wait for the transaction announcements, make sure all txs ar propagated.
|
||||||
// all txs should be announced within a couple announcements.
|
for time.Now().Before(end) {
|
||||||
recvHashes := make([]common.Hash, 0)
|
msg, err := recvConn.ReadEth()
|
||||||
|
if err != nil {
|
||||||
for i := 0; i < 20; i++ {
|
return fmt.Errorf("failed to read from connection: %w", err)
|
||||||
switch msg := recvConn.readAndServe(s.chain, timeout).(type) {
|
}
|
||||||
case *Transactions:
|
switch msg := msg.(type) {
|
||||||
|
case *eth.TransactionsPacket:
|
||||||
for _, tx := range *msg {
|
for _, tx := range *msg {
|
||||||
recvHashes = append(recvHashes, tx.Hash())
|
got[tx.Hash()] = true
|
||||||
}
|
}
|
||||||
case *NewPooledTransactionHashes66:
|
case *eth.NewPooledTransactionHashesPacket:
|
||||||
recvHashes = append(recvHashes, *msg...)
|
for _, hash := range msg.Hashes {
|
||||||
case *NewPooledTransactionHashes:
|
got[hash] = true
|
||||||
recvHashes = append(recvHashes, msg.Hashes...)
|
}
|
||||||
|
case *eth.GetBlockHeadersPacket:
|
||||||
|
headers, err := s.chain.GetHeaders(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("invalid GetBlockHeaders request: %v", err)
|
||||||
|
}
|
||||||
|
recvConn.Write(ethProto, eth.BlockHeadersMsg, ð.BlockHeadersPacket{
|
||||||
|
RequestId: msg.RequestId,
|
||||||
|
BlockHeadersRequest: headers,
|
||||||
|
})
|
||||||
default:
|
default:
|
||||||
if !strings.Contains(pretty.Sdump(msg), "i/o timeout") {
|
return fmt.Errorf("unexpected eth wire msg: %s", pretty.Sdump(msg))
|
||||||
return fmt.Errorf("unexpected message while waiting to receive txs: %s", pretty.Sdump(msg))
|
}
|
||||||
|
|
||||||
|
// Check if all txs received.
|
||||||
|
allReceived := func() bool {
|
||||||
|
for _, tx := range txs {
|
||||||
|
if !got[tx.Hash()] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
// break once all 2000 txs have been received
|
if allReceived() {
|
||||||
if len(recvHashes) == 2000 {
|
return nil
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(recvHashes) > 0 {
|
|
||||||
_, missingTxs := compareReceivedTxs(recvHashes, txs)
|
|
||||||
if len(missingTxs) > 0 {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
t.Logf("successfully received all %d txs", len(txs))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, missingTxs := compareReceivedTxs(recvHashes, txs)
|
return fmt.Errorf("timed out waiting for txs")
|
||||||
if len(missingTxs) > 0 {
|
|
||||||
for _, missing := range missingTxs {
|
|
||||||
t.Logf("missing tx: %v", missing.Hash())
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("missing %d txs", len(missingTxs))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkMaliciousTxPropagation checks whether the given malicious transactions were
|
func (s *Suite) sendInvalidTxs(t *utesting.T, txs []*types.Transaction) error {
|
||||||
// propagated by the node.
|
// Open sending conn.
|
||||||
// nolint:typecheck
|
sendConn, err := s.dial()
|
||||||
func checkMaliciousTxPropagation(s *Suite, txs []*types.Transaction, conn *Conn) error {
|
|
||||||
switch msg := conn.readAndServe(s.chain, time.Second*8).(type) {
|
|
||||||
case *Transactions:
|
|
||||||
// check to see if any of the failing txs were in the announcement
|
|
||||||
recvTxs := make([]common.Hash, len(*msg))
|
|
||||||
for i, recvTx := range *msg {
|
|
||||||
recvTxs[i] = recvTx.Hash()
|
|
||||||
}
|
|
||||||
|
|
||||||
badTxs, _ := compareReceivedTxs(recvTxs, txs)
|
|
||||||
if len(badTxs) > 0 {
|
|
||||||
return fmt.Errorf("received %d bad txs: \n%v", len(badTxs), badTxs)
|
|
||||||
}
|
|
||||||
case *NewPooledTransactionHashes66:
|
|
||||||
badTxs, _ := compareReceivedTxs(*msg, txs)
|
|
||||||
if len(badTxs) > 0 {
|
|
||||||
return fmt.Errorf("received %d bad txs: \n%v", len(badTxs), badTxs)
|
|
||||||
}
|
|
||||||
case *NewPooledTransactionHashes:
|
|
||||||
badTxs, _ := compareReceivedTxs(msg.Hashes, txs)
|
|
||||||
if len(badTxs) > 0 {
|
|
||||||
return fmt.Errorf("received %d bad txs: \n%v", len(badTxs), badTxs)
|
|
||||||
}
|
|
||||||
case *Error:
|
|
||||||
// Transaction should not be announced -> wait for timeout
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unexpected message in sendFailingTx: %s", pretty.Sdump(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// compareReceivedTxs compares the received set of txs against the given set of txs,
|
|
||||||
// returning both the set received txs that were present within the given txs, and
|
|
||||||
// the set of txs that were missing from the set of received txs
|
|
||||||
func compareReceivedTxs(recvTxs []common.Hash, txs []*types.Transaction) (present []*types.Transaction, missing []*types.Transaction) {
|
|
||||||
// create a map of the hashes received from node
|
|
||||||
recvHashes := make(map[common.Hash]common.Hash)
|
|
||||||
for _, hash := range recvTxs {
|
|
||||||
recvHashes[hash] = hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect present txs and missing txs separately
|
|
||||||
present = make([]*types.Transaction, 0)
|
|
||||||
missing = make([]*types.Transaction, 0)
|
|
||||||
|
|
||||||
for _, tx := range txs {
|
|
||||||
if _, exists := recvHashes[tx.Hash()]; exists {
|
|
||||||
present = append(present, tx)
|
|
||||||
} else {
|
|
||||||
missing = append(missing, tx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return present, missing
|
|
||||||
}
|
|
||||||
|
|
||||||
func unknownTx(s *Suite) *types.Transaction {
|
|
||||||
tx := getNextTxFromChain(s)
|
|
||||||
if tx == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var to common.Address
|
|
||||||
if tx.To() != nil {
|
|
||||||
to = *tx.To()
|
|
||||||
}
|
|
||||||
|
|
||||||
txNew := types.NewTransaction(tx.Nonce()+1, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data())
|
|
||||||
|
|
||||||
return signWithFaucet(s.chain.chainConfig, txNew)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNextTxFromChain(s *Suite) *types.Transaction {
|
|
||||||
// Get a new transaction
|
|
||||||
for _, blocks := range s.fullChain.blocks[s.chain.Len():] {
|
|
||||||
txs := blocks.Transactions()
|
|
||||||
if txs.Len() != 0 {
|
|
||||||
return txs[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateTxs(s *Suite, numTxs int) (map[common.Hash]common.Hash, []*types.Transaction, error) {
|
|
||||||
txHashMap := make(map[common.Hash]common.Hash, numTxs)
|
|
||||||
txs := make([]*types.Transaction, numTxs)
|
|
||||||
|
|
||||||
nextTx := getNextTxFromChain(s)
|
|
||||||
if nextTx == nil {
|
|
||||||
return nil, nil, errors.New("failed to get the next transaction")
|
|
||||||
}
|
|
||||||
|
|
||||||
gas := nextTx.Gas()
|
|
||||||
|
|
||||||
nonce = nonce + 1
|
|
||||||
// generate txs
|
|
||||||
for i := 0; i < numTxs; i++ {
|
|
||||||
tx := generateTx(s.chain.chainConfig, nonce, gas)
|
|
||||||
if tx == nil {
|
|
||||||
return nil, nil, errors.New("failed to get the next transaction")
|
|
||||||
}
|
|
||||||
|
|
||||||
txHashMap[tx.Hash()] = tx.Hash()
|
|
||||||
txs[i] = tx
|
|
||||||
nonce = nonce + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return txHashMap, txs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateTx(chainConfig *params.ChainConfig, nonce uint64, gas uint64) *types.Transaction {
|
|
||||||
var to common.Address
|
|
||||||
tx := types.NewTransaction(nonce, to, big.NewInt(1), gas, big.NewInt(1), []byte{})
|
|
||||||
|
|
||||||
return signWithFaucet(chainConfig, tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOldTxFromChain(s *Suite) *types.Transaction {
|
|
||||||
for _, blocks := range s.fullChain.blocks[:s.chain.Len()-1] {
|
|
||||||
txs := blocks.Transactions()
|
|
||||||
if txs.Len() != 0 {
|
|
||||||
return txs[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func invalidNonceTx(s *Suite) *types.Transaction {
|
|
||||||
tx := getNextTxFromChain(s)
|
|
||||||
if tx == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var to common.Address
|
|
||||||
if tx.To() != nil {
|
|
||||||
to = *tx.To()
|
|
||||||
}
|
|
||||||
|
|
||||||
txNew := types.NewTransaction(tx.Nonce()-2, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data())
|
|
||||||
|
|
||||||
return signWithFaucet(s.chain.chainConfig, txNew)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hugeAmount(s *Suite) *types.Transaction {
|
|
||||||
tx := getNextTxFromChain(s)
|
|
||||||
if tx == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
amount := largeNumber(2)
|
|
||||||
|
|
||||||
var to common.Address
|
|
||||||
if tx.To() != nil {
|
|
||||||
to = *tx.To()
|
|
||||||
}
|
|
||||||
|
|
||||||
txNew := types.NewTransaction(tx.Nonce(), to, amount, tx.Gas(), tx.GasPrice(), tx.Data())
|
|
||||||
|
|
||||||
return signWithFaucet(s.chain.chainConfig, txNew)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hugeGasPrice(s *Suite) *types.Transaction {
|
|
||||||
tx := getNextTxFromChain(s)
|
|
||||||
if tx == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
gasPrice := largeNumber(2)
|
|
||||||
|
|
||||||
var to common.Address
|
|
||||||
if tx.To() != nil {
|
|
||||||
to = *tx.To()
|
|
||||||
}
|
|
||||||
|
|
||||||
txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), gasPrice, tx.Data())
|
|
||||||
|
|
||||||
return signWithFaucet(s.chain.chainConfig, txNew)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hugeData(s *Suite) *types.Transaction {
|
|
||||||
tx := getNextTxFromChain(s)
|
|
||||||
if tx == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var to common.Address
|
|
||||||
if tx.To() != nil {
|
|
||||||
to = *tx.To()
|
|
||||||
}
|
|
||||||
|
|
||||||
txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), tx.GasPrice(), largeBuffer(2))
|
|
||||||
|
|
||||||
return signWithFaucet(s.chain.chainConfig, txNew)
|
|
||||||
}
|
|
||||||
|
|
||||||
func signWithFaucet(chainConfig *params.ChainConfig, tx *types.Transaction) *types.Transaction {
|
|
||||||
signer := types.LatestSigner(chainConfig)
|
|
||||||
|
|
||||||
signedTx, err := types.SignTx(tx, signer, faucetKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
|
}
|
||||||
|
defer sendConn.Close()
|
||||||
|
if err = sendConn.peer(s.chain, nil); err != nil {
|
||||||
|
return fmt.Errorf("peering failed: %v", err)
|
||||||
|
}
|
||||||
|
sendConn.SetDeadline(time.Now().Add(timeout))
|
||||||
|
|
||||||
|
// Open receiving conn.
|
||||||
|
recvConn, err := s.dial()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer recvConn.Close()
|
||||||
|
if err = recvConn.peer(s.chain, nil); err != nil {
|
||||||
|
return fmt.Errorf("peering failed: %v", err)
|
||||||
|
}
|
||||||
|
recvConn.SetDeadline(time.Now().Add(timeout))
|
||||||
|
|
||||||
|
if err = sendConn.Write(ethProto, eth.TransactionsMsg, txs); err != nil {
|
||||||
|
return fmt.Errorf("failed to write message to connection: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return signedTx
|
// Make map of invalid txs.
|
||||||
|
invalids := make(map[common.Hash]struct{})
|
||||||
|
for _, tx := range txs {
|
||||||
|
invalids[tx.Hash()] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get responses.
|
||||||
|
recvConn.SetReadDeadline(time.Now().Add(timeout))
|
||||||
|
for {
|
||||||
|
msg, err := recvConn.ReadEth()
|
||||||
|
if errors.Is(err, os.ErrDeadlineExceeded) {
|
||||||
|
// Successful if no invalid txs are propagated before timeout.
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("failed to read from connection: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case *eth.TransactionsPacket:
|
||||||
|
for _, tx := range txs {
|
||||||
|
if _, ok := invalids[tx.Hash()]; ok {
|
||||||
|
return fmt.Errorf("received bad tx: %s", tx.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *eth.NewPooledTransactionHashesPacket:
|
||||||
|
for _, hash := range msg.Hashes {
|
||||||
|
if _, ok := invalids[hash]; ok {
|
||||||
|
return fmt.Errorf("received bad tx: %s", hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *eth.GetBlockHeadersPacket:
|
||||||
|
headers, err := s.chain.GetHeaders(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("invalid GetBlockHeaders request: %v", err)
|
||||||
|
}
|
||||||
|
recvConn.Write(ethProto, eth.BlockHeadersMsg, ð.BlockHeadersPacket{
|
||||||
|
RequestId: msg.RequestId,
|
||||||
|
BlockHeadersRequest: headers,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpected eth message: %v", pretty.Sdump(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,307 +0,0 @@
|
||||||
// Copyright 2020 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 ethtest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/rlpx"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Message interface {
|
|
||||||
Code() int
|
|
||||||
ReqID() uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type Error struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Error) Unwrap() error { return e.err }
|
|
||||||
func (e *Error) Error() string { return e.err.Error() }
|
|
||||||
func (e *Error) String() string { return e.Error() }
|
|
||||||
|
|
||||||
func (e *Error) Code() int { return -1 }
|
|
||||||
func (e *Error) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
func errorf(format string, args ...interface{}) *Error {
|
|
||||||
return &Error{fmt.Errorf(format, args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hello is the RLP structure of the protocol handshake.
|
|
||||||
type Hello struct {
|
|
||||||
Version uint64
|
|
||||||
Name string
|
|
||||||
Caps []p2p.Cap
|
|
||||||
ListenPort uint64
|
|
||||||
ID []byte // secp256k1 public key
|
|
||||||
|
|
||||||
// Ignore additional fields (for forward compatibility).
|
|
||||||
Rest []rlp.RawValue `rlp:"tail"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg Hello) Code() int { return 0x00 }
|
|
||||||
func (msg Hello) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
// Disconnect is the RLP structure for a disconnect message.
|
|
||||||
type Disconnect struct {
|
|
||||||
Reason p2p.DiscReason
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg Disconnect) Code() int { return 0x01 }
|
|
||||||
func (msg Disconnect) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
type Ping struct{}
|
|
||||||
|
|
||||||
func (msg Ping) Code() int { return 0x02 }
|
|
||||||
func (msg Ping) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
type Pong struct{}
|
|
||||||
|
|
||||||
func (msg Pong) Code() int { return 0x03 }
|
|
||||||
func (msg Pong) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
// Status is the network packet for the status message for eth/64 and later.
|
|
||||||
type Status eth.StatusPacket
|
|
||||||
|
|
||||||
func (msg Status) Code() int { return 16 }
|
|
||||||
func (msg Status) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
// NewBlockHashes is the network packet for the block announcements.
|
|
||||||
type NewBlockHashes eth.NewBlockHashesPacket
|
|
||||||
|
|
||||||
func (msg NewBlockHashes) Code() int { return 17 }
|
|
||||||
func (msg NewBlockHashes) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
type Transactions eth.TransactionsPacket
|
|
||||||
|
|
||||||
func (msg Transactions) Code() int { return 18 }
|
|
||||||
func (msg Transactions) ReqID() uint64 { return 18 }
|
|
||||||
|
|
||||||
// GetBlockHeaders represents a block header query.
|
|
||||||
type GetBlockHeaders eth.GetBlockHeadersPacket
|
|
||||||
|
|
||||||
func (msg GetBlockHeaders) Code() int { return 19 }
|
|
||||||
func (msg GetBlockHeaders) ReqID() uint64 { return msg.RequestId }
|
|
||||||
|
|
||||||
type BlockHeaders eth.BlockHeadersPacket
|
|
||||||
|
|
||||||
func (msg BlockHeaders) Code() int { return 20 }
|
|
||||||
func (msg BlockHeaders) ReqID() uint64 { return msg.RequestId }
|
|
||||||
|
|
||||||
// GetBlockBodies represents a GetBlockBodies request
|
|
||||||
type GetBlockBodies eth.GetBlockBodiesPacket
|
|
||||||
|
|
||||||
func (msg GetBlockBodies) Code() int { return 21 }
|
|
||||||
func (msg GetBlockBodies) ReqID() uint64 { return msg.RequestId }
|
|
||||||
|
|
||||||
// BlockBodies is the network packet for block content distribution.
|
|
||||||
type BlockBodies eth.BlockBodiesPacket
|
|
||||||
|
|
||||||
func (msg BlockBodies) Code() int { return 22 }
|
|
||||||
func (msg BlockBodies) ReqID() uint64 { return msg.RequestId }
|
|
||||||
|
|
||||||
// NewBlock is the network packet for the block propagation message.
|
|
||||||
type NewBlock eth.NewBlockPacket
|
|
||||||
|
|
||||||
func (msg NewBlock) Code() int { return 23 }
|
|
||||||
func (msg NewBlock) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
// NewPooledTransactionHashes66 is the network packet for the tx hash propagation message.
|
|
||||||
type NewPooledTransactionHashes66 eth.NewPooledTransactionHashesPacket67
|
|
||||||
|
|
||||||
func (msg NewPooledTransactionHashes66) Code() int { return 24 }
|
|
||||||
func (msg NewPooledTransactionHashes66) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
// NewPooledTransactionHashes is the network packet for the tx hash propagation message.
|
|
||||||
type NewPooledTransactionHashes eth.NewPooledTransactionHashesPacket68
|
|
||||||
|
|
||||||
func (msg NewPooledTransactionHashes) Code() int { return 24 }
|
|
||||||
func (msg NewPooledTransactionHashes) ReqID() uint64 { return 0 }
|
|
||||||
|
|
||||||
type GetPooledTransactions eth.GetPooledTransactionsPacket
|
|
||||||
|
|
||||||
func (msg GetPooledTransactions) Code() int { return 25 }
|
|
||||||
func (msg GetPooledTransactions) ReqID() uint64 { return msg.RequestId }
|
|
||||||
|
|
||||||
type PooledTransactions eth.PooledTransactionsPacket
|
|
||||||
|
|
||||||
func (msg PooledTransactions) Code() int { return 26 }
|
|
||||||
func (msg PooledTransactions) ReqID() uint64 { return msg.RequestId }
|
|
||||||
|
|
||||||
// Conn represents an individual connection with a peer
|
|
||||||
type Conn struct {
|
|
||||||
*rlpx.Conn
|
|
||||||
ourKey *ecdsa.PrivateKey
|
|
||||||
negotiatedProtoVersion uint
|
|
||||||
negotiatedSnapProtoVersion uint
|
|
||||||
ourHighestProtoVersion uint
|
|
||||||
ourHighestSnapProtoVersion uint
|
|
||||||
caps []p2p.Cap
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads an eth66 packet from the connection.
|
|
||||||
func (c *Conn) Read() Message {
|
|
||||||
code, rawData, _, err := c.Conn.Read()
|
|
||||||
if err != nil {
|
|
||||||
return errorf("could not read from connection: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg Message
|
|
||||||
|
|
||||||
switch int(code) {
|
|
||||||
case (Hello{}).Code():
|
|
||||||
msg = new(Hello)
|
|
||||||
case (Ping{}).Code():
|
|
||||||
msg = new(Ping)
|
|
||||||
case (Pong{}).Code():
|
|
||||||
msg = new(Pong)
|
|
||||||
case (Disconnect{}).Code():
|
|
||||||
msg = new(Disconnect)
|
|
||||||
case (Status{}).Code():
|
|
||||||
msg = new(Status)
|
|
||||||
case (GetBlockHeaders{}).Code():
|
|
||||||
ethMsg := new(eth.GetBlockHeadersPacket)
|
|
||||||
if err := rlp.DecodeBytes(rawData, ethMsg); err != nil {
|
|
||||||
return errorf("could not rlp decode message: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (*GetBlockHeaders)(ethMsg)
|
|
||||||
case (BlockHeaders{}).Code():
|
|
||||||
ethMsg := new(eth.BlockHeadersPacket)
|
|
||||||
if err := rlp.DecodeBytes(rawData, ethMsg); err != nil {
|
|
||||||
return errorf("could not rlp decode message: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (*BlockHeaders)(ethMsg)
|
|
||||||
case (GetBlockBodies{}).Code():
|
|
||||||
ethMsg := new(eth.GetBlockBodiesPacket)
|
|
||||||
if err := rlp.DecodeBytes(rawData, ethMsg); err != nil {
|
|
||||||
return errorf("could not rlp decode message: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (*GetBlockBodies)(ethMsg)
|
|
||||||
case (BlockBodies{}).Code():
|
|
||||||
ethMsg := new(eth.BlockBodiesPacket)
|
|
||||||
if err := rlp.DecodeBytes(rawData, ethMsg); err != nil {
|
|
||||||
return errorf("could not rlp decode message: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (*BlockBodies)(ethMsg)
|
|
||||||
case (NewBlock{}).Code():
|
|
||||||
msg = new(NewBlock)
|
|
||||||
case (NewBlockHashes{}).Code():
|
|
||||||
msg = new(NewBlockHashes)
|
|
||||||
case (Transactions{}).Code():
|
|
||||||
msg = new(Transactions)
|
|
||||||
case (NewPooledTransactionHashes66{}).Code():
|
|
||||||
// Try decoding to eth68
|
|
||||||
ethMsg := new(NewPooledTransactionHashes)
|
|
||||||
if err := rlp.DecodeBytes(rawData, ethMsg); err == nil {
|
|
||||||
return ethMsg
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = new(NewPooledTransactionHashes66)
|
|
||||||
case (GetPooledTransactions{}.Code()):
|
|
||||||
ethMsg := new(eth.GetPooledTransactionsPacket)
|
|
||||||
if err := rlp.DecodeBytes(rawData, ethMsg); err != nil {
|
|
||||||
return errorf("could not rlp decode message: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (*GetPooledTransactions)(ethMsg)
|
|
||||||
case (PooledTransactions{}.Code()):
|
|
||||||
ethMsg := new(eth.PooledTransactionsPacket)
|
|
||||||
if err := rlp.DecodeBytes(rawData, ethMsg); err != nil {
|
|
||||||
return errorf("could not rlp decode message: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (*PooledTransactions)(ethMsg)
|
|
||||||
default:
|
|
||||||
msg = errorf("invalid message code: %d", code)
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg != nil {
|
|
||||||
if err := rlp.DecodeBytes(rawData, msg); err != nil {
|
|
||||||
return errorf("could not rlp decode message: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
return errorf("invalid message: %s", string(rawData))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes a eth packet to the connection.
|
|
||||||
func (c *Conn) Write(msg Message) error {
|
|
||||||
payload, err := rlp.EncodeToBytes(msg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = c.Conn.Write(uint64(msg.Code()), payload)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadSnap reads a snap/1 response with the given id from the connection.
|
|
||||||
func (c *Conn) ReadSnap(id uint64) (Message, error) {
|
|
||||||
respId := id + 1
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
for respId != id && time.Since(start) < timeout {
|
|
||||||
code, rawData, _, err := c.Conn.Read()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not read from connection: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var snpMsg interface{}
|
|
||||||
switch int(code) {
|
|
||||||
case (GetAccountRange{}).Code():
|
|
||||||
snpMsg = new(GetAccountRange)
|
|
||||||
case (AccountRange{}).Code():
|
|
||||||
snpMsg = new(AccountRange)
|
|
||||||
case (GetStorageRanges{}).Code():
|
|
||||||
snpMsg = new(GetStorageRanges)
|
|
||||||
case (StorageRanges{}).Code():
|
|
||||||
snpMsg = new(StorageRanges)
|
|
||||||
case (GetByteCodes{}).Code():
|
|
||||||
snpMsg = new(GetByteCodes)
|
|
||||||
case (ByteCodes{}).Code():
|
|
||||||
snpMsg = new(ByteCodes)
|
|
||||||
case (GetTrieNodes{}).Code():
|
|
||||||
snpMsg = new(GetTrieNodes)
|
|
||||||
case (TrieNodes{}).Code():
|
|
||||||
snpMsg = new(TrieNodes)
|
|
||||||
default:
|
|
||||||
//return nil, fmt.Errorf("invalid message code: %d", code)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rlp.DecodeBytes(rawData, snpMsg); err != nil {
|
|
||||||
return nil, fmt.Errorf("could not rlp decode message: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return snpMsg.(Message), nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("request timed out")
|
|
||||||
}
|
|
||||||
|
|
@ -21,13 +21,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/devp2p/internal/ethtest"
|
"github.com/ethereum/go-ethereum/cmd/devp2p/internal/ethtest"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/p2p/rlpx"
|
"github.com/ethereum/go-ethereum/p2p/rlpx"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -47,93 +47,119 @@ var (
|
||||||
}
|
}
|
||||||
rlpxEthTestCommand = &cli.Command{
|
rlpxEthTestCommand = &cli.Command{
|
||||||
Name: "eth-test",
|
Name: "eth-test",
|
||||||
Usage: "Runs tests against a node",
|
Usage: "Runs eth protocol tests against a node",
|
||||||
ArgsUsage: "<node> <chain.rlp> <genesis.json>",
|
ArgsUsage: "<node>",
|
||||||
Action: rlpxEthTest,
|
Action: rlpxEthTest,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
testPatternFlag,
|
testPatternFlag,
|
||||||
testTAPFlag,
|
testTAPFlag,
|
||||||
|
testChainDirFlag,
|
||||||
|
testNodeFlag,
|
||||||
|
testNodeJWTFlag,
|
||||||
|
testNodeEngineFlag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
rlpxSnapTestCommand = &cli.Command{
|
rlpxSnapTestCommand = &cli.Command{
|
||||||
Name: "snap-test",
|
Name: "snap-test",
|
||||||
Usage: "Runs tests against a node",
|
Usage: "Runs snap protocol tests against a node",
|
||||||
ArgsUsage: "<node> <chain.rlp> <genesis.json>",
|
ArgsUsage: "",
|
||||||
Action: rlpxSnapTest,
|
Action: rlpxSnapTest,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
testPatternFlag,
|
testPatternFlag,
|
||||||
testTAPFlag,
|
testTAPFlag,
|
||||||
|
testChainDirFlag,
|
||||||
|
testNodeFlag,
|
||||||
|
testNodeJWTFlag,
|
||||||
|
testNodeEngineFlag,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func rlpxPing(ctx *cli.Context) error {
|
func rlpxPing(ctx *cli.Context) error {
|
||||||
n := getNodeArg(ctx)
|
n := getNodeArg(ctx)
|
||||||
|
|
||||||
fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", n.IP(), n.TCP()))
|
fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", n.IP(), n.TCP()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conn := rlpx.NewConn(fd, n.Pubkey())
|
conn := rlpx.NewConn(fd, n.Pubkey())
|
||||||
ourKey, _ := crypto.GenerateKey()
|
ourKey, _ := crypto.GenerateKey()
|
||||||
|
|
||||||
_, err = conn.Handshake(ourKey)
|
_, err = conn.Handshake(ourKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
code, data, _, err := conn.Read()
|
code, data, _, err := conn.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch code {
|
switch code {
|
||||||
case 0:
|
case 0:
|
||||||
var h ethtest.Hello
|
var h ethtest.Hello
|
||||||
if err := rlp.DecodeBytes(data, &h); err != nil {
|
if err := rlp.DecodeBytes(data, &h); err != nil {
|
||||||
return fmt.Errorf("invalid handshake: %v", err)
|
return fmt.Errorf("invalid handshake: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%+v\n", h)
|
fmt.Printf("%+v\n", h)
|
||||||
case 1:
|
case 1:
|
||||||
var msg []p2p.DiscReason
|
var msg []p2p.DiscReason
|
||||||
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
|
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
|
||||||
return errors.New("invalid disconnect message")
|
return errors.New("invalid disconnect message")
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("received disconnect message: %v", msg[0])
|
return fmt.Errorf("received disconnect message: %v", msg[0])
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid message code %d, expected handshake (code zero)", code)
|
return fmt.Errorf("invalid message code %d, expected handshake (code zero)", code)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// rlpxEthTest runs the eth protocol test suite.
|
// rlpxEthTest runs the eth protocol test suite.
|
||||||
func rlpxEthTest(ctx *cli.Context) error {
|
func rlpxEthTest(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 3 {
|
p := cliTestParams(ctx)
|
||||||
exit("missing path to chain.rlp as command-line argument")
|
suite, err := ethtest.NewSuite(p.node, p.chainDir, p.engineAPI, p.jwt)
|
||||||
}
|
|
||||||
|
|
||||||
suite, err := ethtest.NewSuite(getNodeArg(ctx), ctx.Args().Get(1), ctx.Args().Get(2))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return runTests(ctx, suite.EthTests())
|
return runTests(ctx, suite.EthTests())
|
||||||
}
|
}
|
||||||
|
|
||||||
// rlpxSnapTest runs the snap protocol test suite.
|
// rlpxSnapTest runs the snap protocol test suite.
|
||||||
func rlpxSnapTest(ctx *cli.Context) error {
|
func rlpxSnapTest(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 3 {
|
p := cliTestParams(ctx)
|
||||||
exit("missing path to chain.rlp as command-line argument")
|
suite, err := ethtest.NewSuite(p.node, p.chainDir, p.engineAPI, p.jwt)
|
||||||
}
|
|
||||||
|
|
||||||
suite, err := ethtest.NewSuite(getNodeArg(ctx), ctx.Args().Get(1), ctx.Args().Get(2))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return runTests(ctx, suite.SnapTests())
|
return runTests(ctx, suite.SnapTests())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testParams struct {
|
||||||
|
node *enode.Node
|
||||||
|
engineAPI string
|
||||||
|
jwt string
|
||||||
|
chainDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func cliTestParams(ctx *cli.Context) *testParams {
|
||||||
|
nodeStr := ctx.String(testNodeFlag.Name)
|
||||||
|
if nodeStr == "" {
|
||||||
|
exit(fmt.Errorf("missing -%s", testNodeFlag.Name))
|
||||||
|
}
|
||||||
|
node, err := parseNode(nodeStr)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
p := testParams{
|
||||||
|
node: node,
|
||||||
|
engineAPI: ctx.String(testNodeEngineFlag.Name),
|
||||||
|
jwt: ctx.String(testNodeJWTFlag.Name),
|
||||||
|
chainDir: ctx.String(testChainDirFlag.Name),
|
||||||
|
}
|
||||||
|
if p.engineAPI == "" {
|
||||||
|
exit(fmt.Errorf("missing -%s", testNodeEngineFlag.Name))
|
||||||
|
}
|
||||||
|
if p.jwt == "" {
|
||||||
|
exit(fmt.Errorf("missing -%s", testNodeJWTFlag.Name))
|
||||||
|
}
|
||||||
|
if p.chainDir == "" {
|
||||||
|
exit(fmt.Errorf("missing -%s", testChainDirFlag.Name))
|
||||||
|
}
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,29 +22,58 @@ import (
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/devp2p/internal/v4test"
|
"github.com/ethereum/go-ethereum/cmd/devp2p/internal/v4test"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testPatternFlag = &cli.StringFlag{
|
testPatternFlag = &cli.StringFlag{
|
||||||
Name: "run",
|
Name: "run",
|
||||||
Usage: "Pattern of test suite(s) to run",
|
Usage: "Pattern of test suite(s) to run",
|
||||||
|
Category: flags.TestingCategory,
|
||||||
}
|
}
|
||||||
testTAPFlag = &cli.BoolFlag{
|
testTAPFlag = &cli.BoolFlag{
|
||||||
Name: "tap",
|
Name: "tap",
|
||||||
Usage: "Output TAP",
|
Usage: "Output test results in TAP format",
|
||||||
|
Category: flags.TestingCategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for eth/snap tests
|
||||||
|
testChainDirFlag = &cli.StringFlag{
|
||||||
|
Name: "chain",
|
||||||
|
Usage: "Test chain directory (required)",
|
||||||
|
Category: flags.TestingCategory,
|
||||||
|
}
|
||||||
|
testNodeFlag = &cli.StringFlag{
|
||||||
|
Name: "node",
|
||||||
|
Usage: "Peer-to-Peer endpoint (ENR) of the test node (required)",
|
||||||
|
Category: flags.TestingCategory,
|
||||||
|
}
|
||||||
|
testNodeJWTFlag = &cli.StringFlag{
|
||||||
|
Name: "jwtsecret",
|
||||||
|
Usage: "JWT secret for the engine API of the test node (required)",
|
||||||
|
Category: flags.TestingCategory,
|
||||||
|
Value: "0x7365637265747365637265747365637265747365637265747365637265747365",
|
||||||
|
}
|
||||||
|
testNodeEngineFlag = &cli.StringFlag{
|
||||||
|
Name: "engineapi",
|
||||||
|
Usage: "Engine API endpoint of the test node (required)",
|
||||||
|
Category: flags.TestingCategory,
|
||||||
|
}
|
||||||
|
|
||||||
// These two are specific to the discovery tests.
|
// These two are specific to the discovery tests.
|
||||||
testListen1Flag = &cli.StringFlag{
|
testListen1Flag = &cli.StringFlag{
|
||||||
Name: "listen1",
|
Name: "listen1",
|
||||||
Usage: "IP address of the first tester",
|
Usage: "IP address of the first tester",
|
||||||
Value: v4test.Listen1,
|
Value: v4test.Listen1,
|
||||||
|
Category: flags.TestingCategory,
|
||||||
}
|
}
|
||||||
testListen2Flag = &cli.StringFlag{
|
testListen2Flag = &cli.StringFlag{
|
||||||
Name: "listen2",
|
Name: "listen2",
|
||||||
Usage: "IP address of the second tester",
|
Usage: "IP address of the second tester",
|
||||||
Value: v4test.Listen2,
|
Value: v4test.Listen2,
|
||||||
|
Category: flags.TestingCategory,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
324
cmd/era/main.go
Normal file
324
cmd/era/main.go
Normal file
|
|
@ -0,0 +1,324 @@
|
||||||
|
// Copyright 2023 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 (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/era"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var app = flags.NewApp("go-ethereum era tool")
|
||||||
|
|
||||||
|
var (
|
||||||
|
dirFlag = &cli.StringFlag{
|
||||||
|
Name: "dir",
|
||||||
|
Usage: "directory storing all relevant era1 files",
|
||||||
|
Value: "eras",
|
||||||
|
}
|
||||||
|
networkFlag = &cli.StringFlag{
|
||||||
|
Name: "network",
|
||||||
|
Usage: "network name associated with era1 files",
|
||||||
|
Value: "mainnet",
|
||||||
|
}
|
||||||
|
eraSizeFlag = &cli.IntFlag{
|
||||||
|
Name: "size",
|
||||||
|
Usage: "number of blocks per era",
|
||||||
|
Value: era.MaxEra1Size,
|
||||||
|
}
|
||||||
|
txsFlag = &cli.BoolFlag{
|
||||||
|
Name: "txs",
|
||||||
|
Usage: "print full transaction values",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
blockCommand = &cli.Command{
|
||||||
|
Name: "block",
|
||||||
|
Usage: "get block data",
|
||||||
|
ArgsUsage: "<number>",
|
||||||
|
Action: block,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
txsFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
infoCommand = &cli.Command{
|
||||||
|
Name: "info",
|
||||||
|
ArgsUsage: "<epoch>",
|
||||||
|
Usage: "get epoch information",
|
||||||
|
Action: info,
|
||||||
|
}
|
||||||
|
verifyCommand = &cli.Command{
|
||||||
|
Name: "verify",
|
||||||
|
ArgsUsage: "<expected>",
|
||||||
|
Usage: "verifies each era1 against expected accumulator root",
|
||||||
|
Action: verify,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
app.Commands = []*cli.Command{
|
||||||
|
blockCommand,
|
||||||
|
infoCommand,
|
||||||
|
verifyCommand,
|
||||||
|
}
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
dirFlag,
|
||||||
|
networkFlag,
|
||||||
|
eraSizeFlag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// block prints the specified block from an era1 store.
|
||||||
|
func block(ctx *cli.Context) error {
|
||||||
|
num, err := strconv.ParseUint(ctx.Args().First(), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid block number: %w", err)
|
||||||
|
}
|
||||||
|
e, err := open(ctx, num/uint64(ctx.Int(eraSizeFlag.Name)))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening era1: %w", err)
|
||||||
|
}
|
||||||
|
defer e.Close()
|
||||||
|
// Read block with number.
|
||||||
|
block, err := e.GetBlockByNumber(num)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading block %d: %w", num, err)
|
||||||
|
}
|
||||||
|
// Convert block to JSON and print.
|
||||||
|
val := ethapi.RPCMarshalBlock(block, ctx.Bool(txsFlag.Name), ctx.Bool(txsFlag.Name), params.MainnetChainConfig, nil)
|
||||||
|
b, err := json.MarshalIndent(val, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error marshaling json: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(b))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// info prints some high-level information about the era1 file.
|
||||||
|
func info(ctx *cli.Context) error {
|
||||||
|
epoch, err := strconv.ParseUint(ctx.Args().First(), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid epoch number: %w", err)
|
||||||
|
}
|
||||||
|
e, err := open(ctx, epoch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer e.Close()
|
||||||
|
acc, err := e.Accumulator()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading accumulator: %w", err)
|
||||||
|
}
|
||||||
|
td, err := e.InitialTD()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading total difficulty: %w", err)
|
||||||
|
}
|
||||||
|
info := struct {
|
||||||
|
Accumulator common.Hash `json:"accumulator"`
|
||||||
|
TotalDifficulty *big.Int `json:"totalDifficulty"`
|
||||||
|
StartBlock uint64 `json:"startBlock"`
|
||||||
|
Count uint64 `json:"count"`
|
||||||
|
}{
|
||||||
|
acc, td, e.Start(), e.Count(),
|
||||||
|
}
|
||||||
|
b, _ := json.MarshalIndent(info, "", " ")
|
||||||
|
fmt.Println(string(b))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// open opens an era1 file at a certain epoch.
|
||||||
|
func open(ctx *cli.Context, epoch uint64) (*era.Era, error) {
|
||||||
|
var (
|
||||||
|
dir = ctx.String(dirFlag.Name)
|
||||||
|
network = ctx.String(networkFlag.Name)
|
||||||
|
)
|
||||||
|
entries, err := era.ReadDir(dir, network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading era dir: %w", err)
|
||||||
|
}
|
||||||
|
if epoch >= uint64(len(entries)) {
|
||||||
|
return nil, fmt.Errorf("epoch out-of-bounds: last %d, want %d", len(entries)-1, epoch)
|
||||||
|
}
|
||||||
|
return era.Open(path.Join(dir, entries[epoch]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify checks each era1 file in a directory to ensure it is well-formed and
|
||||||
|
// that the accumulator matches the expected value.
|
||||||
|
func verify(ctx *cli.Context) error {
|
||||||
|
if ctx.Args().Len() != 1 {
|
||||||
|
return fmt.Errorf("missing accumulators file")
|
||||||
|
}
|
||||||
|
|
||||||
|
roots, err := readHashes(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read expected roots file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
dir = ctx.String(dirFlag.Name)
|
||||||
|
network = ctx.String(networkFlag.Name)
|
||||||
|
start = time.Now()
|
||||||
|
reported = time.Now()
|
||||||
|
)
|
||||||
|
|
||||||
|
entries, err := era.ReadDir(dir, network)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entries) != len(roots) {
|
||||||
|
return fmt.Errorf("number of era1 files should match the number of accumulator hashes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify each epoch matches the expected root.
|
||||||
|
for i, want := range roots {
|
||||||
|
// Wrap in function so defers don't stack.
|
||||||
|
err := func() error {
|
||||||
|
name := entries[i]
|
||||||
|
e, err := era.Open(path.Join(dir, name))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening era1 file %s: %w", name, err)
|
||||||
|
}
|
||||||
|
defer e.Close()
|
||||||
|
// Read accumulator and check against expected.
|
||||||
|
if got, err := e.Accumulator(); err != nil {
|
||||||
|
return fmt.Errorf("error retrieving accumulator for %s: %w", name, err)
|
||||||
|
} else if got != want {
|
||||||
|
return fmt.Errorf("invalid root %s: got %s, want %s", name, got, want)
|
||||||
|
}
|
||||||
|
// Recompute accumulator.
|
||||||
|
if err := checkAccumulator(e); err != nil {
|
||||||
|
return fmt.Errorf("error verify era1 file %s: %w", name, err)
|
||||||
|
}
|
||||||
|
// Give the user some feedback that something is happening.
|
||||||
|
if time.Since(reported) >= 8*time.Second {
|
||||||
|
fmt.Printf("Verifying Era1 files \t\t verified=%d,\t elapsed=%s\n", i, common.PrettyDuration(time.Since(start)))
|
||||||
|
reported = time.Now()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkAccumulator verifies the accumulator matches the data in the Era.
|
||||||
|
func checkAccumulator(e *era.Era) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
want common.Hash
|
||||||
|
td *big.Int
|
||||||
|
tds = make([]*big.Int, 0)
|
||||||
|
hashes = make([]common.Hash, 0)
|
||||||
|
)
|
||||||
|
if want, err = e.Accumulator(); err != nil {
|
||||||
|
return fmt.Errorf("error reading accumulator: %w", err)
|
||||||
|
}
|
||||||
|
if td, err = e.InitialTD(); err != nil {
|
||||||
|
return fmt.Errorf("error reading total difficulty: %w", err)
|
||||||
|
}
|
||||||
|
it, err := era.NewIterator(e)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error making era iterator: %w", err)
|
||||||
|
}
|
||||||
|
// To fully verify an era the following attributes must be checked:
|
||||||
|
// 1) the block index is constructed correctly
|
||||||
|
// 2) the tx root matches the value in the block
|
||||||
|
// 3) the receipts root matches the value in the block
|
||||||
|
// 4) the starting total difficulty value is correct
|
||||||
|
// 5) the accumulator is correct by recomputing it locally, which verifies
|
||||||
|
// the blocks are all correct (via hash)
|
||||||
|
//
|
||||||
|
// The attributes 1), 2), and 3) are checked for each block. 4) and 5) require
|
||||||
|
// accumulation across the entire set and are verified at the end.
|
||||||
|
for it.Next() {
|
||||||
|
// 1) next() walks the block index, so we're able to implicitly verify it.
|
||||||
|
if it.Error() != nil {
|
||||||
|
return fmt.Errorf("error reading block %d: %w", it.Number(), err)
|
||||||
|
}
|
||||||
|
block, receipts, err := it.BlockAndReceipts()
|
||||||
|
if it.Error() != nil {
|
||||||
|
return fmt.Errorf("error reading block %d: %w", it.Number(), err)
|
||||||
|
}
|
||||||
|
// 2) recompute tx root and verify against header.
|
||||||
|
tr := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil))
|
||||||
|
if tr != block.TxHash() {
|
||||||
|
return fmt.Errorf("tx root in block %d mismatch: want %s, got %s", block.NumberU64(), block.TxHash(), tr)
|
||||||
|
}
|
||||||
|
// 3) recompute receipt root and check value against block.
|
||||||
|
rr := types.DeriveSha(receipts, trie.NewStackTrie(nil))
|
||||||
|
if rr != block.ReceiptHash() {
|
||||||
|
return fmt.Errorf("receipt root in block %d mismatch: want %s, got %s", block.NumberU64(), block.ReceiptHash(), rr)
|
||||||
|
}
|
||||||
|
hashes = append(hashes, block.Hash())
|
||||||
|
td.Add(td, block.Difficulty())
|
||||||
|
tds = append(tds, new(big.Int).Set(td))
|
||||||
|
}
|
||||||
|
// 4+5) Verify accumulator and total difficulty.
|
||||||
|
got, err := era.ComputeAccumulator(hashes, tds)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error computing accumulator: %w", err)
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
return fmt.Errorf("expected accumulator root does not match calculated: got %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readHashes reads a file of newline-delimited hashes.
|
||||||
|
func readHashes(f string) ([]common.Hash, error) {
|
||||||
|
b, err := os.ReadFile(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to open accumulators file")
|
||||||
|
}
|
||||||
|
s := strings.Split(string(b), "\n")
|
||||||
|
// Remove empty last element, if present.
|
||||||
|
if s[len(s)-1] == "" {
|
||||||
|
s = s[:len(s)-1]
|
||||||
|
}
|
||||||
|
// Convert to hashes.
|
||||||
|
r := make([]common.Hash, len(s))
|
||||||
|
for i := range s {
|
||||||
|
r[i] = common.HexToHash(s[i])
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
@ -214,7 +214,7 @@ exitcode:3 OK
|
||||||
|
|
||||||
The chain configuration to be used for a transition is specified via the
|
The chain configuration to be used for a transition is specified via the
|
||||||
`--state.fork` CLI flag. A list of possible values and configurations can be
|
`--state.fork` CLI flag. A list of possible values and configurations can be
|
||||||
found in [`tests/init.go`](tests/init.go).
|
found in [`tests/init.go`](../../tests/init.go).
|
||||||
|
|
||||||
#### Examples
|
#### Examples
|
||||||
##### Basic usage
|
##### Basic usage
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,14 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Prestate struct {
|
type Prestate struct {
|
||||||
Env stEnv `json:"env"`
|
Env stEnv `json:"env"`
|
||||||
Pre core.GenesisAlloc `json:"pre"`
|
Pre types.GenesisAlloc `json:"pre"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecutionResult contains the execution status after running a state test, any
|
// ExecutionResult contains the execution status after running a state test, any
|
||||||
|
|
@ -145,6 +146,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
rejectedTxs []*rejectedTx
|
rejectedTxs []*rejectedTx
|
||||||
includedTxs types.Transactions
|
includedTxs types.Transactions
|
||||||
gasUsed = uint64(0)
|
gasUsed = uint64(0)
|
||||||
|
blobGasUsed = uint64(0)
|
||||||
receipts = make(types.Receipts, 0)
|
receipts = make(types.Receipts, 0)
|
||||||
txIndex = 0
|
txIndex = 0
|
||||||
)
|
)
|
||||||
|
|
@ -195,7 +197,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
||||||
}
|
}
|
||||||
var blobGasUsed uint64
|
|
||||||
|
|
||||||
for i := 0; txIt.Next(); i++ {
|
for i := 0; txIt.Next(); i++ {
|
||||||
tx, err := txIt.Tx()
|
tx, err := txIt.Tx()
|
||||||
|
|
@ -217,15 +218,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
txBlobGas := uint64(0)
|
||||||
if tx.Type() == types.BlobTxType {
|
if tx.Type() == types.BlobTxType {
|
||||||
txBlobGas := uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
|
txBlobGas = uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
|
||||||
if used, max := blobGasUsed+txBlobGas, uint64(params.MaxBlobGasPerBlock); used > max {
|
if used, max := blobGasUsed+txBlobGas, uint64(params.MaxBlobGasPerBlock); used > max {
|
||||||
err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max)
|
err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max)
|
||||||
log.Warn("rejected tx", "index", i, "err", err)
|
log.Warn("rejected tx", "index", i, "err", err)
|
||||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
blobGasUsed += txBlobGas
|
|
||||||
}
|
}
|
||||||
tracer, err := getTracerFn(txIndex, tx.Hash())
|
tracer, err := getTracerFn(txIndex, tx.Hash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -260,7 +261,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
if hashError != nil {
|
if hashError != nil {
|
||||||
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
|
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
|
||||||
}
|
}
|
||||||
|
blobGasUsed += txBlobGas
|
||||||
gasUsed += msgResult.UsedGas
|
gasUsed += msgResult.UsedGas
|
||||||
|
|
||||||
// Receipt:
|
// Receipt:
|
||||||
|
|
@ -325,16 +326,16 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta))
|
reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta))
|
||||||
reward.Mul(reward, blockReward)
|
reward.Mul(reward, blockReward)
|
||||||
reward.Div(reward, big.NewInt(8))
|
reward.Div(reward, big.NewInt(8))
|
||||||
statedb.AddBalance(ommer.Address, reward)
|
statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward))
|
||||||
}
|
}
|
||||||
|
|
||||||
statedb.AddBalance(pre.Env.Coinbase, minerReward)
|
statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward))
|
||||||
}
|
}
|
||||||
// Apply withdrawals
|
// Apply withdrawals
|
||||||
for _, w := range pre.Env.Withdrawals {
|
for _, w := range pre.Env.Withdrawals {
|
||||||
// Amount is in gwei, turn into wei
|
// Amount is in gwei, turn into wei
|
||||||
amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei))
|
amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei))
|
||||||
statedb.AddBalance(w.Address, amount)
|
statedb.AddBalance(w.Address, uint256.MustFromBig(amount))
|
||||||
}
|
}
|
||||||
// Commit block
|
// Commit block
|
||||||
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
|
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||||
|
|
@ -373,14 +374,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
return statedb, execRs, body, nil
|
return statedb, execRs, body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB {
|
||||||
sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
|
sdb := state.NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
|
||||||
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
|
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
|
||||||
for addr, a := range accounts {
|
for addr, a := range accounts {
|
||||||
statedb.SetCode(addr, a.Code)
|
statedb.SetCode(addr, a.Code)
|
||||||
statedb.SetNonce(addr, a.Nonce)
|
statedb.SetNonce(addr, a.Nonce)
|
||||||
statedb.SetBalance(addr, a.Balance)
|
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance))
|
||||||
|
|
||||||
for k, v := range a.Storage {
|
for k, v := range a.Storage {
|
||||||
statedb.SetState(addr, k, v)
|
statedb.SetState(addr, k, v)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -75,10 +74,10 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type input struct {
|
type input struct {
|
||||||
Alloc core.GenesisAlloc `json:"alloc,omitempty"`
|
Alloc types.GenesisAlloc `json:"alloc,omitempty"`
|
||||||
Env *stEnv `json:"env,omitempty"`
|
Env *stEnv `json:"env,omitempty"`
|
||||||
Txs []*txWithKey `json:"txs,omitempty"`
|
Txs []*txWithKey `json:"txs,omitempty"`
|
||||||
TxRlp string `json:"txsRlp,omitempty"`
|
TxRlp string `json:"txsRlp,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Transition(ctx *cli.Context) error {
|
func Transition(ctx *cli.Context) error {
|
||||||
|
|
@ -194,7 +193,7 @@ func Transition(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Dump the excution result
|
// Dump the execution result
|
||||||
collector := make(Alloc)
|
collector := make(Alloc)
|
||||||
s.DumpToCollector(collector, nil)
|
s.DumpToCollector(collector, nil)
|
||||||
return dispatchOutput(ctx, baseDir, result, collector, body)
|
return dispatchOutput(ctx, baseDir, result, collector, body)
|
||||||
|
|
@ -278,7 +277,7 @@ func applyCancunChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Alloc map[common.Address]core.GenesisAccount
|
type Alloc map[common.Address]types.Account
|
||||||
|
|
||||||
func (g Alloc) OnRoot(common.Hash) {}
|
func (g Alloc) OnRoot(common.Hash) {}
|
||||||
|
|
||||||
|
|
@ -286,7 +285,8 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
||||||
if addr == nil {
|
if addr == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
|
|
||||||
|
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
|
||||||
|
|
||||||
var storage map[common.Hash]common.Hash
|
var storage map[common.Hash]common.Hash
|
||||||
if dumpAccount.Storage != nil {
|
if dumpAccount.Storage != nil {
|
||||||
|
|
@ -296,7 +296,7 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genesisAccount := core.GenesisAccount{
|
genesisAccount := types.Account{
|
||||||
Code: dumpAccount.Code,
|
Code: dumpAccount.Code,
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
Balance: balance,
|
Balance: balance,
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
var runCommand = &cli.Command{
|
var runCommand = &cli.Command{
|
||||||
|
|
@ -156,7 +156,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
triedb := trie.NewDatabase(db, &trie.Config{
|
triedb := triedb.NewDatabase(db, &triedb.Config{
|
||||||
Preimages: preimages,
|
Preimages: preimages,
|
||||||
HashDB: hashdb.Defaults,
|
HashDB: hashdb.Defaults,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
|
|
@ -92,27 +91,28 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var tests map[string]tests.StateTest
|
var testsByName map[string]tests.StateTest
|
||||||
if err := json.Unmarshal(src, &tests); err != nil {
|
if err := json.Unmarshal(src, &testsByName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Iterate over all the tests, run them and aggregate the results
|
|
||||||
results := make([]StatetestResult, 0, len(tests))
|
|
||||||
|
|
||||||
for key, test := range tests {
|
// Iterate over all the tests, run them and aggregate the results
|
||||||
|
results := make([]StatetestResult, 0, len(testsByName))
|
||||||
|
|
||||||
|
for key, test := range testsByName {
|
||||||
for _, st := range test.Subtests() {
|
for _, st := range test.Subtests() {
|
||||||
// Run the test and aggregate the result
|
// Run the test and aggregate the result
|
||||||
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
|
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
|
||||||
test.Run(st, cfg, false, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, statedb *state.StateDB) {
|
test.Run(st, cfg, false, rawdb.HashScheme, func(err error, tstate *tests.StateTestState) {
|
||||||
var root common.Hash
|
var root common.Hash
|
||||||
if statedb != nil {
|
if tstate.StateDB != nil {
|
||||||
root = statedb.IntermediateRoot(false)
|
root = tstate.StateDB.IntermediateRoot(false)
|
||||||
result.Root = &root
|
result.Root = &root
|
||||||
if jsonOut {
|
if jsonOut {
|
||||||
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
||||||
}
|
}
|
||||||
if dump { // Dump any state to aid debugging
|
if dump { // Dump any state to aid debugging
|
||||||
cpy, _ := state.New(root, statedb.Database(), nil)
|
cpy, _ := state.New(root, tstate.StateDB.Database(), nil)
|
||||||
dump := cpy.RawDump(nil)
|
dump := cpy.RawDump(nil)
|
||||||
result.State = &dump
|
result.State = &dump
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ type Env struct {
|
||||||
CurrentTimestamp uint64 `json:"currentTimestamp"`
|
CurrentTimestamp uint64 `json:"currentTimestamp"`
|
||||||
Withdrawals []*Withdrawal `json:"withdrawals"`
|
Withdrawals []*Withdrawal `json:"withdrawals"`
|
||||||
// optional
|
// optional
|
||||||
CurrentDifficulty *big.Int `json:"currentDifficuly"`
|
CurrentDifficulty *big.Int `json:"currentDifficulty"`
|
||||||
CurrentRandom *big.Int `json:"currentRandom"`
|
CurrentRandom *big.Int `json:"currentRandom"`
|
||||||
CurrentBaseFee *big.Int `json:"currentBaseFee"`
|
CurrentBaseFee *big.Int `json:"currentBaseFee"`
|
||||||
ParentDifficulty *big.Int `json:"parentDifficulty"`
|
ParentDifficulty *big.Int `json:"parentDifficulty"`
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,12 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/era"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -124,6 +126,33 @@ Optional second and third arguments control the first and
|
||||||
last block to write. In this mode, the file will be appended
|
last block to write. In this mode, the file will be appended
|
||||||
if already existing. If the file ends with .gz, the output will
|
if already existing. If the file ends with .gz, the output will
|
||||||
be gzipped.`,
|
be gzipped.`,
|
||||||
|
}
|
||||||
|
importHistoryCommand = &cli.Command{
|
||||||
|
Action: importHistory,
|
||||||
|
Name: "import-history",
|
||||||
|
Usage: "Import an Era archive",
|
||||||
|
ArgsUsage: "<dir>",
|
||||||
|
Flags: flags.Merge([]cli.Flag{
|
||||||
|
utils.TxLookupLimitFlag,
|
||||||
|
},
|
||||||
|
utils.DatabaseFlags,
|
||||||
|
utils.NetworkFlags,
|
||||||
|
),
|
||||||
|
Description: `
|
||||||
|
The import-history command will import blocks and their corresponding receipts
|
||||||
|
from Era archives.
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
exportHistoryCommand = &cli.Command{
|
||||||
|
Action: exportHistory,
|
||||||
|
Name: "export-history",
|
||||||
|
Usage: "Export blockchain history to Era archives",
|
||||||
|
ArgsUsage: "<dir> <first> <last>",
|
||||||
|
Flags: flags.Merge(utils.DatabaseFlags),
|
||||||
|
Description: `
|
||||||
|
The export-history command will export blocks and their corresponding receipts
|
||||||
|
into Era archives. Eras are typically packaged in steps of 8192 blocks.
|
||||||
|
`,
|
||||||
}
|
}
|
||||||
importPreimagesCommand = &cli.Command{
|
importPreimagesCommand = &cli.Command{
|
||||||
Action: importPreimages,
|
Action: importPreimages,
|
||||||
|
|
@ -391,7 +420,97 @@ func exportChain(ctx *cli.Context) error {
|
||||||
|
|
||||||
err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last))
|
err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last))
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Export error: %v\n", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Export done in %v\n", time.Since(start))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func importHistory(ctx *cli.Context) error {
|
||||||
|
if ctx.Args().Len() != 1 {
|
||||||
|
utils.Fatalf("usage: %s", ctx.Command.ArgsUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
stack, _ := makeConfigNode(ctx)
|
||||||
|
defer stack.Close()
|
||||||
|
|
||||||
|
chain, db := utils.MakeChain(ctx, stack, false)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
start = time.Now()
|
||||||
|
dir = ctx.Args().Get(0)
|
||||||
|
network string
|
||||||
|
)
|
||||||
|
|
||||||
|
// Determine network.
|
||||||
|
if utils.IsNetworkPreset(ctx) {
|
||||||
|
switch {
|
||||||
|
case ctx.Bool(utils.MainnetFlag.Name):
|
||||||
|
network = "mainnet"
|
||||||
|
case ctx.Bool(utils.SepoliaFlag.Name):
|
||||||
|
network = "sepolia"
|
||||||
|
case ctx.Bool(utils.GoerliFlag.Name):
|
||||||
|
network = "goerli"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No network flag set, try to determine network based on files
|
||||||
|
// present in directory.
|
||||||
|
var networks []string
|
||||||
|
for _, n := range params.NetworkNames {
|
||||||
|
entries, err := era.ReadDir(dir, n)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
if len(entries) > 0 {
|
||||||
|
networks = append(networks, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(networks) == 0 {
|
||||||
|
return fmt.Errorf("no era1 files found in %s", dir)
|
||||||
|
}
|
||||||
|
if len(networks) > 1 {
|
||||||
|
return fmt.Errorf("multiple networks found, use a network flag to specify desired network")
|
||||||
|
}
|
||||||
|
network = networks[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := utils.ImportHistory(chain, db, dir, network); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Import done in %v\n", time.Since(start))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// exportHistory exports chain history in Era archives at a specified
|
||||||
|
// directory.
|
||||||
|
func exportHistory(ctx *cli.Context) error {
|
||||||
|
if ctx.Args().Len() != 3 {
|
||||||
|
utils.Fatalf("usage: %s", ctx.Command.ArgsUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
stack, _ := makeConfigNode(ctx)
|
||||||
|
defer stack.Close()
|
||||||
|
|
||||||
|
chain, _ := utils.MakeChain(ctx, stack, true)
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
var (
|
||||||
|
dir = ctx.Args().Get(0)
|
||||||
|
first, ferr = strconv.ParseInt(ctx.Args().Get(1), 10, 64)
|
||||||
|
last, lerr = strconv.ParseInt(ctx.Args().Get(2), 10, 64)
|
||||||
|
)
|
||||||
|
if ferr != nil || lerr != nil {
|
||||||
|
utils.Fatalf("Export error in parsing parameters: block number not an integer\n")
|
||||||
|
}
|
||||||
|
if first < 0 || last < 0 {
|
||||||
|
utils.Fatalf("Export error: block number must be greater than 0\n")
|
||||||
|
}
|
||||||
|
if head := chain.CurrentSnapBlock(); uint64(last) > head.Number.Uint64() {
|
||||||
|
utils.Fatalf("Export error: block number %d larger than head block %d\n", uint64(last), head.Number.Uint64())
|
||||||
|
}
|
||||||
|
err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), uint64(era.MaxEra1Size))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Export error: %v\n", err)
|
utils.Fatalf("Export error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,22 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
removeStateDataFlag = &cli.BoolFlag{
|
||||||
|
Name: "remove.state",
|
||||||
|
Usage: "If set, selects the state data for removal",
|
||||||
|
}
|
||||||
|
removeChainDataFlag = &cli.BoolFlag{
|
||||||
|
Name: "remove.chain",
|
||||||
|
Usage: "If set, selects the state data for removal",
|
||||||
|
}
|
||||||
|
|
||||||
removedbCommand = &cli.Command{
|
removedbCommand = &cli.Command{
|
||||||
Action: removeDB,
|
Action: removeDB,
|
||||||
Name: "removedb",
|
Name: "removedb",
|
||||||
Usage: "Remove blockchain and state databases",
|
Usage: "Remove blockchain and state databases",
|
||||||
ArgsUsage: "",
|
ArgsUsage: "",
|
||||||
Flags: utils.DatabaseFlags,
|
Flags: flags.Merge(utils.DatabaseFlags,
|
||||||
|
[]cli.Flag{removeStateDataFlag, removeChainDataFlag}),
|
||||||
Description: `
|
Description: `
|
||||||
Remove blockchain and state databases`,
|
Remove blockchain and state databases`,
|
||||||
}
|
}
|
||||||
|
|
@ -198,66 +208,85 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
||||||
func removeDB(ctx *cli.Context) error {
|
func removeDB(ctx *cli.Context) error {
|
||||||
stack, config := makeConfigNode(ctx)
|
stack, config := makeConfigNode(ctx)
|
||||||
|
|
||||||
// Remove the full node state database
|
// Resolve folder paths.
|
||||||
path := stack.ResolvePath("chaindata")
|
var (
|
||||||
if common.FileExist(path) {
|
rootDir = stack.ResolvePath("chaindata")
|
||||||
confirmAndRemoveDB(path, "full node state database")
|
ancientDir = config.Eth.DatabaseFreezer
|
||||||
} else {
|
)
|
||||||
log.Info("Full node state database missing", "path", path)
|
|
||||||
}
|
|
||||||
// Remove the full node ancient database
|
|
||||||
path = config.Eth.DatabaseFreezer
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case path == "":
|
case ancientDir == "":
|
||||||
path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
|
ancientDir = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
|
||||||
case !filepath.IsAbs(path):
|
case !filepath.IsAbs(ancientDir):
|
||||||
path = config.Node.ResolvePath(path)
|
ancientDir = config.Node.ResolvePath(ancientDir)
|
||||||
}
|
|
||||||
|
|
||||||
if common.FileExist(path) {
|
|
||||||
confirmAndRemoveDB(path, "full node ancient database")
|
|
||||||
} else {
|
|
||||||
log.Info("Full node ancient database missing", "path", path)
|
|
||||||
}
|
|
||||||
// Remove the light node database
|
|
||||||
path = stack.ResolvePath("lightchaindata")
|
|
||||||
if common.FileExist(path) {
|
|
||||||
confirmAndRemoveDB(path, "light node database")
|
|
||||||
} else {
|
|
||||||
log.Info("Light node database missing", "path", path)
|
|
||||||
}
|
}
|
||||||
|
// Delete state data
|
||||||
|
statePaths := []string{rootDir, filepath.Join(ancientDir, rawdb.StateFreezerName)}
|
||||||
|
confirmAndRemoveDB(statePaths, "state data", ctx, removeStateDataFlag.Name)
|
||||||
|
|
||||||
|
// Delete ancient chain
|
||||||
|
chainPaths := []string{filepath.Join(ancientDir, rawdb.ChainFreezerName)}
|
||||||
|
confirmAndRemoveDB(chainPaths, "ancient chain", ctx, removeChainDataFlag.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirmAndRemoveDB prompts the user for a last confirmation and removes the
|
// removeFolder deletes all files (not folders) inside the directory 'dir' (but
|
||||||
// folder if accepted.
|
// not files in subfolders).
|
||||||
func confirmAndRemoveDB(database string, kind string) {
|
func removeFolder(dir string) {
|
||||||
confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
|
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
// If we're at the top level folder, recurse into
|
||||||
|
if path == dir {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Delete all the files, but not subfolders
|
||||||
|
if !info.IsDir() {
|
||||||
|
os.Remove(path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return filepath.SkipDir
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// confirmAndRemoveDB prompts the user for a last confirmation and removes the
|
||||||
|
// list of folders if accepted.
|
||||||
|
func confirmAndRemoveDB(paths []string, kind string, ctx *cli.Context, removeFlagName string) {
|
||||||
|
var (
|
||||||
|
confirm bool
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
msg := fmt.Sprintf("Location(s) of '%s': \n", kind)
|
||||||
|
for _, path := range paths {
|
||||||
|
msg += fmt.Sprintf("\t- %s\n", path)
|
||||||
|
}
|
||||||
|
fmt.Println(msg)
|
||||||
|
if ctx.IsSet(removeFlagName) {
|
||||||
|
confirm = ctx.Bool(removeFlagName)
|
||||||
|
if confirm {
|
||||||
|
fmt.Printf("Remove '%s'? [y/n] y\n", kind)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Remove '%s'? [y/n] n\n", kind)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
confirm, err = prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove '%s'?", kind))
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
case !confirm:
|
case !confirm:
|
||||||
log.Info("Database deletion skipped", "path", database)
|
log.Info("Database deletion skipped", "kind", kind, "paths", paths)
|
||||||
default:
|
default:
|
||||||
start := time.Now()
|
var (
|
||||||
|
deleted []string
|
||||||
filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
|
start = time.Now()
|
||||||
// If we're at the top level folder, recurse into
|
)
|
||||||
if path == database {
|
for _, path := range paths {
|
||||||
return nil
|
if common.FileExist(path) {
|
||||||
|
removeFolder(path)
|
||||||
|
deleted = append(deleted, path)
|
||||||
|
} else {
|
||||||
|
log.Info("Folder is not existent", "path", path)
|
||||||
}
|
}
|
||||||
// Delete all the files, but not subfolders
|
}
|
||||||
if !info.IsDir() {
|
log.Info("Database successfully deleted", "kind", kind, "paths", deleted, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
os.Remove(path)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.SkipDir
|
|
||||||
})
|
|
||||||
log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
@ -51,9 +50,6 @@ func (c customQuotedStringer) String() string {
|
||||||
// logTest is an entry point which spits out some logs. This is used by testing
|
// logTest is an entry point which spits out some logs. This is used by testing
|
||||||
// to verify expected outputs
|
// to verify expected outputs
|
||||||
func logTest(ctx *cli.Context) error {
|
func logTest(ctx *cli.Context) error {
|
||||||
// clear field padding map
|
|
||||||
debug.ResetLogging()
|
|
||||||
|
|
||||||
{ // big.Int
|
{ // big.Int
|
||||||
ba, _ := new(big.Int).SetString("111222333444555678999", 10) // "111,222,333,444,555,678,999"
|
ba, _ := new(big.Int).SetString("111222333444555678999", 10) // "111,222,333,444,555,678,999"
|
||||||
bb, _ := new(big.Int).SetString("-111222333444555678999", 10) // "-111,222,333,444,555,678,999"
|
bb, _ := new(big.Int).SetString("-111222333444555678999", 10) // "-111,222,333,444,555,678,999"
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// geth is the official command-line client for Ethereum.
|
// geth is a command-line client for Ethereum.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -214,12 +214,13 @@ var app = flags.NewApp("the go-ethereum command line interface")
|
||||||
func init() {
|
func init() {
|
||||||
// Initialize the CLI app and start Geth
|
// Initialize the CLI app and start Geth
|
||||||
app.Action = geth
|
app.Action = geth
|
||||||
app.Copyright = "Copyright 2013-2023 The go-ethereum Authors"
|
|
||||||
app.Commands = []*cli.Command{
|
app.Commands = []*cli.Command{
|
||||||
// See chaincmd.go:
|
// See chaincmd.go:
|
||||||
initCommand,
|
initCommand,
|
||||||
importCommand,
|
importCommand,
|
||||||
exportCommand,
|
exportCommand,
|
||||||
|
importHistoryCommand,
|
||||||
|
exportHistoryCommand,
|
||||||
importPreimagesCommand,
|
importPreimagesCommand,
|
||||||
removedbCommand,
|
removedbCommand,
|
||||||
dumpCommand,
|
dumpCommand,
|
||||||
|
|
|
||||||
2
cmd/geth/testdata/logging/logtest-json.txt
vendored
2
cmd/geth/testdata/logging/logtest-json.txt
vendored
|
|
@ -29,7 +29,7 @@
|
||||||
{"t":"2023-11-22T15:42:00.408237+08:00","lvl":"info","msg":"repeated-key 2","xx":"short","xx":"longer"}
|
{"t":"2023-11-22T15:42:00.408237+08:00","lvl":"info","msg":"repeated-key 2","xx":"short","xx":"longer"}
|
||||||
{"t":"2023-11-22T15:42:00.408241+08:00","lvl":"info","msg":"log at level info"}
|
{"t":"2023-11-22T15:42:00.408241+08:00","lvl":"info","msg":"log at level info"}
|
||||||
{"t":"2023-11-22T15:42:00.408244+08:00","lvl":"warn","msg":"log at level warn"}
|
{"t":"2023-11-22T15:42:00.408244+08:00","lvl":"warn","msg":"log at level warn"}
|
||||||
{"t":"2023-11-22T15:42:00.408247+08:00","lvl":"eror","msg":"log at level error"}
|
{"t":"2023-11-22T15:42:00.408247+08:00","lvl":"error","msg":"log at level error"}
|
||||||
{"t":"2023-11-22T15:42:00.408251+08:00","lvl":"info","msg":"test","bar":"short","a":"aligned left"}
|
{"t":"2023-11-22T15:42:00.408251+08:00","lvl":"info","msg":"test","bar":"short","a":"aligned left"}
|
||||||
{"t":"2023-11-22T15:42:00.408254+08:00","lvl":"info","msg":"test","bar":"a long message","a":1}
|
{"t":"2023-11-22T15:42:00.408254+08:00","lvl":"info","msg":"test","bar":"a long message","a":1}
|
||||||
{"t":"2023-11-22T15:42:00.408258+08:00","lvl":"info","msg":"test","bar":"short","a":"aligned right"}
|
{"t":"2023-11-22T15:42:00.408258+08:00","lvl":"info","msg":"test","bar":"short","a":"aligned right"}
|
||||||
|
|
|
||||||
2
cmd/geth/testdata/logging/logtest-logfmt.txt
vendored
2
cmd/geth/testdata/logging/logtest-logfmt.txt
vendored
|
|
@ -29,7 +29,7 @@ t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="repeated-key 1" foo=alpha foo=beta
|
||||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="repeated-key 2" xx=short xx=longer
|
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="repeated-key 2" xx=short xx=longer
|
||||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="log at level info"
|
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="log at level info"
|
||||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=warn msg="log at level warn"
|
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=warn msg="log at level warn"
|
||||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=eror msg="log at level error"
|
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=error msg="log at level error"
|
||||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar=short a="aligned left"
|
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar=short a="aligned left"
|
||||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar="a long message" a=1
|
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar="a long message" a=1
|
||||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar=short a="aligned right"
|
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar=short a="aligned right"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -37,6 +39,7 @@ var (
|
||||||
reverseMode = flag.Bool("reverse", false, "convert ASCII to rlp")
|
reverseMode = flag.Bool("reverse", false, "convert ASCII to rlp")
|
||||||
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
|
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
|
||||||
single = flag.Bool("single", false, "print only the first element, discard the rest")
|
single = flag.Bool("single", false, "print only the first element, discard the rest")
|
||||||
|
showpos = flag.Bool("pos", false, "display element byte posititions")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -52,8 +55,7 @@ If the filename is omitted, data is read from stdin.`)
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var r io.Reader
|
var r *inStream
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case *hexMode != "":
|
case *hexMode != "":
|
||||||
data, err := hex.DecodeString(strings.TrimPrefix(*hexMode, "0x"))
|
data, err := hex.DecodeString(strings.TrimPrefix(*hexMode, "0x"))
|
||||||
|
|
@ -61,10 +63,10 @@ func main() {
|
||||||
die(err)
|
die(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r = bytes.NewReader(data)
|
r = newInStream(bytes.NewReader(data), int64(len(data)))
|
||||||
|
|
||||||
case flag.NArg() == 0:
|
case flag.NArg() == 0:
|
||||||
r = os.Stdin
|
r = newInStream(bufio.NewReader(os.Stdin), 0)
|
||||||
|
|
||||||
case flag.NArg() == 1:
|
case flag.NArg() == 1:
|
||||||
fd, err := os.Open(flag.Arg(0))
|
fd, err := os.Open(flag.Arg(0))
|
||||||
|
|
@ -73,7 +75,12 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
r = fd
|
var size int64
|
||||||
|
finfo, err := fd.Stat()
|
||||||
|
if err == nil {
|
||||||
|
size = finfo.Size()
|
||||||
|
}
|
||||||
|
r = newInStream(bufio.NewReader(fd), size)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fmt.Fprintln(os.Stderr, "Error: too many arguments")
|
fmt.Fprintln(os.Stderr, "Error: too many arguments")
|
||||||
|
|
@ -100,11 +107,11 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rlpToText(r io.Reader, out io.Writer) error {
|
func rlpToText(in *inStream, out io.Writer) error {
|
||||||
s := rlp.NewStream(r, 0)
|
stream := rlp.NewStream(in, 0)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := dump(s, 0, out); err != nil {
|
if err := dump(in, stream, 0, out); err != nil {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +129,10 @@ func rlpToText(r io.Reader, out io.Writer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dump(s *rlp.Stream, depth int, out io.Writer) error {
|
func dump(in *inStream, s *rlp.Stream, depth int, out io.Writer) error {
|
||||||
|
if *showpos {
|
||||||
|
fmt.Fprintf(out, "%s: ", in.posLabel())
|
||||||
|
}
|
||||||
kind, size, err := s.Kind()
|
kind, size, err := s.Kind()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -153,8 +163,7 @@ func dump(s *rlp.Stream, depth int, out io.Writer) error {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
fmt.Fprint(out, ",\n")
|
fmt.Fprint(out, ",\n")
|
||||||
}
|
}
|
||||||
|
if err := dump(in, s, depth+1, out); err == rlp.EOL {
|
||||||
if err := dump(s, depth+1, out); err == rlp.EOL {
|
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -233,3 +242,36 @@ func textToRlp(r io.Reader) ([]byte, error) {
|
||||||
|
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type inStream struct {
|
||||||
|
br rlp.ByteReader
|
||||||
|
pos int
|
||||||
|
columns int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInStream(br rlp.ByteReader, totalSize int64) *inStream {
|
||||||
|
col := int(math.Ceil(math.Log10(float64(totalSize))))
|
||||||
|
return &inStream{br: br, columns: col}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *inStream) Read(b []byte) (n int, err error) {
|
||||||
|
n, err = rc.br.Read(b)
|
||||||
|
rc.pos += n
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *inStream) ReadByte() (byte, error) {
|
||||||
|
b, err := rc.br.ReadByte()
|
||||||
|
if err == nil {
|
||||||
|
rc.pos++
|
||||||
|
}
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *inStream) posLabel() string {
|
||||||
|
l := strconv.FormatInt(int64(rc.pos), 10)
|
||||||
|
if len(l) < rc.columns {
|
||||||
|
l = strings.Repeat(" ", rc.columns-len(l)) + l
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@ func TestRoundtrip(t *testing.T) {
|
||||||
} {
|
} {
|
||||||
var out strings.Builder
|
var out strings.Builder
|
||||||
|
|
||||||
err := rlpToText(bytes.NewReader(common.FromHex(want)), &out)
|
in := newInStream(bytes.NewReader(common.FromHex(want)), 0)
|
||||||
|
err := rlpToText(in, &out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
191
cmd/utils/cmd.go
191
cmd/utils/cmd.go
|
|
@ -19,12 +19,15 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
@ -41,8 +44,10 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/era"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -251,6 +256,105 @@ func ImportChain(chain *core.BlockChain, fn string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readList(filename string) ([]string, error) {
|
||||||
|
b, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return strings.Split(string(b), "\n"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportHistory imports Era1 files containing historical block information,
|
||||||
|
// starting from genesis.
|
||||||
|
func ImportHistory(chain *core.BlockChain, db ethdb.Database, dir string, network string) error {
|
||||||
|
if chain.CurrentSnapBlock().Number.BitLen() != 0 {
|
||||||
|
return fmt.Errorf("history import only supported when starting from genesis")
|
||||||
|
}
|
||||||
|
entries, err := era.ReadDir(dir, network)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
checksums, err := readList(path.Join(dir, "checksums.txt"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read checksums.txt: %w", err)
|
||||||
|
}
|
||||||
|
if len(checksums) != len(entries) {
|
||||||
|
return fmt.Errorf("expected equal number of checksums and entries, have: %d checksums, %d entries", len(checksums), len(entries))
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
start = time.Now()
|
||||||
|
reported = time.Now()
|
||||||
|
imported = 0
|
||||||
|
forker = core.NewForkChoice(chain, nil, nil)
|
||||||
|
h = sha256.New()
|
||||||
|
buf = bytes.NewBuffer(nil)
|
||||||
|
)
|
||||||
|
for i, filename := range entries {
|
||||||
|
err := func() error {
|
||||||
|
f, err := os.Open(path.Join(dir, filename))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to open era: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Validate checksum.
|
||||||
|
if _, err := io.Copy(h, f); err != nil {
|
||||||
|
return fmt.Errorf("unable to recalculate checksum: %w", err)
|
||||||
|
}
|
||||||
|
if have, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; have != want {
|
||||||
|
return fmt.Errorf("checksum mismatch: have %s, want %s", have, want)
|
||||||
|
}
|
||||||
|
h.Reset()
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
// Import all block data from Era1.
|
||||||
|
e, err := era.From(f)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening era: %w", err)
|
||||||
|
}
|
||||||
|
it, err := era.NewIterator(e)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error making era reader: %w", err)
|
||||||
|
}
|
||||||
|
for it.Next() {
|
||||||
|
block, err := it.Block()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading block %d: %w", it.Number(), err)
|
||||||
|
}
|
||||||
|
if block.Number().BitLen() == 0 {
|
||||||
|
continue // skip genesis
|
||||||
|
}
|
||||||
|
receipts, err := it.Receipts()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
||||||
|
}
|
||||||
|
if status, err := chain.HeaderChain().InsertHeaderChain([]*types.Header{block.Header()}, start, forker); err != nil {
|
||||||
|
return fmt.Errorf("error inserting header %d: %w", it.Number(), err)
|
||||||
|
} else if status != core.CanonStatTy {
|
||||||
|
return fmt.Errorf("error inserting header %d, not canon: %v", it.Number(), status)
|
||||||
|
}
|
||||||
|
if _, err := chain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{receipts}, 2^64-1); err != nil {
|
||||||
|
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
||||||
|
}
|
||||||
|
imported += 1
|
||||||
|
|
||||||
|
// Give the user some feedback that something is happening.
|
||||||
|
if time.Since(reported) >= 8*time.Second {
|
||||||
|
log.Info("Importing Era files", "head", it.Number(), "imported", imported, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
imported = 0
|
||||||
|
reported = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block {
|
func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block {
|
||||||
head := chain.CurrentBlock()
|
head := chain.CurrentBlock()
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
|
|
@ -325,6 +429,93 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExportHistory exports blockchain history into the specified directory,
|
||||||
|
// following the Era format.
|
||||||
|
func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) error {
|
||||||
|
log.Info("Exporting blockchain history", "dir", dir)
|
||||||
|
if head := bc.CurrentBlock().Number.Uint64(); head < last {
|
||||||
|
log.Warn("Last block beyond head, setting last = head", "head", head, "last", last)
|
||||||
|
last = head
|
||||||
|
}
|
||||||
|
network := "unknown"
|
||||||
|
if name, ok := params.NetworkNames[bc.Config().ChainID.String()]; ok {
|
||||||
|
network = name
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||||
|
return fmt.Errorf("error creating output directory: %w", err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
start = time.Now()
|
||||||
|
reported = time.Now()
|
||||||
|
h = sha256.New()
|
||||||
|
buf = bytes.NewBuffer(nil)
|
||||||
|
checksums []string
|
||||||
|
)
|
||||||
|
for i := first; i <= last; i += step {
|
||||||
|
err := func() error {
|
||||||
|
filename := path.Join(dir, era.Filename(network, int(i/step), common.Hash{}))
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not create era file: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
w := era.NewBuilder(f)
|
||||||
|
for j := uint64(0); j < step && j <= last-i; j++ {
|
||||||
|
var (
|
||||||
|
n = i + j
|
||||||
|
block = bc.GetBlockByNumber(n)
|
||||||
|
)
|
||||||
|
if block == nil {
|
||||||
|
return fmt.Errorf("export failed on #%d: not found", n)
|
||||||
|
}
|
||||||
|
receipts := bc.GetReceiptsByHash(block.Hash())
|
||||||
|
if receipts == nil {
|
||||||
|
return fmt.Errorf("export failed on #%d: receipts not found", n)
|
||||||
|
}
|
||||||
|
td := bc.GetTd(block.Hash(), block.NumberU64())
|
||||||
|
if td == nil {
|
||||||
|
return fmt.Errorf("export failed on #%d: total difficulty not found", n)
|
||||||
|
}
|
||||||
|
if err := w.Add(block, receipts, td); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root, err := w.Finalize()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("export failed to finalize %d: %w", step/i, err)
|
||||||
|
}
|
||||||
|
// Set correct filename with root.
|
||||||
|
os.Rename(filename, path.Join(dir, era.Filename(network, int(i/step), root)))
|
||||||
|
|
||||||
|
// Compute checksum of entire Era1.
|
||||||
|
if _, err := f.Seek(0, io.SeekStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(h, f); err != nil {
|
||||||
|
return fmt.Errorf("unable to calculate checksum: %w", err)
|
||||||
|
}
|
||||||
|
checksums = append(checksums, common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex())
|
||||||
|
h.Reset()
|
||||||
|
buf.Reset()
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if time.Since(reported) >= 8*time.Second {
|
||||||
|
log.Info("Exporting blocks", "exported", i, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
reported = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.WriteFile(path.Join(dir, "checksums.txt"), []byte(strings.Join(checksums, "\n")), os.ModePerm)
|
||||||
|
|
||||||
|
log.Info("Exported blockchain to", "dir", dir)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ImportPreimages imports a batch of exported hash preimages into the database.
|
// ImportPreimages imports a batch of exported hash preimages into the database.
|
||||||
// It's a part of the deprecated functionality, should be removed in the future.
|
// It's a part of the deprecated functionality, should be removed in the future.
|
||||||
func ImportPreimages(db ethdb.Database, fn string) error {
|
func ImportPreimages(db ethdb.Database, fn string) error {
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These are all the command line flags we support.
|
// These are all the command line flags we support.
|
||||||
|
|
@ -2310,8 +2310,8 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeTrieDatabase constructs a trie database based on the configured scheme.
|
// MakeTrieDatabase constructs a trie database based on the configured scheme.
|
||||||
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *trie.Database {
|
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database {
|
||||||
config := &trie.Config{
|
config := &triedb.Config{
|
||||||
Preimages: preimage,
|
Preimages: preimage,
|
||||||
IsVerkle: isVerkle,
|
IsVerkle: isVerkle,
|
||||||
}
|
}
|
||||||
|
|
@ -2324,12 +2324,12 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read
|
||||||
// ignore the parameter silently. TODO(rjl493456442)
|
// ignore the parameter silently. TODO(rjl493456442)
|
||||||
// please config it if read mode is implemented.
|
// please config it if read mode is implemented.
|
||||||
config.HashDB = hashdb.Defaults
|
config.HashDB = hashdb.Defaults
|
||||||
return trie.NewDatabase(disk, config)
|
return triedb.NewDatabase(disk, config)
|
||||||
}
|
}
|
||||||
if readOnly {
|
if readOnly {
|
||||||
config.PathDB = pathdb.ReadOnly
|
config.PathDB = pathdb.ReadOnly
|
||||||
} else {
|
} else {
|
||||||
config.PathDB = pathdb.Defaults
|
config.PathDB = pathdb.Defaults
|
||||||
}
|
}
|
||||||
return trie.NewDatabase(disk, config)
|
return triedb.NewDatabase(disk, config)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
185
cmd/utils/history_test.go
Normal file
185
cmd/utils/history_test.go
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
// Copyright 2023 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 utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"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/internal/era"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
count uint64 = 128
|
||||||
|
step uint64 = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHistoryImportAndExport(t *testing.T) {
|
||||||
|
var (
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
genesis = &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{address: {Balance: big.NewInt(1000000000000000000)}},
|
||||||
|
}
|
||||||
|
signer = types.LatestSigner(genesis.Config)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate chain.
|
||||||
|
db, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), int(count), func(i int, g *core.BlockGen) {
|
||||||
|
if i == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
|
||||||
|
ChainID: genesis.Config.ChainID,
|
||||||
|
Nonce: uint64(i - 1),
|
||||||
|
GasTipCap: common.Big0,
|
||||||
|
GasFeeCap: g.PrevBlock(0).BaseFee(),
|
||||||
|
Gas: 50000,
|
||||||
|
To: &common.Address{0xaa},
|
||||||
|
Value: big.NewInt(int64(i)),
|
||||||
|
Data: nil,
|
||||||
|
AccessList: nil,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating tx: %v", err)
|
||||||
|
}
|
||||||
|
g.AddTx(tx)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Initialize BlockChain.
|
||||||
|
chain, err := core.NewBlockChain(db, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to initialize chain: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("error insterting chain: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make temp directory for era files.
|
||||||
|
dir, err := os.MkdirTemp("", "history-export-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating temp test directory: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
// Export history to temp directory.
|
||||||
|
if err := ExportHistory(chain, dir, 0, count, step); err != nil {
|
||||||
|
t.Fatalf("error exporting history: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read checksums.
|
||||||
|
b, err := os.ReadFile(path.Join(dir, "checksums.txt"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read checksums: %v", err)
|
||||||
|
}
|
||||||
|
checksums := strings.Split(string(b), "\n")
|
||||||
|
|
||||||
|
// Verify each Era.
|
||||||
|
entries, _ := era.ReadDir(dir, "mainnet")
|
||||||
|
for i, filename := range entries {
|
||||||
|
func() {
|
||||||
|
f, err := os.Open(path.Join(dir, filename))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error opening era file: %v", err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
h = sha256.New()
|
||||||
|
buf = bytes.NewBuffer(nil)
|
||||||
|
)
|
||||||
|
if _, err := io.Copy(h, f); err != nil {
|
||||||
|
t.Fatalf("unable to recalculate checksum: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; got != want {
|
||||||
|
t.Fatalf("checksum %d does not match: got %s, want %s", i, got, want)
|
||||||
|
}
|
||||||
|
e, err := era.From(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error opening era: %v", err)
|
||||||
|
}
|
||||||
|
defer e.Close()
|
||||||
|
it, err := era.NewIterator(e)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error making era reader: %v", err)
|
||||||
|
}
|
||||||
|
for j := 0; it.Next(); j++ {
|
||||||
|
n := i*int(step) + j
|
||||||
|
if it.Error() != nil {
|
||||||
|
t.Fatalf("error reading block entry %d: %v", n, it.Error())
|
||||||
|
}
|
||||||
|
block, receipts, err := it.BlockAndReceipts()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error reading block entry %d: %v", n, err)
|
||||||
|
}
|
||||||
|
want := chain.GetBlockByNumber(uint64(n))
|
||||||
|
if want, got := uint64(n), block.NumberU64(); want != got {
|
||||||
|
t.Fatalf("blocks out of order: want %d, got %d", want, got)
|
||||||
|
}
|
||||||
|
if want.Hash() != block.Hash() {
|
||||||
|
t.Fatalf("block hash mismatch %d: want %s, got %s", n, want.Hash().Hex(), block.Hash().Hex())
|
||||||
|
}
|
||||||
|
if got := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); got != want.TxHash() {
|
||||||
|
t.Fatalf("tx hash %d mismatch: want %s, got %s", n, want.TxHash(), got)
|
||||||
|
}
|
||||||
|
if got := types.CalcUncleHash(block.Uncles()); got != want.UncleHash() {
|
||||||
|
t.Fatalf("uncle hash %d mismatch: want %s, got %s", n, want.UncleHash(), got)
|
||||||
|
}
|
||||||
|
if got := types.DeriveSha(receipts, trie.NewStackTrie(nil)); got != want.ReceiptHash() {
|
||||||
|
t.Fatalf("receipt root %d mismatch: want %s, got %s", n, want.ReceiptHash(), got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now import Era.
|
||||||
|
freezer := t.TempDir()
|
||||||
|
db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false, false, false)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
db2.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
genesis.MustCommit(db2, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
|
imported, err := core.NewBlockChain(db2, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to initialize chain: %v", err)
|
||||||
|
}
|
||||||
|
if err := ImportHistory(imported, db2, dir, "mainnet"); err != nil {
|
||||||
|
t.Fatalf("failed to import chain: %v", err)
|
||||||
|
}
|
||||||
|
if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() {
|
||||||
|
t.Fatalf("imported chain does not match expected, have (%d, %s) want (%d, %s)", have.Number, have.Hash(), want.Number, want.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import "math/big"
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
// Common big integers often used
|
// Common big integers often used
|
||||||
var (
|
var (
|
||||||
|
|
@ -27,4 +31,6 @@ var (
|
||||||
Big32 = big.NewInt(32)
|
Big32 = big.NewInt(32)
|
||||||
Big256 = big.NewInt(256)
|
Big256 = big.NewInt(256)
|
||||||
Big257 = big.NewInt(257)
|
Big257 = big.NewInt(257)
|
||||||
|
|
||||||
|
U2560 = uint256.NewInt(0)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,10 @@ func BigMinUint256(x, y *uint256.Int) *uint256.Int {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BigIntToUint256Int(x *big.Int) *uint256.Int {
|
||||||
|
return new(uint256.Int).SetUint64(x.Uint64())
|
||||||
|
}
|
||||||
|
|
||||||
// FirstBitSet returns the index of the first 1 bit in v, counting from LSB.
|
// FirstBitSet returns the index of the first 1 bit in v, counting from LSB.
|
||||||
func FirstBitSet(v *big.Int) int {
|
func FirstBitSet(v *big.Int) int {
|
||||||
for i := 0; i < v.BitLen(); i++ {
|
for i := 0; i < v.BitLen(); i++ {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Proof-of-stake protocol constants.
|
// Proof-of-stake protocol constants.
|
||||||
|
|
@ -356,8 +357,8 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
||||||
// Withdrawals processing.
|
// Withdrawals processing.
|
||||||
for _, w := range withdrawals {
|
for _, w := range withdrawals {
|
||||||
// Convert amount from gwei to wei.
|
// Convert amount from gwei to wei.
|
||||||
amount := new(big.Int).SetUint64(w.Amount)
|
amount := new(uint256.Int).SetUint64(w.Amount)
|
||||||
amount = amount.Mul(amount, big.NewInt(params.GWei))
|
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
|
||||||
state.AddBalance(w.Address, amount)
|
state.AddBalance(w.Address, amount)
|
||||||
}
|
}
|
||||||
// No block reward which is issued by consensus layer instead.
|
// No block reward which is issued by consensus layer instead.
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
|
@ -884,8 +885,8 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.State
|
||||||
log.Info("change contract code", "address", addr)
|
log.Info("change contract code", "address", addr)
|
||||||
state.SetCode(addr, account.Code)
|
state.SetCode(addr, account.Code)
|
||||||
|
|
||||||
if state.GetBalance(addr).Cmp(big.NewInt(0)) == 0 {
|
if state.GetBalance(addr).Cmp(uint256.NewInt(0)) == 0 {
|
||||||
state.SetBalance(addr, account.Balance)
|
state.SetBalance(addr, uint256.NewInt(account.Balance.Uint64()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenesisContractChange(t *testing.T) {
|
func TestGenesisContractChange(t *testing.T) {
|
||||||
|
|
@ -63,7 +63,7 @@ func TestGenesisContractChange(t *testing.T) {
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
|
||||||
genesis := genspec.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
|
genesis := genspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
|
|
||||||
statedb, err := state.New(genesis.Root(), state.NewDatabase(db), nil)
|
statedb, err := state.New(genesis.Root(), state.NewDatabase(db), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var systemAddress = common.HexToAddress("0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE")
|
var systemAddress = common.HexToAddress("0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE")
|
||||||
|
|
@ -85,7 +86,7 @@ func ApplyMessage(
|
||||||
*msg.To(),
|
*msg.To(),
|
||||||
msg.Data(),
|
msg.Data(),
|
||||||
msg.Gas(),
|
msg.Gas(),
|
||||||
msg.Value(),
|
uint256.NewInt(msg.Value().Uint64()),
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -118,7 +119,7 @@ func ApplyBorMessage(vmenv *vm.EVM, msg Callmsg) (*core.ExecutionResult, error)
|
||||||
*msg.To(),
|
*msg.To(),
|
||||||
msg.Data(),
|
msg.Data(),
|
||||||
msg.Gas(),
|
msg.Gas(),
|
||||||
msg.Value(),
|
uint256.NewInt(msg.Value().Uint64()),
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
// Update the state with pending changes
|
// Update the state with pending changes
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func TestReimportMirroredState(t *testing.T) {
|
||||||
genspec := &core.Genesis{
|
genspec := &core.Genesis{
|
||||||
Config: params.AllCliqueProtocolChanges,
|
Config: params.AllCliqueProtocolChanges,
|
||||||
ExtraData: make([]byte, extraVanity+common.AddressLength+extraSeal),
|
ExtraData: make([]byte, extraVanity+common.AddressLength+extraSeal),
|
||||||
Alloc: map[common.Address]core.GenesisAccount{
|
Alloc: map[common.Address]types.Account{
|
||||||
addr: {Balance: big.NewInt(10000000000000000)},
|
addr: {Balance: big.NewInt(10000000000000000)},
|
||||||
},
|
},
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
|
|
|
||||||
|
|
@ -34,16 +34,17 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ethash proof-of-work protocol constants.
|
// Ethash proof-of-work protocol constants.
|
||||||
var (
|
var (
|
||||||
FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block
|
FrontierBlockReward = uint256.NewInt(5e+18) // Block reward in wei for successfully mining a block
|
||||||
ByzantiumBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium
|
ByzantiumBlockReward = uint256.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium
|
||||||
ConstantinopleBlockReward = big.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople
|
ConstantinopleBlockReward = uint256.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople
|
||||||
maxUncles = 2 // Maximum number of uncles allowed in a single block
|
maxUncles = 2 // Maximum number of uncles allowed in a single block
|
||||||
allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks
|
allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks
|
||||||
|
|
||||||
// calcDifficultyEip5133 is the difficulty adjustment algorithm as specified by EIP 5133.
|
// calcDifficultyEip5133 is the difficulty adjustment algorithm as specified by EIP 5133.
|
||||||
// It offsets the bomb a total of 11.4M blocks.
|
// It offsets the bomb a total of 11.4M blocks.
|
||||||
|
|
@ -564,8 +565,8 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
|
||||||
|
|
||||||
// Some weird constants to avoid constant memory allocs for them.
|
// Some weird constants to avoid constant memory allocs for them.
|
||||||
var (
|
var (
|
||||||
big8 = big.NewInt(8)
|
u256_8 = uint256.NewInt(8)
|
||||||
big32 = big.NewInt(32)
|
u256_32 = uint256.NewInt(32)
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccumulateRewards credits the coinbase of the given block with the mining
|
// AccumulateRewards credits the coinbase of the given block with the mining
|
||||||
|
|
@ -581,16 +582,18 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
|
||||||
blockReward = ConstantinopleBlockReward
|
blockReward = ConstantinopleBlockReward
|
||||||
}
|
}
|
||||||
// Accumulate the rewards for the miner and any included uncles
|
// Accumulate the rewards for the miner and any included uncles
|
||||||
reward := new(big.Int).Set(blockReward)
|
reward := new(uint256.Int).Set(blockReward)
|
||||||
r := new(big.Int)
|
r := new(uint256.Int)
|
||||||
|
hNum, _ := uint256.FromBig(header.Number)
|
||||||
for _, uncle := range uncles {
|
for _, uncle := range uncles {
|
||||||
r.Add(uncle.Number, big8)
|
uNum, _ := uint256.FromBig(uncle.Number)
|
||||||
r.Sub(r, header.Number)
|
r.AddUint64(uNum, 8)
|
||||||
|
r.Sub(r, hNum)
|
||||||
r.Mul(r, blockReward)
|
r.Mul(r, blockReward)
|
||||||
r.Div(r, big8)
|
r.Div(r, u256_8)
|
||||||
state.AddBalance(uncle.Coinbase, r)
|
state.AddBalance(uncle.Coinbase, r)
|
||||||
|
|
||||||
r.Div(blockReward, big32)
|
r.Div(blockReward, u256_32)
|
||||||
reward.Add(reward, r)
|
reward.Add(reward, r)
|
||||||
}
|
}
|
||||||
state.AddBalance(header.Coinbase, reward)
|
state.AddBalance(header.Coinbase, reward)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -81,6 +82,6 @@ func ApplyDAOHardFork(statedb *state.StateDB) {
|
||||||
// Move every DAO account and extra-balance account funds into the refund contract
|
// Move every DAO account and extra-balance account funds into the refund contract
|
||||||
for _, addr := range params.DAODrainList() {
|
for _, addr := range params.DAODrainList() {
|
||||||
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr))
|
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr))
|
||||||
statedb.SetBalance(addr, new(big.Int))
|
statedb.SetBalance(addr, new(uint256.Int))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||||
// generator function.
|
// generator function.
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
|
Alloc: types.GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
|
||||||
}
|
}
|
||||||
_, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), b.N, gen)
|
_, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), b.N, gen)
|
||||||
|
|
||||||
|
|
@ -257,7 +257,7 @@ func BenchmarkChainWrite_full_500k(b *testing.B) {
|
||||||
|
|
||||||
// makeChainForBench writes a given number of headers or empty blocks/receipts
|
// makeChainForBench writes a given number of headers or empty blocks/receipts
|
||||||
// into a database.
|
// into a database.
|
||||||
func makeChainForBench(db ethdb.Database, full bool, count uint64) {
|
func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uint64) {
|
||||||
var hash common.Hash
|
var hash common.Hash
|
||||||
for n := uint64(0); n < count; n++ {
|
for n := uint64(0); n < count; n++ {
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
|
|
@ -269,6 +269,9 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
|
||||||
TxHash: types.EmptyTxsHash,
|
TxHash: types.EmptyTxsHash,
|
||||||
ReceiptHash: types.EmptyReceiptsHash,
|
ReceiptHash: types.EmptyReceiptsHash,
|
||||||
}
|
}
|
||||||
|
if n == 0 {
|
||||||
|
header = genesis.ToBlock().Header()
|
||||||
|
}
|
||||||
hash = header.Hash()
|
hash = header.Hash()
|
||||||
|
|
||||||
rawdb.WriteHeader(db, header)
|
rawdb.WriteHeader(db, header)
|
||||||
|
|
@ -276,7 +279,7 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
|
||||||
rawdb.WriteTd(db, hash, n, big.NewInt(int64(n+1)))
|
rawdb.WriteTd(db, hash, n, big.NewInt(int64(n+1)))
|
||||||
|
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
rawdb.WriteChainConfig(db, hash, params.AllEthashProtocolChanges)
|
rawdb.WriteChainConfig(db, hash, genesis.Config)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawdb.WriteHeadHeaderHash(db, hash)
|
rawdb.WriteHeadHeaderHash(db, hash)
|
||||||
|
|
@ -291,6 +294,7 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchWriteChain(b *testing.B, full bool, count uint64) {
|
func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||||
|
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
dir := b.TempDir()
|
dir := b.TempDir()
|
||||||
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
|
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
|
||||||
|
|
@ -299,7 +303,7 @@ func benchWriteChain(b *testing.B, full bool, count uint64) {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
makeChainForBench(db, full, count)
|
makeChainForBench(db, genesis, full, count)
|
||||||
db.Close()
|
db.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -312,7 +316,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
makeChainForBench(db, full, count)
|
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
|
||||||
|
makeChainForBench(db, genesis, full, count)
|
||||||
db.Close()
|
db.Close()
|
||||||
|
|
||||||
cacheConfig := *defaultCacheConfig
|
cacheConfig := *defaultCacheConfig
|
||||||
|
|
@ -327,7 +332,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
chain, err := NewBlockChain(db, &cacheConfig, nil, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
chain, err := NewBlockChain(db, &cacheConfig, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error creating chain: %v", err)
|
b.Fatalf("error creating chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: &config,
|
Config: &config,
|
||||||
ExtraData: make([]byte, 32+common.AddressLength+crypto.SignatureLength),
|
ExtraData: make([]byte, 32+common.AddressLength+crypto.SignatureLength),
|
||||||
Alloc: map[common.Address]GenesisAccount{
|
Alloc: map[common.Address]types.Account{
|
||||||
addr: {Balance: big.NewInt(1)},
|
addr: {Balance: big.NewInt(1)},
|
||||||
},
|
},
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -159,8 +159,8 @@ type CacheConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// triedbConfig derives the configures for trie database.
|
// triedbConfig derives the configures for trie database.
|
||||||
func (c *CacheConfig) triedbConfig() *trie.Config {
|
func (c *CacheConfig) triedbConfig() *triedb.Config {
|
||||||
config := &trie.Config{Preimages: c.Preimages}
|
config := &triedb.Config{Preimages: c.Preimages}
|
||||||
if c.StateScheme == rawdb.HashScheme {
|
if c.StateScheme == rawdb.HashScheme {
|
||||||
config.HashDB = &hashdb.Config{
|
config.HashDB = &hashdb.Config{
|
||||||
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
||||||
|
|
@ -198,6 +198,13 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig {
|
||||||
|
|
||||||
var DefaultCacheConfig = defaultCacheConfig
|
var DefaultCacheConfig = defaultCacheConfig
|
||||||
|
|
||||||
|
// txLookup is wrapper over transaction lookup along with the corresponding
|
||||||
|
// transaction object.
|
||||||
|
type txLookup struct {
|
||||||
|
lookup *rawdb.LegacyTxLookupEntry
|
||||||
|
transaction *types.Transaction
|
||||||
|
}
|
||||||
|
|
||||||
// BlockChain represents the canonical chain given a database with a genesis
|
// BlockChain represents the canonical chain given a database with a genesis
|
||||||
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
|
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
|
||||||
//
|
//
|
||||||
|
|
@ -222,15 +229,9 @@ type BlockChain struct {
|
||||||
gcproc time.Duration // Accumulates canonical block processing for trie dumping
|
gcproc time.Duration // Accumulates canonical block processing for trie dumping
|
||||||
lastWrite uint64 // Last block when the state was flushed
|
lastWrite uint64 // Last block when the state was flushed
|
||||||
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
|
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
|
||||||
triedb *trie.Database // The database handler for maintaining trie nodes.
|
triedb *triedb.Database // The database handler for maintaining trie nodes.
|
||||||
stateCache state.Database // State database to reuse between imports (contains state cache)
|
stateCache state.Database // State database to reuse between imports (contains state cache)
|
||||||
|
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||||
// txLookupLimit is the maximum number of blocks from head whose tx indices
|
|
||||||
// are reserved:
|
|
||||||
// * 0: means no limit and regenerate any missing indexes
|
|
||||||
// * N: means N block limit [HEAD-N+1, HEAD] and delete extra indexes
|
|
||||||
// * nil: disable tx reindexer/deleter, but still index new blocks
|
|
||||||
txLookupLimit uint64
|
|
||||||
|
|
||||||
hc *HeaderChain
|
hc *HeaderChain
|
||||||
rmLogsFeed event.Feed
|
rmLogsFeed event.Feed
|
||||||
|
|
@ -255,15 +256,15 @@ type BlockChain struct {
|
||||||
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
|
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
|
||||||
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
|
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
|
||||||
blockCache *lru.Cache[common.Hash, *types.Block]
|
blockCache *lru.Cache[common.Hash, *types.Block]
|
||||||
txLookupCache *lru.Cache[common.Hash, *rawdb.LegacyTxLookupEntry]
|
txLookupCache *lru.Cache[common.Hash, txLookup]
|
||||||
|
|
||||||
// future blocks are blocks added for later processing
|
// future blocks are blocks added for later processing
|
||||||
futureBlocks *lru.Cache[common.Hash, *types.Block]
|
futureBlocks *lru.Cache[common.Hash, *types.Block]
|
||||||
|
|
||||||
wg sync.WaitGroup //
|
wg sync.WaitGroup
|
||||||
quit chan struct{} // shutdown signal, closed in Stop.
|
quit chan struct{} // shutdown signal, closed in Stop.
|
||||||
stopping atomic.Bool // false if chain is running, true when stopped
|
stopping atomic.Bool // false if chain is running, true when stopped
|
||||||
procInterrupt atomic.Bool // interrupt signaler for block processing
|
procInterrupt atomic.Bool // interrupt signaler for block processing
|
||||||
|
|
||||||
engine consensus.Engine
|
engine consensus.Engine
|
||||||
validator Validator // Block and state validator interface
|
validator Validator // Block and state validator interface
|
||||||
|
|
@ -295,7 +296,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
cacheConfig.TriesInMemory = defaultCacheConfig.TriesInMemory
|
cacheConfig.TriesInMemory = defaultCacheConfig.TriesInMemory
|
||||||
}
|
}
|
||||||
// Open trie database with provided config
|
// Open trie database with provided config
|
||||||
triedb := trie.NewDatabase(db, cacheConfig.triedbConfig())
|
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig())
|
||||||
|
|
||||||
// Setup the genesis block, commit the provided genesis specification
|
// Setup the genesis block, commit the provided genesis specification
|
||||||
// to database if the genesis block is not present yet, or load the
|
// to database if the genesis block is not present yet, or load the
|
||||||
|
|
@ -327,7 +328,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
|
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
|
||||||
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
|
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
|
||||||
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
|
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
|
||||||
txLookupCache: lru.NewCache[common.Hash, *rawdb.LegacyTxLookupEntry](txLookupCacheLimit),
|
txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit),
|
||||||
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
|
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
|
||||||
engine: engine,
|
engine: engine,
|
||||||
vmConfig: vmConfig,
|
vmConfig: vmConfig,
|
||||||
|
|
@ -513,13 +514,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
|
|
||||||
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
||||||
}
|
}
|
||||||
// Start tx indexer/unindexer if required.
|
// Start tx indexer if it's enabled.
|
||||||
if txLookupLimit != nil {
|
if txLookupLimit != nil {
|
||||||
bc.txLookupLimit = *txLookupLimit
|
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
|
||||||
|
|
||||||
bc.wg.Add(1)
|
|
||||||
|
|
||||||
go bc.maintainTxIndex()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bc, nil
|
return bc, nil
|
||||||
|
|
@ -1154,7 +1151,10 @@ func (bc *BlockChain) stopWithoutSaving() {
|
||||||
if !bc.stopping.CompareAndSwap(false, true) {
|
if !bc.stopping.CompareAndSwap(false, true) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Signal shutdown tx indexer.
|
||||||
|
if bc.txIndexer != nil {
|
||||||
|
bc.txIndexer.close()
|
||||||
|
}
|
||||||
// Unsubscribe all subscriptions registered from blockchain.
|
// Unsubscribe all subscriptions registered from blockchain.
|
||||||
bc.scope.Close()
|
bc.scope.Close()
|
||||||
|
|
||||||
|
|
@ -1400,12 +1400,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
// Write all chain data to ancients.
|
// Write all chain data to ancients.
|
||||||
td := bc.GetTd(first.Hash(), first.NumberU64())
|
td := bc.GetTd(first.Hash(), first.NumberU64())
|
||||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain, borReceipts, td)
|
writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain, borReceipts, td)
|
||||||
size += writeSize
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error importing chain data to ancients", "err", err)
|
log.Error("Error importing chain data to ancients", "err", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
size += writeSize
|
||||||
|
|
||||||
// Write tx indices if any condition is satisfied:
|
// Write tx indices if any condition is satisfied:
|
||||||
// * If user requires to reserve all tx indices(txlookuplimit=0)
|
// * If user requires to reserve all tx indices(txlookuplimit=0)
|
||||||
|
|
@ -1420,7 +1419,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
// generated.
|
// generated.
|
||||||
batch := bc.db.NewBatch()
|
batch := bc.db.NewBatch()
|
||||||
for i, block := range blockChain {
|
for i, block := range blockChain {
|
||||||
if bc.txLookupLimit == 0 || ancientLimit <= bc.txLookupLimit || block.NumberU64() >= ancientLimit-bc.txLookupLimit {
|
if bc.txIndexer.limit == 0 || ancientLimit <= bc.txIndexer.limit || block.NumberU64() >= ancientLimit-bc.txIndexer.limit {
|
||||||
rawdb.WriteTxLookupEntriesByBlock(batch, block)
|
rawdb.WriteTxLookupEntriesByBlock(batch, block)
|
||||||
} else if rawdb.ReadTxIndexTail(bc.db) != nil {
|
} else if rawdb.ReadTxIndexTail(bc.db) != nil {
|
||||||
rawdb.WriteTxLookupEntriesByBlock(batch, block)
|
rawdb.WriteTxLookupEntriesByBlock(batch, block)
|
||||||
|
|
@ -1461,9 +1460,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete block data from the main database.
|
// Delete block data from the main database.
|
||||||
batch.Reset()
|
var (
|
||||||
|
canonHashes = make(map[common.Hash]struct{})
|
||||||
canonHashes := make(map[common.Hash]struct{})
|
)
|
||||||
for _, block := range blockChain {
|
for _, block := range blockChain {
|
||||||
canonHashes[block.Hash()] = struct{}{}
|
canonHashes[block.Hash()] = struct{}{}
|
||||||
|
|
||||||
|
|
@ -1485,15 +1484,18 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stats.processed += int32(len(blockChain))
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeLive writes blockchain and corresponding receipt chain into active store.
|
// writeLive writes blockchain and corresponding receipt chain into active store.
|
||||||
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
||||||
skipPresenceCheck := false
|
|
||||||
batch := bc.db.NewBatch()
|
|
||||||
|
|
||||||
headers := make([]*types.Header, 0, len(blockChain))
|
headers := make([]*types.Header, 0, len(blockChain))
|
||||||
|
var (
|
||||||
|
skipPresenceCheck = false
|
||||||
|
batch = bc.db.NewBatch()
|
||||||
|
)
|
||||||
for i, block := range blockChain {
|
for i, block := range blockChain {
|
||||||
// Update the headers for bor specific reorg check
|
// Update the headers for bor specific reorg check
|
||||||
headers = append(headers, block.Header())
|
headers = append(headers, block.Header())
|
||||||
|
|
@ -1522,11 +1524,10 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
// Write all the data out into the database
|
// Write all the data out into the database
|
||||||
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
|
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
|
||||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
||||||
rawdb.WriteTxLookupEntriesByBlock(batch, block) // Always write tx indices for live blocks, we assume they are needed
|
|
||||||
|
|
||||||
// Write everything belongs to the blocks into the database. So that
|
// Write everything belongs to the blocks into the database. So that
|
||||||
// we can ensure all components of body is completed(body, receipts,
|
// we can ensure all components of body is completed(body, receipts)
|
||||||
// tx indexes)
|
// except transaction indexes(will be created once sync is finished).
|
||||||
if batch.ValueSize() >= ethdb.IdealBatchSize {
|
if batch.ValueSize() >= ethdb.IdealBatchSize {
|
||||||
if err := batch.Write(); err != nil {
|
if err := batch.Write(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -1564,19 +1565,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Write the tx index tail (block number from where we index) before write any live blocks
|
|
||||||
if len(liveBlocks) > 0 && liveBlocks[0].NumberU64() == ancientLimit+1 {
|
|
||||||
// The tx index tail can only be one of the following two options:
|
|
||||||
// * 0: all ancient blocks have been indexed
|
|
||||||
// * ancient-limit: the indices of blocks before ancient-limit are ignored
|
|
||||||
if tail := rawdb.ReadTxIndexTail(bc.db); tail == nil {
|
|
||||||
if bc.txLookupLimit == 0 || ancientLimit <= bc.txLookupLimit {
|
|
||||||
rawdb.WriteTxIndexTail(bc.db, 0)
|
|
||||||
} else {
|
|
||||||
rawdb.WriteTxIndexTail(bc.db, ancientLimit-bc.txLookupLimit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(liveBlocks) > 0 {
|
if len(liveBlocks) > 0 {
|
||||||
if n, err := writeLive(liveBlocks, liveReceipts); err != nil {
|
if n, err := writeLive(liveBlocks, liveReceipts); err != nil {
|
||||||
|
|
@ -1587,14 +1575,15 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
|
head = blockChain[len(blockChain)-1]
|
||||||
|
|
||||||
head := blockChain[len(blockChain)-1]
|
context = []interface{}{
|
||||||
|
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
|
||||||
context := []interface{}{
|
"number", head.Number(), "hash", head.Hash(), "age", common.PrettyAge(time.Unix(int64(head.Time()), 0)),
|
||||||
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
|
"size", common.StorageSize(size),
|
||||||
"number", head.Number(), "hash", head.Hash(), "age", common.PrettyAge(time.Unix(int64(head.Time()), 0)),
|
}
|
||||||
"size", common.StorageSize(size),
|
)
|
||||||
}
|
|
||||||
if stats.ignored > 0 {
|
if stats.ignored > 0 {
|
||||||
context = append(context, []interface{}{"ignored", stats.ignored}...)
|
context = append(context, []interface{}{"ignored", stats.ignored}...)
|
||||||
}
|
}
|
||||||
|
|
@ -1611,7 +1600,6 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (e
|
||||||
if bc.insertStopped() {
|
if bc.insertStopped() {
|
||||||
return errInsertionInterrupted
|
return errInsertionInterrupted
|
||||||
}
|
}
|
||||||
|
|
||||||
batch := bc.db.NewBatch()
|
batch := bc.db.NewBatch()
|
||||||
rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td)
|
rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td)
|
||||||
rawdb.WriteBlock(batch, block)
|
rawdb.WriteBlock(batch, block)
|
||||||
|
|
@ -2973,109 +2961,6 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// indexBlocks reindexes or unindexes transactions depending on user configuration
|
|
||||||
func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{}) {
|
|
||||||
defer func() { close(done) }()
|
|
||||||
|
|
||||||
// If head is 0, it means the chain is just initialized and no blocks are inserted,
|
|
||||||
// so don't need to indexing anything.
|
|
||||||
if head == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The tail flag is not existent, it means the node is just initialized
|
|
||||||
// and all blocks(may from ancient store) are not indexed yet.
|
|
||||||
if tail == nil {
|
|
||||||
from := uint64(0)
|
|
||||||
if bc.txLookupLimit != 0 && head >= bc.txLookupLimit {
|
|
||||||
from = head - bc.txLookupLimit + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
rawdb.IndexTransactions(bc.db, from, head+1, bc.quit)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// The tail flag is existent, but the whole chain is required to be indexed.
|
|
||||||
if bc.txLookupLimit == 0 || head < bc.txLookupLimit {
|
|
||||||
if *tail > 0 {
|
|
||||||
// It can happen when chain is rewound to a historical point which
|
|
||||||
// is even lower than the indexes tail, recap the indexing target
|
|
||||||
// to new head to avoid reading non-existent block bodies.
|
|
||||||
end := *tail
|
|
||||||
if end > head+1 {
|
|
||||||
end = head + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
rawdb.IndexTransactions(bc.db, 0, end, bc.quit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Update the transaction index to the new chain state
|
|
||||||
if head-bc.txLookupLimit+1 < *tail {
|
|
||||||
// Reindex a part of missing indices and rewind index tail to HEAD-limit
|
|
||||||
rawdb.IndexTransactions(bc.db, head-bc.txLookupLimit+1, *tail, bc.quit)
|
|
||||||
} else {
|
|
||||||
// Unindex a part of stale indices and forward index tail to HEAD-limit
|
|
||||||
rawdb.UnindexTransactions(bc.db, *tail, head-bc.txLookupLimit+1, bc.quit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// maintainTxIndex is responsible for the construction and deletion of the
|
|
||||||
// transaction index.
|
|
||||||
//
|
|
||||||
// User can use flag `txlookuplimit` to specify a "recentness" block, below
|
|
||||||
// which ancient tx indices get deleted. If `txlookuplimit` is 0, it means
|
|
||||||
// all tx indices will be reserved.
|
|
||||||
//
|
|
||||||
// The user can adjust the txlookuplimit value for each launch after sync,
|
|
||||||
// Geth will automatically construct the missing indices or delete the extra
|
|
||||||
// indices.
|
|
||||||
func (bc *BlockChain) maintainTxIndex() {
|
|
||||||
defer bc.wg.Done()
|
|
||||||
|
|
||||||
// Listening to chain events and manipulate the transaction indexes.
|
|
||||||
var (
|
|
||||||
done chan struct{} // Non-nil if background unindexing or reindexing routine is active.
|
|
||||||
headCh = make(chan ChainHeadEvent, 1) // Buffered to avoid locking up the event feed
|
|
||||||
)
|
|
||||||
|
|
||||||
sub := bc.SubscribeChainHeadEvent(headCh)
|
|
||||||
if sub == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer sub.Unsubscribe()
|
|
||||||
log.Info("Initialized transaction indexer", "limit", bc.TxLookupLimit())
|
|
||||||
|
|
||||||
// Launch the initial processing if chain is not empty. This step is
|
|
||||||
// useful in these scenarios that chain has no progress and indexer
|
|
||||||
// is never triggered.
|
|
||||||
if head := rawdb.ReadHeadBlock(bc.db); head != nil {
|
|
||||||
done = make(chan struct{})
|
|
||||||
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.NumberU64(), done)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case head := <-headCh:
|
|
||||||
if done == nil {
|
|
||||||
done = make(chan struct{})
|
|
||||||
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.Block.NumberU64(), done)
|
|
||||||
}
|
|
||||||
case <-done:
|
|
||||||
done = nil
|
|
||||||
case <-bc.quit:
|
|
||||||
if done != nil {
|
|
||||||
log.Info("Waiting background transaction indexer to exit")
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reportBlock logs a bad block error.
|
// reportBlock logs a bad block error.
|
||||||
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
|
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
|
||||||
rawdb.WriteBadBlock(bc.db, block)
|
rawdb.WriteBadBlock(bc.db, block)
|
||||||
|
|
@ -3151,7 +3036,7 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
|
||||||
bc.flushInterval.Store(int64(interval))
|
bc.flushInterval.Store(int64(interval))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTrieFlushInterval gets the in-memory tries flush interval
|
// GetTrieFlushInterval gets the in-memory tries flushAlloc interval
|
||||||
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
|
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
|
||||||
return time.Duration(bc.flushInterval.Load())
|
return time.Duration(bc.flushInterval.Load())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestChain2HeadEvent(t *testing.T) {
|
func TestChain2HeadEvent(t *testing.T) {
|
||||||
|
|
@ -24,7 +24,7 @@ func TestChain2HeadEvent(t *testing.T) {
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
|
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
|
genesis = gspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -30,7 +31,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CurrentHeader retrieves the current head header of the canonical chain. The
|
// CurrentHeader retrieves the current head header of the canonical chain. The
|
||||||
|
|
@ -287,23 +288,48 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
|
||||||
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionLookup retrieves the lookup associate with the given transaction
|
// GetTransactionLookup retrieves the lookup along with the transaction
|
||||||
// hash from the cache or database.
|
// itself associate with the given transaction hash.
|
||||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry {
|
//
|
||||||
|
// An error will be returned if the transaction is not found, and background
|
||||||
|
// indexing for transactions is still in progress. The transaction might be
|
||||||
|
// reachable shortly once it's indexed.
|
||||||
|
//
|
||||||
|
// A null will be returned in the transaction is not found and background
|
||||||
|
// transaction indexing is already finished. The transaction is not existent
|
||||||
|
// from the node's perspective.
|
||||||
|
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
|
||||||
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
||||||
if lookup, exist := bc.txLookupCache.Get(hash); exist {
|
if item, exist := bc.txLookupCache.Get(hash); exist {
|
||||||
return lookup
|
return item.lookup, item.transaction, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
return nil
|
progress, err := bc.TxIndexProgress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
// The transaction indexing is not finished yet, returning an
|
||||||
|
// error to explicitly indicate it.
|
||||||
|
if !progress.Done() {
|
||||||
|
return nil, nil, errors.New("transaction indexing still in progress")
|
||||||
|
}
|
||||||
|
// The transaction is already indexed, the transaction is either
|
||||||
|
// not existent or not in the range of index, returning null.
|
||||||
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
lookup := &rawdb.LegacyTxLookupEntry{
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockIndex: blockNumber,
|
||||||
|
Index: txIndex,
|
||||||
|
}
|
||||||
|
bc.txLookupCache.Add(hash, txLookup{
|
||||||
|
lookup: lookup,
|
||||||
|
transaction: tx,
|
||||||
|
})
|
||||||
|
|
||||||
lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex}
|
return lookup, tx, nil
|
||||||
bc.txLookupCache.Add(hash, lookup)
|
|
||||||
|
|
||||||
return lookup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTd retrieves a block's total difficulty in the canonical chain from the
|
// GetTd retrieves a block's total difficulty in the canonical chain from the
|
||||||
|
|
@ -407,23 +433,24 @@ func (bc *BlockChain) GetVMConfig() *vm.Config {
|
||||||
return &bc.vmConfig
|
return &bc.vmConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTxLookupLimit is responsible for updating the txlookup limit to the
|
// TxIndexProgress returns the transaction indexing progress.
|
||||||
// original one stored in db if the new mismatches with the old one.
|
func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
||||||
func (bc *BlockChain) SetTxLookupLimit(limit uint64) {
|
if bc.txIndexer == nil {
|
||||||
bc.txLookupLimit = limit
|
return TxIndexProgress{}, errors.New("tx indexer is not enabled")
|
||||||
}
|
}
|
||||||
|
return bc.txIndexer.txIndexProgress()
|
||||||
// TxLookupLimit retrieves the txlookup limit used by blockchain to prune
|
|
||||||
// stale transaction indices.
|
|
||||||
func (bc *BlockChain) TxLookupLimit() uint64 {
|
|
||||||
return bc.txLookupLimit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrieDB retrieves the low level trie database used for data storage.
|
// TrieDB retrieves the low level trie database used for data storage.
|
||||||
func (bc *BlockChain) TrieDB() *trie.Database {
|
func (bc *BlockChain) TrieDB() *triedb.Database {
|
||||||
return bc.triedb
|
return bc.triedb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HeaderChain returns the underlying header chain.
|
||||||
|
func (bc *BlockChain) HeaderChain() *HeaderChain {
|
||||||
|
return bc.hc
|
||||||
|
}
|
||||||
|
|
||||||
// SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent.
|
// SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent.
|
||||||
func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription {
|
func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription {
|
||||||
return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch))
|
return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch))
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// rewindTest is a test case for chain rollback upon user request.
|
// rewindTest is a test case for chain rollback upon user request.
|
||||||
|
|
@ -2035,13 +2035,13 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
}
|
}
|
||||||
// Reopen the trie database without persisting in-memory dirty nodes.
|
// Reopen the trie database without persisting in-memory dirty nodes.
|
||||||
chain.triedb.Close()
|
chain.triedb.Close()
|
||||||
dbconfig := &trie.Config{}
|
dbconfig := &triedb.Config{}
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
dbconfig.PathDB = pathdb.Defaults
|
dbconfig.PathDB = pathdb.Defaults
|
||||||
} else {
|
} else {
|
||||||
dbconfig.HashDB = hashdb.Defaults
|
dbconfig.HashDB = hashdb.Defaults
|
||||||
}
|
}
|
||||||
chain.triedb = trie.NewDatabase(chain.db, dbconfig)
|
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
||||||
chain.stateCache = state.NewDatabaseWithNodeDB(chain.db, chain.triedb)
|
chain.stateCache = state.NewDatabaseWithNodeDB(chain.db, chain.triedb)
|
||||||
|
|
||||||
// Force run a freeze cycle
|
// Force run a freeze cycle
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// So we can deterministically seed different blockchains
|
// So we can deterministically seed different blockchains
|
||||||
|
|
@ -896,7 +897,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||||
funds = big.NewInt(1000000000000000)
|
funds = big.NewInt(1000000000000000)
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
}
|
}
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
|
|
@ -1041,7 +1042,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||||
funds = big.NewInt(1000000000000000)
|
funds = big.NewInt(1000000000000000)
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -1170,7 +1171,7 @@ func testChainTxReorgs(t *testing.T, scheme string) {
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
GasLimit: 3141592,
|
GasLimit: 3141592,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
addr1: {Balance: big.NewInt(1000000000000000)},
|
addr1: {Balance: big.NewInt(1000000000000000)},
|
||||||
addr2: {Balance: big.NewInt(1000000000000000)},
|
addr2: {Balance: big.NewInt(1000000000000000)},
|
||||||
addr3: {Balance: big.NewInt(1000000000000000)},
|
addr3: {Balance: big.NewInt(1000000000000000)},
|
||||||
|
|
@ -1289,7 +1290,7 @@ func testLogReorgs(t *testing.T, scheme string) {
|
||||||
|
|
||||||
// this code generates a log
|
// this code generates a log
|
||||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1352,7 +1353,7 @@ func testLogRebirth(t *testing.T, scheme string) {
|
||||||
var (
|
var (
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
engine = ethash.NewFaker()
|
engine = ethash.NewFaker()
|
||||||
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil, nil)
|
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil, nil)
|
||||||
|
|
@ -1442,7 +1443,7 @@ func testSideLogRebirth(t *testing.T, scheme string) {
|
||||||
var (
|
var (
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
)
|
)
|
||||||
|
|
@ -1553,7 +1554,7 @@ func testReorgSideEvent(t *testing.T, scheme string) {
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
|
Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
|
||||||
}
|
}
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
)
|
)
|
||||||
|
|
@ -1706,7 +1707,7 @@ func testEIP155Transition(t *testing.T, scheme string) {
|
||||||
EIP155Block: big.NewInt(2),
|
EIP155Block: big.NewInt(2),
|
||||||
HomesteadBlock: new(big.Int),
|
HomesteadBlock: new(big.Int),
|
||||||
},
|
},
|
||||||
Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
|
Alloc: types.GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1834,7 +1835,7 @@ func testEIP161AccountRemoval(t *testing.T, scheme string) {
|
||||||
EIP150Block: new(big.Int),
|
EIP150Block: new(big.Int),
|
||||||
EIP158Block: big.NewInt(2),
|
EIP158Block: big.NewInt(2),
|
||||||
},
|
},
|
||||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -2083,7 +2084,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{address: {Balance: funds}}}
|
||||||
)
|
)
|
||||||
|
|
||||||
height := uint64(1024)
|
height := uint64(1024)
|
||||||
|
|
@ -2311,7 +2312,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
|
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: &chainConfig,
|
Config: &chainConfig,
|
||||||
Alloc: GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
|
Alloc: types.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
}
|
}
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
|
|
@ -2959,218 +2960,6 @@ func testReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T, scheme strin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTransactionIndices(t *testing.T) {
|
|
||||||
// Configure and generate a sample block chain
|
|
||||||
var (
|
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
||||||
funds = big.NewInt(100000000000000000)
|
|
||||||
gspec = &Genesis{
|
|
||||||
Config: params.TestChainConfig,
|
|
||||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
||||||
}
|
|
||||||
signer = types.LatestSigner(gspec.Config)
|
|
||||||
)
|
|
||||||
|
|
||||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 128, func(i int, block *BlockGen) {
|
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
block.AddTx(tx)
|
|
||||||
})
|
|
||||||
|
|
||||||
borReceipts := make([]types.Receipts, len(receipts))
|
|
||||||
|
|
||||||
check := func(tail *uint64, chain *BlockChain) {
|
|
||||||
stored := rawdb.ReadTxIndexTail(chain.db)
|
|
||||||
if tail == nil && stored != nil {
|
|
||||||
t.Fatalf("Oldest indexded block mismatch, want nil, have %d", *stored)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tail != nil && *stored != *tail {
|
|
||||||
t.Fatalf("Oldest indexded block mismatch, want %d, have %d", *tail, *stored)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tail != nil {
|
|
||||||
for i := *tail; i <= chain.CurrentBlock().Number.Uint64(); i++ {
|
|
||||||
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
|
|
||||||
if block.Transactions().Len() == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tx := range block.Transactions() {
|
|
||||||
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index == nil {
|
|
||||||
t.Fatalf("Miss transaction indice, number %d hash %s", i, tx.Hash().Hex())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := uint64(0); i < *tail; i++ {
|
|
||||||
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
|
|
||||||
if block.Transactions().Len() == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tx := range block.Transactions() {
|
|
||||||
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index != nil {
|
|
||||||
t.Fatalf("Transaction indice should be deleted, number %d hash %s", i, tx.Hash().Hex())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Init block chain with external ancients, check all needed indices has been indexed.
|
|
||||||
limit := []uint64{0, 32, 64, 128}
|
|
||||||
for _, l := range limit {
|
|
||||||
frdir := t.TempDir()
|
|
||||||
ancientDb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false)
|
|
||||||
_, _ = rawdb.WriteAncientBlocks(ancientDb, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), append([]types.Receipts{{}}, borReceipts...), big.NewInt(0))
|
|
||||||
|
|
||||||
l := l
|
|
||||||
|
|
||||||
chain, err := NewBlockChain(ancientDb, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.indexBlocks(rawdb.ReadTxIndexTail(ancientDb), 128, make(chan struct{}))
|
|
||||||
|
|
||||||
var tail uint64
|
|
||||||
if l != 0 {
|
|
||||||
tail = uint64(128) - l + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
check(&tail, chain)
|
|
||||||
chain.Stop()
|
|
||||||
ancientDb.Close()
|
|
||||||
os.RemoveAll(frdir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reconstruct a block chain which only reserves HEAD-64 tx indices
|
|
||||||
ancientDb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false)
|
|
||||||
defer ancientDb.Close()
|
|
||||||
|
|
||||||
_, _ = rawdb.WriteAncientBlocks(ancientDb, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), append([]types.Receipts{{}}, borReceipts...), big.NewInt(0))
|
|
||||||
|
|
||||||
limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */}
|
|
||||||
|
|
||||||
for _, l := range limit {
|
|
||||||
l := l
|
|
||||||
|
|
||||||
chain, err := NewBlockChain(ancientDb, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var tail uint64
|
|
||||||
|
|
||||||
if l != 0 {
|
|
||||||
tail = uint64(128) - l + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.indexBlocks(rawdb.ReadTxIndexTail(ancientDb), 128, make(chan struct{}))
|
|
||||||
check(&tail, chain)
|
|
||||||
chain.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSkipStaleTxIndicesInSnapSync(t *testing.T) {
|
|
||||||
testSkipStaleTxIndicesInSnapSync(t, rawdb.HashScheme)
|
|
||||||
testSkipStaleTxIndicesInSnapSync(t, rawdb.PathScheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSkipStaleTxIndicesInSnapSync(t *testing.T, scheme string) {
|
|
||||||
// Configure and generate a sample block chain
|
|
||||||
var (
|
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
||||||
funds = big.NewInt(100000000000000000)
|
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
|
||||||
signer = types.LatestSigner(gspec.Config)
|
|
||||||
)
|
|
||||||
|
|
||||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 128, func(i int, block *BlockGen) {
|
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
block.AddTx(tx)
|
|
||||||
})
|
|
||||||
|
|
||||||
check := func(tail *uint64, chain *BlockChain) {
|
|
||||||
stored := rawdb.ReadTxIndexTail(chain.db)
|
|
||||||
if tail == nil && stored != nil {
|
|
||||||
t.Fatalf("Oldest indexded block mismatch, want nil, have %d", *stored)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tail != nil && *stored != *tail {
|
|
||||||
t.Fatalf("Oldest indexded block mismatch, want %d, have %d", *tail, *stored)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tail != nil {
|
|
||||||
for i := *tail; i <= chain.CurrentBlock().Number.Uint64(); i++ {
|
|
||||||
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
|
|
||||||
if block.Transactions().Len() == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tx := range block.Transactions() {
|
|
||||||
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index == nil {
|
|
||||||
t.Fatalf("Miss transaction indice, number %d hash %s", i, tx.Hash().Hex())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := uint64(0); i < *tail; i++ {
|
|
||||||
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
|
|
||||||
if block.Transactions().Len() == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tx := range block.Transactions() {
|
|
||||||
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index != nil {
|
|
||||||
t.Fatalf("Transaction indice should be deleted, number %d hash %s", i, tx.Hash().Hex())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
|
||||||
}
|
|
||||||
defer ancientDb.Close()
|
|
||||||
|
|
||||||
// Import all blocks into ancient db, only HEAD-32 indices are kept.
|
|
||||||
l := uint64(32)
|
|
||||||
chain, err := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, &l, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer chain.Stop()
|
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
|
||||||
for i, block := range blocks {
|
|
||||||
headers[i] = block.Header()
|
|
||||||
}
|
|
||||||
if n, err := chain.InsertHeaderChain(headers); err != nil {
|
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
||||||
}
|
|
||||||
// The indices before ancient-N(32) should be ignored. After that all blocks should be indexed.
|
|
||||||
if n, err := chain.InsertReceiptChain(blocks, receipts, 64); err != nil {
|
|
||||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tail := uint64(32)
|
|
||||||
check(&tail, chain)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmarks large blocks with value transfers to non-existing accounts
|
// Benchmarks large blocks with value transfers to non-existing accounts
|
||||||
func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address, dataFn func(uint64) []byte) {
|
func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address, dataFn func(uint64) []byte) {
|
||||||
var (
|
var (
|
||||||
|
|
@ -3180,7 +2969,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||||
bankFunds = big.NewInt(100000000000000000)
|
bankFunds = big.NewInt(100000000000000000)
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
testBankAddress: {Balance: bankFunds},
|
testBankAddress: {Balance: bankFunds},
|
||||||
common.HexToAddress("0xc0de"): {
|
common.HexToAddress("0xc0de"): {
|
||||||
Code: []byte{0x60, 0x01, 0x50},
|
Code: []byte{0x60, 0x01, 0x50},
|
||||||
|
|
@ -3376,7 +3165,7 @@ func testDeleteCreateRevert(t *testing.T, scheme string) {
|
||||||
funds = big.NewInt(100000000000000000)
|
funds = big.NewInt(100000000000000000)
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
// The address 0xAAAAA selfdestructs if called
|
// The address 0xAAAAA selfdestructs if called
|
||||||
aa: {
|
aa: {
|
||||||
|
|
@ -3502,7 +3291,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) {
|
||||||
|
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
// The address 0xAAAAA selfdestructs if called
|
// The address 0xAAAAA selfdestructs if called
|
||||||
aa: {
|
aa: {
|
||||||
|
|
@ -3592,7 +3381,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) {
|
||||||
|
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
// The address 0xAAAAA selfdestructs if called
|
// The address 0xAAAAA selfdestructs if called
|
||||||
aa: {
|
aa: {
|
||||||
|
|
@ -3717,7 +3506,7 @@ func testDeleteRecreateSlotsAcrossManyBlocks(t *testing.T, scheme string) {
|
||||||
t.Logf("Destination address: %x\n", aa)
|
t.Logf("Destination address: %x\n", aa)
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
// The address 0xAAAAA selfdestructs if called
|
// The address 0xAAAAA selfdestructs if called
|
||||||
aa: {
|
aa: {
|
||||||
|
|
@ -3932,7 +3721,7 @@ func testInitThenFailCreateContract(t *testing.T, scheme string) {
|
||||||
|
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
// The address aa has some funds
|
// The address aa has some funds
|
||||||
aa: {Balance: big.NewInt(100000)},
|
aa: {Balance: big.NewInt(100000)},
|
||||||
|
|
@ -3966,7 +3755,7 @@ func testInitThenFailCreateContract(t *testing.T, scheme string) {
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
statedb, _ := chain.State()
|
statedb, _ := chain.State()
|
||||||
if got, exp := statedb.GetBalance(aa), big.NewInt(100000); got.Cmp(exp) != 0 {
|
if got, exp := statedb.GetBalance(aa), uint256.NewInt(100000); got.Cmp(exp) != 0 {
|
||||||
t.Fatalf("Genesis err, got %v exp %v", got, exp)
|
t.Fatalf("Genesis err, got %v exp %v", got, exp)
|
||||||
}
|
}
|
||||||
// First block tries to create, but fails
|
// First block tries to create, but fails
|
||||||
|
|
@ -3977,7 +3766,7 @@ func testInitThenFailCreateContract(t *testing.T, scheme string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
statedb, _ = chain.State()
|
statedb, _ = chain.State()
|
||||||
if got, exp := statedb.GetBalance(aa), big.NewInt(100000); got.Cmp(exp) != 0 {
|
if got, exp := statedb.GetBalance(aa), uint256.NewInt(100000); got.Cmp(exp) != 0 {
|
||||||
t.Fatalf("block %d: got %v exp %v", block.NumberU64(), got, exp)
|
t.Fatalf("block %d: got %v exp %v", block.NumberU64(), got, exp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4010,7 +3799,7 @@ func testEIP2718Transition(t *testing.T, scheme string) {
|
||||||
funds = big.NewInt(1000000000000000)
|
funds = big.NewInt(1000000000000000)
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
// The address 0xAAAA sloads 0x00 and 0x01
|
// The address 0xAAAA sloads 0x00 and 0x01
|
||||||
aa: {
|
aa: {
|
||||||
|
|
@ -4096,7 +3885,7 @@ func testEIP1559Transition(t *testing.T, scheme string) {
|
||||||
config = *params.AllEthashProtocolChanges
|
config = *params.AllEthashProtocolChanges
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: &config,
|
Config: &config,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
addr1: {Balance: funds},
|
addr1: {Balance: funds},
|
||||||
addr2: {Balance: funds},
|
addr2: {Balance: funds},
|
||||||
// The address 0xAAAA sloads 0x00 and 0x01
|
// The address 0xAAAA sloads 0x00 and 0x01
|
||||||
|
|
@ -4165,10 +3954,10 @@ func testEIP1559Transition(t *testing.T, scheme string) {
|
||||||
state, _ := chain.State()
|
state, _ := chain.State()
|
||||||
|
|
||||||
// 3: Ensure that miner received only the tx's tip.
|
// 3: Ensure that miner received only the tx's tip.
|
||||||
actual := state.GetBalance(block.Coinbase())
|
actual := state.GetBalance(block.Coinbase()).ToBig()
|
||||||
expected := new(big.Int).Add(
|
expected := new(big.Int).Add(
|
||||||
new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()),
|
new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()),
|
||||||
ethash.ConstantinopleBlockReward,
|
ethash.ConstantinopleBlockReward.ToBig(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if actual.Cmp(expected) != 0 {
|
if actual.Cmp(expected) != 0 {
|
||||||
|
|
@ -4176,7 +3965,7 @@ func testEIP1559Transition(t *testing.T, scheme string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
|
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
|
||||||
actual = new(big.Int).Sub(funds, state.GetBalance(addr1))
|
actual = new(big.Int).Sub(funds, state.GetBalance(addr1).ToBig())
|
||||||
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
|
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
|
||||||
|
|
||||||
if actual.Cmp(expected) != 0 {
|
if actual.Cmp(expected) != 0 {
|
||||||
|
|
@ -4207,10 +3996,10 @@ func testEIP1559Transition(t *testing.T, scheme string) {
|
||||||
effectiveTip := block.Transactions()[0].GasTipCap().Uint64() - block.BaseFee().Uint64()
|
effectiveTip := block.Transactions()[0].GasTipCap().Uint64() - block.BaseFee().Uint64()
|
||||||
|
|
||||||
// 6+5: Ensure that miner received only the tx's effective tip.
|
// 6+5: Ensure that miner received only the tx's effective tip.
|
||||||
actual = state.GetBalance(block.Coinbase())
|
actual = state.GetBalance(block.Coinbase()).ToBig()
|
||||||
expected = new(big.Int).Add(
|
expected = new(big.Int).Add(
|
||||||
new(big.Int).SetUint64(block.GasUsed()*effectiveTip),
|
new(big.Int).SetUint64(block.GasUsed()*effectiveTip),
|
||||||
ethash.ConstantinopleBlockReward,
|
ethash.ConstantinopleBlockReward.ToBig(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if actual.Cmp(expected) != 0 {
|
if actual.Cmp(expected) != 0 {
|
||||||
|
|
@ -4218,7 +4007,7 @@ func testEIP1559Transition(t *testing.T, scheme string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4: Ensure the tx sender paid for the gasUsed * (effectiveTip + block baseFee).
|
// 4: Ensure the tx sender paid for the gasUsed * (effectiveTip + block baseFee).
|
||||||
actual = new(big.Int).Sub(funds, state.GetBalance(addr2))
|
actual = new(big.Int).Sub(funds, state.GetBalance(addr2).ToBig())
|
||||||
expected = new(big.Int).SetUint64(block.GasUsed() * (effectiveTip + block.BaseFee().Uint64()))
|
expected = new(big.Int).SetUint64(block.GasUsed() * (effectiveTip + block.BaseFee().Uint64()))
|
||||||
|
|
||||||
if actual.Cmp(expected) != 0 {
|
if actual.Cmp(expected) != 0 {
|
||||||
|
|
@ -4242,7 +4031,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
||||||
funds = big.NewInt(100000000000000000)
|
funds = big.NewInt(100000000000000000)
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
}
|
}
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
|
|
@ -4373,7 +4162,7 @@ func testCanonicalHashMarker(t *testing.T, scheme string) {
|
||||||
var (
|
var (
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{},
|
Alloc: types.GenesisAlloc{},
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
}
|
}
|
||||||
engine = ethash.NewFaker()
|
engine = ethash.NewFaker()
|
||||||
|
|
@ -4447,225 +4236,6 @@ func testCanonicalHashMarker(t *testing.T, scheme string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTxIndexer tests the tx indexes are updated correctly.
|
|
||||||
func TestTxIndexer(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var (
|
|
||||||
testBankKey, _ = crypto.GenerateKey()
|
|
||||||
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
|
||||||
testBankFunds = big.NewInt(1000000000000000000)
|
|
||||||
|
|
||||||
gspec = &Genesis{
|
|
||||||
Config: params.TestChainConfig,
|
|
||||||
Alloc: GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
|
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
||||||
}
|
|
||||||
engine = ethash.NewFaker()
|
|
||||||
nonce = uint64(0)
|
|
||||||
)
|
|
||||||
|
|
||||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, engine, 128, func(i int, gen *BlockGen) {
|
|
||||||
tx, _ := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("0xdeadbeef"), big.NewInt(1000), params.TxGas, big.NewInt(10*params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey)
|
|
||||||
gen.AddTx(tx)
|
|
||||||
|
|
||||||
nonce += 1
|
|
||||||
})
|
|
||||||
|
|
||||||
// verifyIndexes checks if the transaction indexes are present or not
|
|
||||||
// of the specified block.
|
|
||||||
verifyIndexes := func(db ethdb.Database, number uint64, exist bool) {
|
|
||||||
if number == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
block := blocks[number-1]
|
|
||||||
|
|
||||||
for _, tx := range block.Transactions() {
|
|
||||||
lookup := rawdb.ReadTxLookupEntry(db, tx.Hash())
|
|
||||||
if exist && lookup == nil {
|
|
||||||
t.Fatalf("missing %d %x", number, tx.Hash().Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exist && lookup != nil {
|
|
||||||
t.Fatalf("unexpected %d %x", number, tx.Hash().Hex())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// verifyRange runs verifyIndexes for a range of blocks, from and to are included.
|
|
||||||
verifyRange := func(db ethdb.Database, from, to uint64, exist bool) {
|
|
||||||
for number := from; number <= to; number += 1 {
|
|
||||||
verifyIndexes(db, number, exist)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verify := func(db ethdb.Database, expTail uint64) {
|
|
||||||
tail := rawdb.ReadTxIndexTail(db)
|
|
||||||
if tail == nil {
|
|
||||||
t.Fatal("Failed to write tx index tail")
|
|
||||||
}
|
|
||||||
|
|
||||||
if *tail != expTail {
|
|
||||||
t.Fatalf("Unexpected tx index tail, want %v, got %d", expTail, *tail)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *tail != 0 {
|
|
||||||
verifyRange(db, 0, *tail-1, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyRange(db, *tail, 128, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
var cases = []struct {
|
|
||||||
limitA uint64
|
|
||||||
tailA uint64
|
|
||||||
limitB uint64
|
|
||||||
tailB uint64
|
|
||||||
limitC uint64
|
|
||||||
tailC uint64
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
// LimitA: 0
|
|
||||||
// TailA: 0
|
|
||||||
//
|
|
||||||
// all blocks are indexed
|
|
||||||
limitA: 0,
|
|
||||||
tailA: 0,
|
|
||||||
|
|
||||||
// LimitB: 1
|
|
||||||
// TailB: 128
|
|
||||||
//
|
|
||||||
// block-128 is indexed
|
|
||||||
limitB: 1,
|
|
||||||
tailB: 128,
|
|
||||||
|
|
||||||
// LimitB: 64
|
|
||||||
// TailB: 65
|
|
||||||
//
|
|
||||||
// block [65, 128] are indexed
|
|
||||||
limitC: 64,
|
|
||||||
tailC: 65,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// LimitA: 64
|
|
||||||
// TailA: 65
|
|
||||||
//
|
|
||||||
// block [65, 128] are indexed
|
|
||||||
limitA: 64,
|
|
||||||
tailA: 65,
|
|
||||||
|
|
||||||
// LimitB: 1
|
|
||||||
// TailB: 128
|
|
||||||
//
|
|
||||||
// block-128 is indexed
|
|
||||||
limitB: 1,
|
|
||||||
tailB: 128,
|
|
||||||
|
|
||||||
// LimitB: 64
|
|
||||||
// TailB: 65
|
|
||||||
//
|
|
||||||
// block [65, 128] are indexed
|
|
||||||
limitC: 64,
|
|
||||||
tailC: 65,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// LimitA: 127
|
|
||||||
// TailA: 2
|
|
||||||
//
|
|
||||||
// block [2, 128] are indexed
|
|
||||||
limitA: 127,
|
|
||||||
tailA: 2,
|
|
||||||
|
|
||||||
// LimitB: 1
|
|
||||||
// TailB: 128
|
|
||||||
//
|
|
||||||
// block-128 is indexed
|
|
||||||
limitB: 1,
|
|
||||||
tailB: 128,
|
|
||||||
|
|
||||||
// LimitB: 64
|
|
||||||
// TailB: 65
|
|
||||||
//
|
|
||||||
// block [65, 128] are indexed
|
|
||||||
limitC: 64,
|
|
||||||
tailC: 65,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// LimitA: 128
|
|
||||||
// TailA: 1
|
|
||||||
//
|
|
||||||
// block [2, 128] are indexed
|
|
||||||
limitA: 128,
|
|
||||||
tailA: 1,
|
|
||||||
|
|
||||||
// LimitB: 1
|
|
||||||
// TailB: 128
|
|
||||||
//
|
|
||||||
// block-128 is indexed
|
|
||||||
limitB: 1,
|
|
||||||
tailB: 128,
|
|
||||||
|
|
||||||
// LimitB: 64
|
|
||||||
// TailB: 65
|
|
||||||
//
|
|
||||||
// block [65, 128] are indexed
|
|
||||||
limitC: 64,
|
|
||||||
tailC: 65,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// LimitA: 129
|
|
||||||
// TailA: 0
|
|
||||||
//
|
|
||||||
// block [0, 128] are indexed
|
|
||||||
limitA: 129,
|
|
||||||
tailA: 0,
|
|
||||||
|
|
||||||
// LimitB: 1
|
|
||||||
// TailB: 128
|
|
||||||
//
|
|
||||||
// block-128 is indexed
|
|
||||||
limitB: 1,
|
|
||||||
tailB: 128,
|
|
||||||
|
|
||||||
// LimitB: 64
|
|
||||||
// TailB: 65
|
|
||||||
//
|
|
||||||
// block [65, 128] are indexed
|
|
||||||
limitC: 64,
|
|
||||||
tailC: 65,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
borReceipts := make([]types.Receipts, len(receipts))
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
frdir := t.TempDir()
|
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false)
|
|
||||||
_, _ = rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), append([]types.Receipts{{}}, borReceipts...), big.NewInt(0))
|
|
||||||
|
|
||||||
// Index the initial blocks from ancient store
|
|
||||||
chain, _ := NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, &c.limitA, nil)
|
|
||||||
chain.indexBlocks(nil, 128, make(chan struct{}))
|
|
||||||
verify(db, c.tailA)
|
|
||||||
|
|
||||||
chain.SetTxLookupLimit(c.limitB)
|
|
||||||
chain.indexBlocks(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}))
|
|
||||||
verify(db, c.tailB)
|
|
||||||
|
|
||||||
chain.SetTxLookupLimit(c.limitC)
|
|
||||||
chain.indexBlocks(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}))
|
|
||||||
verify(db, c.tailC)
|
|
||||||
|
|
||||||
// Recover all indexes
|
|
||||||
chain.SetTxLookupLimit(0)
|
|
||||||
chain.indexBlocks(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}))
|
|
||||||
verify(db, 0)
|
|
||||||
|
|
||||||
chain.Stop()
|
|
||||||
db.Close()
|
|
||||||
os.RemoveAll(frdir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateThenDeletePreByzantium(t *testing.T) {
|
func TestCreateThenDeletePreByzantium(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
@ -4719,7 +4289,7 @@ func testCreateThenDelete(t *testing.T, config *params.ChainConfig) {
|
||||||
}...)
|
}...)
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: config,
|
Config: config,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -4809,7 +4379,7 @@ func TestDeleteThenCreate(t *testing.T) {
|
||||||
|
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -4924,7 +4494,7 @@ func TestTransientStorageReset(t *testing.T) {
|
||||||
}...)
|
}...)
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -5000,7 +4570,7 @@ func TestEIP3651(t *testing.T) {
|
||||||
config = *params.AllEthashProtocolChanges
|
config = *params.AllEthashProtocolChanges
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: &config,
|
Config: &config,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
addr1: {Balance: funds},
|
addr1: {Balance: funds},
|
||||||
addr2: {Balance: funds},
|
addr2: {Balance: funds},
|
||||||
// The address 0xAAAA sloads 0x00 and 0x01
|
// The address 0xAAAA sloads 0x00 and 0x01
|
||||||
|
|
@ -5085,7 +4655,7 @@ func TestEIP3651(t *testing.T) {
|
||||||
state, _ := chain.State()
|
state, _ := chain.State()
|
||||||
|
|
||||||
// 3: Ensure that miner received only the tx's tip.
|
// 3: Ensure that miner received only the tx's tip.
|
||||||
actual := state.GetBalance(block.Coinbase())
|
actual := state.GetBalance(block.Coinbase()).ToBig()
|
||||||
expected := new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64())
|
expected := new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64())
|
||||||
|
|
||||||
if actual.Cmp(expected) != 0 {
|
if actual.Cmp(expected) != 0 {
|
||||||
|
|
@ -5093,7 +4663,7 @@ func TestEIP3651(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
|
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
|
||||||
actual = new(big.Int).Sub(funds, state.GetBalance(addr1))
|
actual = new(big.Int).Sub(funds, state.GetBalance(addr1).ToBig())
|
||||||
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
|
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
|
||||||
|
|
||||||
if actual.Cmp(expected) != 0 {
|
if actual.Cmp(expected) != 0 {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BlockGen creates blocks for testing.
|
// BlockGen creates blocks for testing.
|
||||||
|
|
@ -85,7 +86,7 @@ func (b *BlockGen) SetDifficulty(diff *big.Int) {
|
||||||
b.header.Difficulty = diff
|
b.header.Difficulty = diff
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPos makes the header a PoS-header (0 difficulty)
|
// SetPoS makes the header a PoS-header (0 difficulty)
|
||||||
func (b *BlockGen) SetPoS() {
|
func (b *BlockGen) SetPoS() {
|
||||||
b.header.Difficulty = new(big.Int)
|
b.header.Difficulty = new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +163,7 @@ func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBalance returns the balance of the given address at the generated block.
|
// GetBalance returns the balance of the given address at the generated block.
|
||||||
func (b *BlockGen) GetBalance(addr common.Address) *big.Int {
|
func (b *BlockGen) GetBalance(addr common.Address) *uint256.Int {
|
||||||
return b.statedb.GetBalance(addr)
|
return b.statedb.GetBalance(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,7 +325,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
}
|
}
|
||||||
cm := newChainMaker(parent, config, engine)
|
cm := newChainMaker(parent, config, engine)
|
||||||
|
|
||||||
genblock := func(i int, parent *types.Block, triedb *trie.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||||
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
|
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
|
||||||
b.header = cm.makeHeader(parent, statedb, b.engine)
|
b.header = cm.makeHeader(parent, statedb, b.engine)
|
||||||
|
|
||||||
|
|
@ -375,7 +376,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forcibly use hash-based state scheme for retaining all nodes in disk.
|
// Forcibly use hash-based state scheme for retaining all nodes in disk.
|
||||||
triedb := trie.NewDatabase(db, trie.HashDefaults)
|
triedb := triedb.NewDatabase(db, triedb.HashDefaults)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
|
|
@ -420,7 +421,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
// then generate chain on top.
|
// then generate chain on top.
|
||||||
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) {
|
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
triedb := trie.NewDatabase(db, trie.HashDefaults)
|
triedb := triedb.NewDatabase(db, triedb.HashDefaults)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
_, err := genesis.Commit(db, triedb)
|
_, err := genesis.Commit(db, triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGeneratePOSChain(t *testing.T) {
|
func TestGeneratePOSChain(t *testing.T) {
|
||||||
|
|
@ -46,7 +46,7 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||||
asm4788 = common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500")
|
asm4788 = common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500")
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: &config,
|
Config: &config,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
address: {Balance: funds},
|
address: {Balance: funds},
|
||||||
params.BeaconRootsStorageAddress: {Balance: common.Big0, Code: asm4788},
|
params.BeaconRootsStorageAddress: {Balance: common.Big0, Code: asm4788},
|
||||||
},
|
},
|
||||||
|
|
@ -71,19 +71,19 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||||
storage[common.Hash{0x01}] = common.Hash{0x01}
|
storage[common.Hash{0x01}] = common.Hash{0x01}
|
||||||
storage[common.Hash{0x02}] = common.Hash{0x02}
|
storage[common.Hash{0x02}] = common.Hash{0x02}
|
||||||
storage[common.Hash{0x03}] = common.HexToHash("0303")
|
storage[common.Hash{0x03}] = common.HexToHash("0303")
|
||||||
gspec.Alloc[aa] = GenesisAccount{
|
gspec.Alloc[aa] = types.Account{
|
||||||
Balance: common.Big1,
|
Balance: common.Big1,
|
||||||
Nonce: 1,
|
Nonce: 1,
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
Code: common.Hex2Bytes("6042"),
|
Code: common.Hex2Bytes("6042"),
|
||||||
}
|
}
|
||||||
gspec.Alloc[bb] = GenesisAccount{
|
gspec.Alloc[bb] = types.Account{
|
||||||
Balance: common.Big2,
|
Balance: common.Big2,
|
||||||
Nonce: 1,
|
Nonce: 1,
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
Code: common.Hex2Bytes("600154600354"),
|
Code: common.Hex2Bytes("600154600354"),
|
||||||
}
|
}
|
||||||
genesis := gspec.MustCommit(gendb, trie.NewDatabase(gendb, trie.HashDefaults))
|
genesis := gspec.MustCommit(gendb, triedb.NewDatabase(gendb, triedb.HashDefaults))
|
||||||
|
|
||||||
genchain, genreceipts := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
|
genchain, genreceipts := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
|
||||||
gen.SetParentBeaconRoot(common.Hash{byte(i + 1)})
|
gen.SetParentBeaconRoot(common.Hash{byte(i + 1)})
|
||||||
|
|
@ -208,9 +208,9 @@ func ExampleGenerateChain() {
|
||||||
// Ensure that key1 has some funds in the genesis block.
|
// Ensure that key1 has some funds in the genesis block.
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
|
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
|
||||||
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
|
Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
|
||||||
}
|
}
|
||||||
genesis := gspec.MustCommit(genDb, trie.NewDatabase(genDb, trie.HashDefaults))
|
genesis := gspec.MustCommit(genDb, triedb.NewDatabase(genDb, triedb.HashDefaults))
|
||||||
|
|
||||||
// This call generates a chain of 5 blocks. The function runs for
|
// This call generates a chain of 5 blocks. The function runs for
|
||||||
// each block and adds different features to gen based on the
|
// each block and adds different features to gen based on the
|
||||||
|
|
|
||||||
|
|
@ -104,4 +104,10 @@ var (
|
||||||
// ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the
|
// ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the
|
||||||
// blob gas fee of the block.
|
// blob gas fee of the block.
|
||||||
ErrBlobFeeCapTooLow = errors.New("max fee per blob gas less than block blob gas fee")
|
ErrBlobFeeCapTooLow = errors.New("max fee per blob gas less than block blob gas fee")
|
||||||
|
|
||||||
|
// ErrMissingBlobHashes is returned if a blob transaction has no blob hashes.
|
||||||
|
ErrMissingBlobHashes = errors.New("blob transaction missing blob hashes")
|
||||||
|
|
||||||
|
// ErrBlobTxCreate is returned if a blob transaction has no explicit to field.
|
||||||
|
ErrBlobTxCreate = errors.New("blob transaction of type create")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChainContext supports retrieving headers and consensus parameters from the
|
// ChainContext supports retrieving headers and consensus parameters from the
|
||||||
|
|
@ -142,12 +143,12 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash
|
||||||
|
|
||||||
// CanTransfer checks whether there are enough funds in the address' account to make a transfer.
|
// CanTransfer checks whether there are enough funds in the address' account to make a transfer.
|
||||||
// This does not take the necessary gas in to account to make the transfer valid.
|
// This does not take the necessary gas in to account to make the transfer valid.
|
||||||
func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
|
func CanTransfer(db vm.StateDB, addr common.Address, amount *uint256.Int) bool {
|
||||||
return db.GetBalance(addr).Cmp(amount) >= 0
|
return db.GetBalance(addr).Cmp(amount) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
|
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
|
||||||
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
|
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) {
|
||||||
// get inputs before
|
// get inputs before
|
||||||
input1 := db.GetBalance(sender)
|
input1 := db.GetBalance(sender)
|
||||||
input2 := db.GetBalance(recipient)
|
input2 := db.GetBalance(recipient)
|
||||||
|
|
@ -160,5 +161,5 @@ func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int)
|
||||||
output2 := db.GetBalance(recipient)
|
output2 := db.GetBalance(recipient)
|
||||||
|
|
||||||
// add transfer log
|
// add transfer log
|
||||||
AddTransferLog(db, sender, recipient, amount, input1, input2, output1, output2)
|
AddTransferLog(db, sender, recipient, amount.ToBig(), input1.ToBig(), input2.ToBig(), output1.ToBig(), output2.ToBig())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
@ -94,7 +94,7 @@ func TestPastChainInsert(t *testing.T) {
|
||||||
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
||||||
)
|
)
|
||||||
|
|
||||||
_, _ = gspec.Commit(db, trie.NewDatabase(db, trie.HashDefaults))
|
_, _ = gspec.Commit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
|
|
||||||
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -166,7 +166,7 @@ func TestFutureChainInsert(t *testing.T) {
|
||||||
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
||||||
)
|
)
|
||||||
|
|
||||||
_, _ = gspec.Commit(db, trie.NewDatabase(db, trie.HashDefaults))
|
_, _ = gspec.Commit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
|
|
||||||
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -226,7 +226,7 @@ func TestOverlappingChainInsert(t *testing.T) {
|
||||||
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
||||||
)
|
)
|
||||||
|
|
||||||
_, _ = gspec.Commit(db, trie.NewDatabase(db, trie.HashDefaults))
|
_, _ = gspec.Commit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
|
|
||||||
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -49,30 +49,36 @@ func TestCreation(t *testing.T) {
|
||||||
params.MainnetChainConfig,
|
params.MainnetChainConfig,
|
||||||
core.DefaultGenesisBlock().ToBlock(),
|
core.DefaultGenesisBlock().ToBlock(),
|
||||||
[]testcase{
|
[]testcase{
|
||||||
{0, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced
|
{0, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Unsynced
|
||||||
{1149999, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block
|
{1149999, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}}, // Last Frontier block
|
||||||
{1150000, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // First Homestead block
|
{1150000, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // First Homestead block
|
||||||
{1919999, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // Last Homestead block
|
{1919999, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}}, // Last Homestead block
|
||||||
{1920000, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // First DAO block
|
{1920000, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // First DAO block
|
||||||
{2462999, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // Last DAO block
|
{2462999, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}}, // Last DAO block
|
||||||
{2463000, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // First Tangerine block
|
{2463000, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // First Tangerine block
|
||||||
{2674999, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // Last Tangerine block
|
{2674999, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}}, // Last Tangerine block
|
||||||
{2675000, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // First Spurious block
|
{2675000, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // First Spurious block
|
||||||
{4369999, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // Last Spurious block
|
{4369999, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}}, // Last Spurious block
|
||||||
{4370000, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // First Byzantium block
|
{4370000, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // First Byzantium block
|
||||||
{7279999, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block
|
{7279999, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block
|
||||||
{7280000, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block
|
{7280000, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block
|
||||||
{9068999, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block
|
{9068999, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block
|
||||||
{9069000, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block
|
{9069000, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block
|
||||||
{9199999, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block
|
{9199999, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block
|
||||||
{9200000, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block
|
{9200000, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block
|
||||||
{12243999, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block
|
{12243999, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block
|
||||||
{12244000, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block
|
{12244000, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block
|
||||||
{12964999, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block
|
{12964999, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block
|
||||||
{12965000, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // First London block
|
{12965000, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // First London block
|
||||||
{13772999, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // Last London block
|
{13772999, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // Last London block
|
||||||
{13773000, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // First Arrow Glacier block
|
{13773000, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // First Arrow Glacier block
|
||||||
{15049999, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // Last Arrow Glacier block
|
{15049999, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // Last Arrow Glacier block
|
||||||
|
{15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1681338455}}, // First Gray Glacier block
|
||||||
|
{20000000, 1681338454, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1681338455}}, // Last Gray Glacier block
|
||||||
|
{20000000, 1681338455, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}}, // First Shanghai block
|
||||||
|
{30000000, 1710338134, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}}, // Last Shanghai block
|
||||||
|
{40000000, 1710338135, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // First Cancun block
|
||||||
|
{50000000, 2000000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // Future Cancun block
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Goerli test cases
|
// Goerli test cases
|
||||||
|
|
@ -80,12 +86,18 @@ func TestCreation(t *testing.T) {
|
||||||
params.GoerliChainConfig,
|
params.GoerliChainConfig,
|
||||||
core.DefaultGoerliGenesisBlock().ToBlock(),
|
core.DefaultGoerliGenesisBlock().ToBlock(),
|
||||||
[]testcase{
|
[]testcase{
|
||||||
{0, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block
|
{0, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block
|
||||||
{1561650, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block
|
{1561650, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block
|
||||||
{1561651, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // First Istanbul block
|
{1561651, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // First Istanbul block
|
||||||
{4460643, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // Last Istanbul block
|
{4460643, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // Last Istanbul block
|
||||||
{4460644, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // First Berlin block
|
{4460644, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // First Berlin block
|
||||||
{5000000, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // Last Berlin block
|
{5000000, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // Last Berlin block
|
||||||
|
{5062605, 0, ID{Hash: checksumToBytes(0xB8C6299D), Next: 1678832736}}, // First London block
|
||||||
|
{6000000, 1678832735, ID{Hash: checksumToBytes(0xB8C6299D), Next: 1678832736}}, // Last London block
|
||||||
|
{6000001, 1678832736, ID{Hash: checksumToBytes(0xf9843abf), Next: 1705473120}}, // First Shanghai block
|
||||||
|
{6500002, 1705473119, ID{Hash: checksumToBytes(0xf9843abf), Next: 1705473120}}, // Last Shanghai block
|
||||||
|
{6500003, 1705473120, ID{Hash: checksumToBytes(0x70cc14e2), Next: 0}}, // First Cancun block
|
||||||
|
{6500003, 2705473120, ID{Hash: checksumToBytes(0x70cc14e2), Next: 0}}, // Future Cancun block
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Sepolia test cases
|
// Sepolia test cases
|
||||||
|
|
@ -93,8 +105,27 @@ func TestCreation(t *testing.T) {
|
||||||
params.SepoliaChainConfig,
|
params.SepoliaChainConfig,
|
||||||
core.DefaultSepoliaGenesisBlock().ToBlock(),
|
core.DefaultSepoliaGenesisBlock().ToBlock(),
|
||||||
[]testcase{
|
[]testcase{
|
||||||
{0, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin and first London block
|
{0, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin and first London block
|
||||||
{1735370, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Last London block
|
{1735370, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Last London block
|
||||||
|
{1735371, 0, ID{Hash: checksumToBytes(0xb96cbd13), Next: 1677557088}}, // First MergeNetsplit block
|
||||||
|
{1735372, 1677557087, ID{Hash: checksumToBytes(0xb96cbd13), Next: 1677557088}}, // Last MergeNetsplit block
|
||||||
|
{1735372, 1677557088, ID{Hash: checksumToBytes(0xf7f9bc08), Next: 1706655072}}, // First Shanghai block
|
||||||
|
{1735372, 1706655071, ID{Hash: checksumToBytes(0xf7f9bc08), Next: 1706655072}}, // Last Shanghai block
|
||||||
|
{1735372, 1706655072, ID{Hash: checksumToBytes(0x88cf81d9), Next: 0}}, // First Cancun block
|
||||||
|
{1735372, 2706655072, ID{Hash: checksumToBytes(0x88cf81d9), Next: 0}}, // Future Cancun block
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Holesky test cases
|
||||||
|
{
|
||||||
|
params.HoleskyChainConfig,
|
||||||
|
core.DefaultHoleskyGenesisBlock().ToBlock(),
|
||||||
|
[]testcase{
|
||||||
|
{0, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris block
|
||||||
|
{123, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // First MergeNetsplit block
|
||||||
|
{123, 1696000704, ID{Hash: checksumToBytes(0xfd4f016b), Next: 1707305664}}, // First Shanghai block
|
||||||
|
{123, 1707305663, ID{Hash: checksumToBytes(0xfd4f016b), Next: 1707305664}}, // Last Shanghai block
|
||||||
|
{123, 1707305664, ID{Hash: checksumToBytes(0x9b192ad0), Next: 0}}, // First Cancun block
|
||||||
|
{123, 2707305664, ID{Hash: checksumToBytes(0x9b192ad0), Next: 0}}, // Future Cancun block
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -188,14 +219,10 @@ func TestValidation(t *testing.T) {
|
||||||
// at some future block 88888888, for itself, but past block for local. Local is incompatible.
|
// at some future block 88888888, for itself, but past block for local. Local is incompatible.
|
||||||
//
|
//
|
||||||
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
|
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
|
||||||
//
|
|
||||||
// TODO(karalabe): This testcase will fail once mainnet gets timestamped forks, make legacy chain config
|
|
||||||
{&legacyConfig, 88888888, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 88888888}, ErrLocalIncompatibleOrStale},
|
{&legacyConfig, 88888888, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 88888888}, ErrLocalIncompatibleOrStale},
|
||||||
|
|
||||||
// Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing
|
// Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing
|
||||||
// fork) at block 7279999, before Petersburg. Local is incompatible.
|
// fork) at block 7279999, before Petersburg. Local is incompatible.
|
||||||
//
|
|
||||||
// TODO(karalabe): This testcase will fail once mainnet gets timestamped forks, make legacy chain config
|
|
||||||
{&legacyConfig, 7279999, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale},
|
{&legacyConfig, 7279999, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale},
|
||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
|
|
@ -250,34 +277,25 @@ func TestValidation(t *testing.T) {
|
||||||
// Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces
|
// Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces
|
||||||
// also Shanghai, but it's not yet aware of Cancun (e.g. non updated node before the fork).
|
// also Shanghai, but it's not yet aware of Cancun (e.g. non updated node before the fork).
|
||||||
// In this case we don't know if Cancun passed yet or not.
|
// In this case we don't know if Cancun passed yet or not.
|
||||||
//
|
{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0xdce96c2d), Next: 0}, nil},
|
||||||
// TODO(karalabe): Enable this when Cancun is specced
|
|
||||||
//{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: 0}, nil},
|
|
||||||
|
|
||||||
// Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces
|
// Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces
|
||||||
// also Shanghai, and it's also aware of Cancun (e.g. updated node before the fork). We
|
// also Shanghai, and it's also aware of Cancun (e.g. updated node before the fork). We
|
||||||
// don't know if Cancun passed yet (will pass) or not.
|
// don't know if Cancun passed yet (will pass) or not.
|
||||||
//
|
{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}, nil},
|
||||||
// TODO(karalabe): Enable this when Cancun is specced and update next timestamp
|
|
||||||
//{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, nil},
|
|
||||||
|
|
||||||
// Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces
|
// Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces
|
||||||
// also Shanghai, and it's also aware of some random fork (e.g. misconfigured Cancun). As
|
// also Shanghai, and it's also aware of some random fork (e.g. misconfigured Cancun). As
|
||||||
// neither forks passed at neither nodes, they may mismatch, but we still connect for now.
|
// neither forks passed at neither nodes, they may mismatch, but we still connect for now.
|
||||||
//
|
{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0xdce96c2d), Next: math.MaxUint64}, nil},
|
||||||
// TODO(karalabe): Enable this when Cancun is specced
|
|
||||||
//{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: math.MaxUint64}, nil},
|
|
||||||
|
|
||||||
// Local is mainnet exactly on Cancun, remote announces Shanghai + knowledge about Cancun. Remote
|
// Local is mainnet exactly on Cancun, remote announces Shanghai + knowledge about Cancun. Remote
|
||||||
// is simply out of sync, accept.
|
// is simply out of sync, accept.
|
||||||
//
|
{params.MainnetChainConfig, 21000000, 1710338135, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}, nil},
|
||||||
// TODO(karalabe): Enable this when Cancun is specced, update local head and time, next timestamp
|
|
||||||
// {params.MainnetChainConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, nil},
|
|
||||||
|
|
||||||
// Local is mainnet Cancun, remote announces Shanghai + knowledge about Cancun. Remote
|
// Local is mainnet Cancun, remote announces Shanghai + knowledge about Cancun. Remote
|
||||||
// is simply out of sync, accept.
|
// is simply out of sync, accept.
|
||||||
// TODO(karalabe): Enable this when Cancun is specced, update local head and time, next timestamp
|
{params.MainnetChainConfig, 21123456, 1710338136, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}, nil},
|
||||||
//{params.MainnetChainConfig, 21123456, 1678123456, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, nil},
|
|
||||||
|
|
||||||
// Local is mainnet Prague, remote announces Shanghai + knowledge about Cancun. Remote
|
// Local is mainnet Prague, remote announces Shanghai + knowledge about Cancun. Remote
|
||||||
// is definitely out of sync. It may or may not need the Prague update, we don't know yet.
|
// is definitely out of sync. It may or may not need the Prague update, we don't know yet.
|
||||||
|
|
@ -286,9 +304,7 @@ func TestValidation(t *testing.T) {
|
||||||
//{params.MainnetChainConfig, 0, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil},
|
//{params.MainnetChainConfig, 0, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil},
|
||||||
|
|
||||||
// Local is mainnet Shanghai, remote announces Cancun. Local is out of sync, accept.
|
// Local is mainnet Shanghai, remote announces Cancun. Local is out of sync, accept.
|
||||||
//
|
{params.MainnetChainConfig, 21000000, 1700000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}, nil},
|
||||||
// TODO(karalabe): Enable this when Cancun is specced, update remote checksum
|
|
||||||
//{params.MainnetChainConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x00000000), Next: 0}, nil},
|
|
||||||
|
|
||||||
// Local is mainnet Shanghai, remote announces Cancun, but is not aware of Prague. Local
|
// Local is mainnet Shanghai, remote announces Cancun, but is not aware of Prague. Local
|
||||||
// out of sync. Local also knows about a future fork, but that is uncertain yet.
|
// out of sync. Local also knows about a future fork, but that is uncertain yet.
|
||||||
|
|
@ -298,9 +314,7 @@ func TestValidation(t *testing.T) {
|
||||||
|
|
||||||
// Local is mainnet Cancun. remote announces Shanghai but is not aware of further forks.
|
// Local is mainnet Cancun. remote announces Shanghai but is not aware of further forks.
|
||||||
// Remote needs software update.
|
// Remote needs software update.
|
||||||
//
|
{params.MainnetChainConfig, 21000000, 1710338135, ID{Hash: checksumToBytes(0xdce96c2d), Next: 0}, ErrRemoteStale},
|
||||||
// TODO(karalabe): Enable this when Cancun is specced, update local head and time
|
|
||||||
//{params.MainnetChainConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x71147644), Next: 0}, ErrRemoteStale},
|
|
||||||
|
|
||||||
// Local is mainnet Shanghai, and isn't aware of more forks. Remote announces Shanghai +
|
// Local is mainnet Shanghai, and isn't aware of more forks. Remote announces Shanghai +
|
||||||
// 0xffffffff. Local needs software update, reject.
|
// 0xffffffff. Local needs software update, reject.
|
||||||
|
|
@ -308,24 +322,20 @@ func TestValidation(t *testing.T) {
|
||||||
|
|
||||||
// Local is mainnet Shanghai, and is aware of Cancun. Remote announces Cancun +
|
// Local is mainnet Shanghai, and is aware of Cancun. Remote announces Cancun +
|
||||||
// 0xffffffff. Local needs software update, reject.
|
// 0xffffffff. Local needs software update, reject.
|
||||||
//
|
{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(checksumUpdate(0x9f3d2254, math.MaxUint64)), Next: 0}, ErrLocalIncompatibleOrStale},
|
||||||
// TODO(karalabe): Enable this when Cancun is specced, update remote checksum
|
|
||||||
//{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(checksumUpdate(0x00000000, math.MaxUint64)), Next: 0}, ErrLocalIncompatibleOrStale},
|
|
||||||
|
|
||||||
// Local is mainnet Shanghai, remote is random Shanghai.
|
// Local is mainnet Shanghai, remote is random Shanghai.
|
||||||
{params.MainnetChainConfig, 20000000, 1681338455, ID{Hash: checksumToBytes(0x12345678), Next: 0}, ErrLocalIncompatibleOrStale},
|
{params.MainnetChainConfig, 20000000, 1681338455, ID{Hash: checksumToBytes(0x12345678), Next: 0}, ErrLocalIncompatibleOrStale},
|
||||||
|
|
||||||
// Local is mainnet Shanghai, far in the future. Remote announces Gopherium (non existing fork)
|
// Local is mainnet Cancun, far in the future. Remote announces Gopherium (non existing fork)
|
||||||
// at some future timestamp 8888888888, for itself, but past block for local. Local is incompatible.
|
// at some future timestamp 8888888888, for itself, but past block for local. Local is incompatible.
|
||||||
//
|
//
|
||||||
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
|
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
|
||||||
{params.MainnetChainConfig, 88888888, 8888888888, ID{Hash: checksumToBytes(0xdce96c2d), Next: 8888888888}, ErrLocalIncompatibleOrStale},
|
{params.MainnetChainConfig, 88888888, 8888888888, ID{Hash: checksumToBytes(0x9f3d2254), Next: 8888888888}, ErrLocalIncompatibleOrStale},
|
||||||
|
|
||||||
// Local is mainnet Shanghai. Remote is also in Shanghai, but announces Gopherium (non existing
|
// Local is mainnet Shanghai. Remote is also in Shanghai, but announces Gopherium (non existing
|
||||||
// fork) at timestamp 1668000000, before Cancun. Local is incompatible.
|
// fork) at timestamp 1668000000, before Cancun. Local is incompatible.
|
||||||
//
|
{params.MainnetChainConfig, 20999999, 1699999999, ID{Hash: checksumToBytes(0x71147644), Next: 1700000000}, ErrLocalIncompatibleOrStale},
|
||||||
// TODO(karalabe): Enable this when Cancun is specced
|
|
||||||
//{params.MainnetChainConfig, 20999999, 1677999999, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, ErrLocalIncompatibleOrStale},
|
|
||||||
}
|
}
|
||||||
genesis := core.DefaultGenesisBlock().ToBlock()
|
genesis := core.DefaultGenesisBlock().ToBlock()
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -18,21 +19,21 @@ var _ = (*genesisSpecMarshaling)(nil)
|
||||||
// MarshalJSON marshals as JSON.
|
// MarshalJSON marshals as JSON.
|
||||||
func (g Genesis) MarshalJSON() ([]byte, error) {
|
func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||||
type Genesis struct {
|
type Genesis struct {
|
||||||
Config *params.ChainConfig `json:"config"`
|
Config *params.ChainConfig `json:"config"`
|
||||||
Nonce math.HexOrDecimal64 `json:"nonce"`
|
Nonce math.HexOrDecimal64 `json:"nonce"`
|
||||||
Timestamp math.HexOrDecimal64 `json:"timestamp"`
|
Timestamp math.HexOrDecimal64 `json:"timestamp"`
|
||||||
ExtraData hexutil.Bytes `json:"extraData"`
|
ExtraData hexutil.Bytes `json:"extraData"`
|
||||||
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||||
Mixhash common.Hash `json:"mixHash"`
|
Mixhash common.Hash `json:"mixHash"`
|
||||||
Coinbase common.Address `json:"coinbase"`
|
Coinbase common.Address `json:"coinbase"`
|
||||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
|
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
|
||||||
Number math.HexOrDecimal64 `json:"number"`
|
Number math.HexOrDecimal64 `json:"number"`
|
||||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
ParentHash common.Hash `json:"parentHash"`
|
ParentHash common.Hash `json:"parentHash"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var enc Genesis
|
var enc Genesis
|
||||||
|
|
@ -46,7 +47,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||||
enc.Coinbase = g.Coinbase
|
enc.Coinbase = g.Coinbase
|
||||||
|
|
||||||
if g.Alloc != nil {
|
if g.Alloc != nil {
|
||||||
enc.Alloc = make(map[common.UnprefixedAddress]GenesisAccount, len(g.Alloc))
|
enc.Alloc = make(map[common.UnprefixedAddress]types.Account, len(g.Alloc))
|
||||||
for k, v := range g.Alloc {
|
for k, v := range g.Alloc {
|
||||||
enc.Alloc[common.UnprefixedAddress(k)] = v
|
enc.Alloc[common.UnprefixedAddress(k)] = v
|
||||||
}
|
}
|
||||||
|
|
@ -64,21 +65,21 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (g *Genesis) UnmarshalJSON(input []byte) error {
|
func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||||
type Genesis struct {
|
type Genesis struct {
|
||||||
Config *params.ChainConfig `json:"config"`
|
Config *params.ChainConfig `json:"config"`
|
||||||
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||||
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
||||||
ExtraData *hexutil.Bytes `json:"extraData"`
|
ExtraData *hexutil.Bytes `json:"extraData"`
|
||||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||||
Mixhash *common.Hash `json:"mixHash"`
|
Mixhash *common.Hash `json:"mixHash"`
|
||||||
Coinbase *common.Address `json:"coinbase"`
|
Coinbase *common.Address `json:"coinbase"`
|
||||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
|
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
|
||||||
Number *math.HexOrDecimal64 `json:"number"`
|
Number *math.HexOrDecimal64 `json:"number"`
|
||||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
ParentHash *common.Hash `json:"parentHash"`
|
ParentHash *common.Hash `json:"parentHash"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var dec Genesis
|
var dec Genesis
|
||||||
|
|
@ -125,7 +126,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||||
return errors.New("missing required field 'alloc' for Genesis")
|
return errors.New("missing required field 'alloc' for Genesis")
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Alloc = make(GenesisAlloc, len(dec.Alloc))
|
g.Alloc = make(types.GenesisAlloc, len(dec.Alloc))
|
||||||
for k, v := range dec.Alloc {
|
for k, v := range dec.Alloc {
|
||||||
g.Alloc[common.Address(k)] = v
|
g.Alloc[common.Address(k)] = v
|
||||||
}
|
}
|
||||||
|
|
|
||||||
129
core/genesis.go
129
core/genesis.go
|
|
@ -19,7 +19,6 @@ package core
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -38,17 +37,24 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
|
//go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
|
||||||
//go:generate go run github.com/fjl/gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go
|
|
||||||
|
|
||||||
//go:embed allocs
|
//go:embed allocs
|
||||||
var allocs embed.FS
|
var allocs embed.FS
|
||||||
|
|
||||||
var errGenesisNoConfig = errors.New("genesis has no chain configuration")
|
var errGenesisNoConfig = errors.New("genesis has no chain configuration")
|
||||||
|
|
||||||
|
// Deprecated: use types.GenesisAccount instead.
|
||||||
|
type GenesisAccount = types.Account
|
||||||
|
|
||||||
|
// Deprecated: use types.GenesisAlloc instead.
|
||||||
|
type GenesisAlloc = types.GenesisAlloc
|
||||||
|
|
||||||
// Genesis specifies the header fields, state of a genesis block. It also defines hard
|
// Genesis specifies the header fields, state of a genesis block. It also defines hard
|
||||||
// fork switch-over blocks through the chain configuration.
|
// fork switch-over blocks through the chain configuration.
|
||||||
type Genesis struct {
|
type Genesis struct {
|
||||||
|
|
@ -60,7 +66,7 @@ type Genesis struct {
|
||||||
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
|
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
|
||||||
Mixhash common.Hash `json:"mixHash"`
|
Mixhash common.Hash `json:"mixHash"`
|
||||||
Coinbase common.Address `json:"coinbase"`
|
Coinbase common.Address `json:"coinbase"`
|
||||||
Alloc GenesisAlloc `json:"alloc" gencodec:"required"`
|
Alloc types.GenesisAlloc `json:"alloc" gencodec:"required"`
|
||||||
|
|
||||||
// These fields are used for consensus tests. Please don't use them
|
// These fields are used for consensus tests. Please don't use them
|
||||||
// in actual genesis blocks.
|
// in actual genesis blocks.
|
||||||
|
|
@ -120,31 +126,14 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
||||||
return &genesis, nil
|
return &genesis, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenesisAlloc specifies the initial state that is part of the genesis block.
|
// hashAlloc computes the state root according to the genesis specification.
|
||||||
type GenesisAlloc map[common.Address]GenesisAccount
|
func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||||
|
|
||||||
func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
|
|
||||||
m := make(map[common.UnprefixedAddress]GenesisAccount)
|
|
||||||
if err := json.Unmarshal(data, &m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*ga = make(GenesisAlloc)
|
|
||||||
for addr, a := range m {
|
|
||||||
(*ga)[common.Address(addr)] = a
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash computes the state root according to the genesis specification.
|
|
||||||
func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
|
|
||||||
// If a genesis-time verkle trie is requested, create a trie config
|
// If a genesis-time verkle trie is requested, create a trie config
|
||||||
// with the verkle trie enabled so that the tree can be initialized
|
// with the verkle trie enabled so that the tree can be initialized
|
||||||
// as such.
|
// as such.
|
||||||
var config *trie.Config
|
var config *triedb.Config
|
||||||
if isVerkle {
|
if isVerkle {
|
||||||
config = &trie.Config{
|
config = &triedb.Config{
|
||||||
PathDB: pathdb.Defaults,
|
PathDB: pathdb.Defaults,
|
||||||
IsVerkle: true,
|
IsVerkle: true,
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +148,7 @@ func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
|
||||||
|
|
||||||
for addr, account := range *ga {
|
for addr, account := range *ga {
|
||||||
if account.Balance != nil {
|
if account.Balance != nil {
|
||||||
statedb.AddBalance(addr, account.Balance)
|
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance))
|
||||||
}
|
}
|
||||||
statedb.SetCode(addr, account.Code)
|
statedb.SetCode(addr, account.Code)
|
||||||
statedb.SetNonce(addr, account.Nonce)
|
statedb.SetNonce(addr, account.Nonce)
|
||||||
|
|
@ -171,10 +160,10 @@ func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
|
||||||
return statedb.Commit(0, false)
|
return statedb.Commit(0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush is very similar with hash, but the main difference is all the generated
|
// flushAlloc is very similar with hash, but the main difference is all the generated
|
||||||
// states will be persisted into the given database. Also, the genesis state
|
// states will be persisted into the given database. Also, the genesis state
|
||||||
// specification will be flushed as well.
|
// specification will be flushed as well.
|
||||||
func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error {
|
func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Database, blockhash common.Hash) error {
|
||||||
statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -182,7 +171,7 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
|
||||||
|
|
||||||
for addr, account := range *ga {
|
for addr, account := range *ga {
|
||||||
if account.Balance != nil {
|
if account.Balance != nil {
|
||||||
statedb.AddBalance(addr, account.Balance)
|
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance))
|
||||||
}
|
}
|
||||||
statedb.SetCode(addr, account.Code)
|
statedb.SetCode(addr, account.Code)
|
||||||
statedb.SetNonce(addr, account.Nonce)
|
statedb.SetNonce(addr, account.Nonce)
|
||||||
|
|
@ -212,15 +201,6 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenesisAccount is an account in the state of the genesis block.
|
|
||||||
type GenesisAccount struct {
|
|
||||||
Code []byte `json:"code,omitempty"`
|
|
||||||
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
|
|
||||||
Balance *big.Int `json:"balance" gencodec:"required"`
|
|
||||||
Nonce uint64 `json:"nonce,omitempty"`
|
|
||||||
PrivateKey []byte `json:"secretKey,omitempty"` // for tests
|
|
||||||
}
|
|
||||||
|
|
||||||
// field type overrides for gencodec
|
// field type overrides for gencodec
|
||||||
type genesisSpecMarshaling struct {
|
type genesisSpecMarshaling struct {
|
||||||
Nonce math.HexOrDecimal64
|
Nonce math.HexOrDecimal64
|
||||||
|
|
@ -230,42 +210,12 @@ type genesisSpecMarshaling struct {
|
||||||
GasUsed math.HexOrDecimal64
|
GasUsed math.HexOrDecimal64
|
||||||
Number math.HexOrDecimal64
|
Number math.HexOrDecimal64
|
||||||
Difficulty *math.HexOrDecimal256
|
Difficulty *math.HexOrDecimal256
|
||||||
Alloc map[common.UnprefixedAddress]GenesisAccount
|
Alloc map[common.UnprefixedAddress]types.Account
|
||||||
BaseFee *math.HexOrDecimal256
|
BaseFee *math.HexOrDecimal256
|
||||||
ExcessBlobGas *math.HexOrDecimal64
|
ExcessBlobGas *math.HexOrDecimal64
|
||||||
BlobGasUsed *math.HexOrDecimal64
|
BlobGasUsed *math.HexOrDecimal64
|
||||||
}
|
}
|
||||||
|
|
||||||
type genesisAccountMarshaling struct {
|
|
||||||
Code hexutil.Bytes
|
|
||||||
Balance *math.HexOrDecimal256
|
|
||||||
Nonce math.HexOrDecimal64
|
|
||||||
Storage map[storageJSON]storageJSON
|
|
||||||
PrivateKey hexutil.Bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
|
|
||||||
// unmarshaling from hex.
|
|
||||||
type storageJSON common.Hash
|
|
||||||
|
|
||||||
func (h *storageJSON) UnmarshalText(text []byte) error {
|
|
||||||
text = bytes.TrimPrefix(text, []byte("0x"))
|
|
||||||
if len(text) > 64 {
|
|
||||||
return fmt.Errorf("too many hex characters in storage key/value %q", text)
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := len(h) - len(text)/2 // pad on the left
|
|
||||||
if _, err := hex.Decode(h[offset:], text); err != nil {
|
|
||||||
return fmt.Errorf("invalid hex storage key/value %q", text)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h storageJSON) MarshalText() ([]byte, error) {
|
|
||||||
return hexutil.Bytes(h[:]).MarshalText()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenesisMismatchError is raised when trying to overwrite an existing
|
// GenesisMismatchError is raised when trying to overwrite an existing
|
||||||
// genesis block with an incompatible one.
|
// genesis block with an incompatible one.
|
||||||
type GenesisMismatchError struct {
|
type GenesisMismatchError struct {
|
||||||
|
|
@ -295,12 +245,11 @@ type ChainOverrides struct {
|
||||||
// error is a *params.ConfigCompatError and the new, unwritten config is returned.
|
// error is a *params.ConfigCompatError and the new, unwritten config is returned.
|
||||||
//
|
//
|
||||||
// The returned chain configuration is never nil.
|
// The returned chain configuration is never nil.
|
||||||
func SetupGenesisBlock(db ethdb.Database, triedb *trie.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
func SetupGenesisBlock(db ethdb.Database, triedb *triedb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
||||||
return SetupGenesisBlockWithOverride(db, triedb, genesis, nil)
|
return SetupGenesisBlockWithOverride(db, triedb, genesis, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint:gocognit
|
func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, genesis *Genesis, overrides *ChainOverrides) (*params.ChainConfig, common.Hash, error) {
|
||||||
func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, genesis *Genesis, overrides *ChainOverrides) (*params.ChainConfig, common.Hash, error) {
|
|
||||||
if genesis != nil && genesis.Config == nil {
|
if genesis != nil && genesis.Config == nil {
|
||||||
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
|
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
|
||||||
}
|
}
|
||||||
|
|
@ -449,6 +398,8 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||||
return g.Config
|
return g.Config
|
||||||
case ghash == params.MainnetGenesisHash:
|
case ghash == params.MainnetGenesisHash:
|
||||||
return params.MainnetChainConfig
|
return params.MainnetChainConfig
|
||||||
|
case ghash == params.HoleskyGenesisHash:
|
||||||
|
return params.HoleskyChainConfig
|
||||||
case ghash == params.SepoliaGenesisHash:
|
case ghash == params.SepoliaGenesisHash:
|
||||||
return params.SepoliaChainConfig
|
return params.SepoliaChainConfig
|
||||||
case ghash == params.GoerliGenesisHash:
|
case ghash == params.GoerliGenesisHash:
|
||||||
|
|
@ -472,7 +423,7 @@ func (g *Genesis) IsVerkle() bool {
|
||||||
|
|
||||||
// ToBlock returns the genesis block according to genesis specification.
|
// ToBlock returns the genesis block according to genesis specification.
|
||||||
func (g *Genesis) ToBlock() *types.Block {
|
func (g *Genesis) ToBlock() *types.Block {
|
||||||
root, err := g.Alloc.hash(g.IsVerkle())
|
root, err := hashAlloc(&g.Alloc, g.IsVerkle())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -526,7 +477,7 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||||
|
|
||||||
// Commit writes the block and state of a genesis specification to the database.
|
// Commit writes the block and state of a genesis specification to the database.
|
||||||
// The block is committed as the canonical head block.
|
// The block is committed as the canonical head block.
|
||||||
func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block, error) {
|
func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) {
|
||||||
block := g.ToBlock()
|
block := g.ToBlock()
|
||||||
if block.Number().Sign() != 0 {
|
if block.Number().Sign() != 0 {
|
||||||
return nil, errors.New("can't commit genesis block with number > 0")
|
return nil, errors.New("can't commit genesis block with number > 0")
|
||||||
|
|
@ -541,10 +492,10 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block
|
||||||
if config.Clique != nil && len(block.Extra()) < 32+crypto.SignatureLength {
|
if config.Clique != nil && len(block.Extra()) < 32+crypto.SignatureLength {
|
||||||
return nil, errors.New("can't start clique chain without signers")
|
return nil, errors.New("can't start clique chain without signers")
|
||||||
}
|
}
|
||||||
// All the checks has passed, flush the states derived from the genesis
|
// All the checks has passed, flushAlloc the states derived from the genesis
|
||||||
// specification as well as the specification itself into the provided
|
// specification as well as the specification itself into the provided
|
||||||
// database.
|
// database.
|
||||||
if err := g.Alloc.flush(db, triedb, block.Hash()); err != nil {
|
if err := flushAlloc(&g.Alloc, db, triedb, block.Hash()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
|
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
|
||||||
|
|
@ -560,7 +511,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block
|
||||||
|
|
||||||
// MustCommit writes the genesis block and state to db, panicking on error.
|
// MustCommit writes the genesis block and state to db, panicking on error.
|
||||||
// The block is committed as the canonical head block.
|
// The block is committed as the canonical head block.
|
||||||
func (g *Genesis) MustCommit(db ethdb.Database, triedb *trie.Database) *types.Block {
|
func (g *Genesis) MustCommit(db ethdb.Database, triedb *triedb.Database) *types.Block {
|
||||||
block, err := g.Commit(db, triedb)
|
block, err := g.Commit(db, triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -575,7 +526,7 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big
|
||||||
Alloc: GenesisAlloc{addr: {Balance: balance}},
|
Alloc: GenesisAlloc{addr: {Balance: balance}},
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
}
|
}
|
||||||
return g.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
|
return g.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultGenesisBlock returns the Ethereum main net genesis block.
|
// DefaultGenesisBlock returns the Ethereum main net genesis block.
|
||||||
|
|
@ -615,6 +566,18 @@ func DefaultSepoliaGenesisBlock() *Genesis {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultHoleskyGenesisBlock returns the Holesky network genesis block.
|
||||||
|
func DefaultHoleskyGenesisBlock() *Genesis {
|
||||||
|
return &Genesis{
|
||||||
|
Config: params.HoleskyChainConfig,
|
||||||
|
Nonce: 0x1234,
|
||||||
|
GasLimit: 0x17d7840,
|
||||||
|
Difficulty: big.NewInt(0x01),
|
||||||
|
Timestamp: 1695902100,
|
||||||
|
Alloc: decodePrealloc(holeskyAllocData),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultMumbaiGenesisBlock returns the Mumbai network genesis block.
|
// DefaultMumbaiGenesisBlock returns the Mumbai network genesis block.
|
||||||
func DefaultMumbaiGenesisBlock() *Genesis {
|
func DefaultMumbaiGenesisBlock() *Genesis {
|
||||||
return &Genesis{
|
return &Genesis{
|
||||||
|
|
@ -668,7 +631,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
||||||
GasLimit: gasLimit,
|
GasLimit: gasLimit,
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
Difficulty: big.NewInt(1),
|
Difficulty: big.NewInt(1),
|
||||||
Alloc: map[common.Address]GenesisAccount{
|
Alloc: map[common.Address]types.Account{
|
||||||
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
||||||
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
|
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
|
||||||
common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD
|
common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD
|
||||||
|
|
@ -681,12 +644,12 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if faucet != nil {
|
if faucet != nil {
|
||||||
genesis.Alloc[*faucet] = GenesisAccount{Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}
|
genesis.Alloc[*faucet] = types.Account{Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}
|
||||||
}
|
}
|
||||||
return genesis
|
return genesis
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodePrealloc(data string) GenesisAlloc {
|
func decodePrealloc(data string) types.GenesisAlloc {
|
||||||
var p []struct {
|
var p []struct {
|
||||||
Addr *big.Int
|
Addr *big.Int
|
||||||
Balance *big.Int
|
Balance *big.Int
|
||||||
|
|
@ -703,9 +666,9 @@ func decodePrealloc(data string) GenesisAlloc {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ga := make(GenesisAlloc, len(p))
|
ga := make(types.GenesisAlloc, len(p))
|
||||||
for _, account := range p {
|
for _, account := range p {
|
||||||
acc := GenesisAccount{Balance: account.Balance}
|
acc := types.Account{Balance: account.Balance}
|
||||||
if account.Misc != nil {
|
if account.Misc != nil {
|
||||||
acc.Nonce = account.Misc.Nonce
|
acc.Nonce = account.Misc.Nonce
|
||||||
acc.Code = account.Misc.Code
|
acc.Code = account.Misc.Code
|
||||||
|
|
|
||||||
|
|
@ -27,18 +27,19 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInvalidCliqueConfig(t *testing.T) {
|
func TestInvalidCliqueConfig(t *testing.T) {
|
||||||
block := DefaultGoerliGenesisBlock()
|
block := DefaultGoerliGenesisBlock()
|
||||||
block.ExtraData = []byte{}
|
block.ExtraData = []byte{}
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
if _, err := block.Commit(db, trie.NewDatabase(db, nil)); err == nil {
|
if _, err := block.Commit(db, triedb.NewDatabase(db, nil)); err == nil {
|
||||||
t.Fatal("Expected error on invalid clique config")
|
t.Fatal("Expected error on invalid clique config")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +54,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
|
customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
|
||||||
customg = Genesis{
|
customg = Genesis{
|
||||||
Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)},
|
Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)},
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +73,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
{
|
{
|
||||||
name: "genesis without ChainConfig",
|
name: "genesis without ChainConfig",
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), new(Genesis))
|
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), new(Genesis))
|
||||||
},
|
},
|
||||||
wantErr: errGenesisNoConfig,
|
wantErr: errGenesisNoConfig,
|
||||||
wantConfig: params.AllEthashProtocolChanges,
|
wantConfig: params.AllEthashProtocolChanges,
|
||||||
|
|
@ -80,7 +81,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
{
|
{
|
||||||
name: "no block in DB, genesis == nil",
|
name: "no block in DB, genesis == nil",
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
|
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||||
},
|
},
|
||||||
wantHash: params.MainnetGenesisHash,
|
wantHash: params.MainnetGenesisHash,
|
||||||
wantConfig: params.MainnetChainConfig,
|
wantConfig: params.MainnetChainConfig,
|
||||||
|
|
@ -88,8 +89,8 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
{
|
{
|
||||||
name: "mainnet block in DB, genesis == nil",
|
name: "mainnet block in DB, genesis == nil",
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
DefaultGenesisBlock().MustCommit(db, trie.NewDatabase(db, newDbConfig(scheme)))
|
DefaultGenesisBlock().MustCommit(db, triedb.NewDatabase(db, newDbConfig(scheme)))
|
||||||
return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
|
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||||
},
|
},
|
||||||
wantHash: params.MainnetGenesisHash,
|
wantHash: params.MainnetGenesisHash,
|
||||||
wantConfig: params.MainnetChainConfig,
|
wantConfig: params.MainnetChainConfig,
|
||||||
|
|
@ -97,7 +98,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
{
|
{
|
||||||
name: "custom block in DB, genesis == nil",
|
name: "custom block in DB, genesis == nil",
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||||
customg.Commit(db, tdb)
|
customg.Commit(db, tdb)
|
||||||
return SetupGenesisBlock(db, tdb, nil)
|
return SetupGenesisBlock(db, tdb, nil)
|
||||||
},
|
},
|
||||||
|
|
@ -107,7 +108,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
{
|
{
|
||||||
name: "custom block in DB, genesis == goerli",
|
name: "custom block in DB, genesis == goerli",
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||||
customg.Commit(db, tdb)
|
customg.Commit(db, tdb)
|
||||||
return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
|
return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
|
||||||
},
|
},
|
||||||
|
|
@ -118,7 +119,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
{
|
{
|
||||||
name: "compatible config in DB",
|
name: "compatible config in DB",
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||||
oldcustomg.Commit(db, tdb)
|
oldcustomg.Commit(db, tdb)
|
||||||
return SetupGenesisBlock(db, tdb, &customg)
|
return SetupGenesisBlock(db, tdb, &customg)
|
||||||
},
|
},
|
||||||
|
|
@ -130,7 +131,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
// Commit the 'old' genesis block with Homestead transition at #2.
|
// Commit the 'old' genesis block with Homestead transition at #2.
|
||||||
// Advance to block #4, past the homestead transition block of customg.
|
// Advance to block #4, past the homestead transition block of customg.
|
||||||
tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||||
oldcustomg.Commit(db, tdb)
|
oldcustomg.Commit(db, tdb)
|
||||||
|
|
||||||
bc, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), &oldcustomg, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
bc, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), &oldcustomg, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
|
||||||
|
|
@ -191,7 +192,7 @@ func TestGenesisHashes(t *testing.T) {
|
||||||
} {
|
} {
|
||||||
// Test via MustCommit
|
// Test via MustCommit
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
if have := c.genesis.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults)).Hash(); have != c.want {
|
if have := c.genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults)).Hash(); have != c.want {
|
||||||
t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
|
t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
|
||||||
}
|
}
|
||||||
// Test via ToBlock
|
// Test via ToBlock
|
||||||
|
|
@ -209,7 +210,7 @@ func TestGenesis_Commit(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
genesisBlock := genesis.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
|
genesisBlock := genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
|
|
||||||
if genesis.Difficulty != nil {
|
if genesis.Difficulty != nil {
|
||||||
t.Fatalf("assumption wrong")
|
t.Fatalf("assumption wrong")
|
||||||
|
|
@ -231,19 +232,18 @@ func TestGenesis_Commit(t *testing.T) {
|
||||||
func TestReadWriteGenesisAlloc(t *testing.T) {
|
func TestReadWriteGenesisAlloc(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
alloc = &GenesisAlloc{
|
alloc = &types.GenesisAlloc{
|
||||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
||||||
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
|
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
|
||||||
}
|
}
|
||||||
hash, _ = alloc.hash(false)
|
hash, _ = hashAlloc(alloc, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint : errchkjson
|
// nolint : errchkjson
|
||||||
blob, _ := json.Marshal(alloc)
|
blob, _ := json.Marshal(alloc)
|
||||||
rawdb.WriteGenesisStateSpec(db, hash, blob)
|
rawdb.WriteGenesisStateSpec(db, hash, blob)
|
||||||
|
|
||||||
var reload GenesisAlloc
|
var reload types.GenesisAlloc
|
||||||
|
|
||||||
err := reload.UnmarshalJSON(rawdb.ReadGenesisStateSpec(db, hash))
|
err := reload.UnmarshalJSON(rawdb.ReadGenesisStateSpec(db, hash))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to load genesis state %v", err)
|
t.Fatalf("Failed to load genesis state %v", err)
|
||||||
|
|
@ -265,11 +265,11 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDbConfig(scheme string) *trie.Config {
|
func newDbConfig(scheme string) *triedb.Config {
|
||||||
if scheme == rawdb.HashScheme {
|
if scheme == rawdb.HashScheme {
|
||||||
return trie.HashDefaults
|
return triedb.HashDefaults
|
||||||
}
|
}
|
||||||
return &trie.Config{PathDB: pathdb.Defaults}
|
return &triedb.Config{PathDB: pathdb.Defaults}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerkleGenesisCommit(t *testing.T) {
|
func TestVerkleGenesisCommit(t *testing.T) {
|
||||||
|
|
@ -308,7 +308,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||||
Config: verkleConfig,
|
Config: verkleConfig,
|
||||||
Timestamp: verkleTime,
|
Timestamp: verkleTime,
|
||||||
Difficulty: big.NewInt(0),
|
Difficulty: big.NewInt(0),
|
||||||
Alloc: GenesisAlloc{
|
Alloc: types.GenesisAlloc{
|
||||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -320,7 +320,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
triedb := trie.NewDatabase(db, &trie.Config{IsVerkle: true, PathDB: pathdb.Defaults})
|
triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults})
|
||||||
block := genesis.MustCommit(db, triedb)
|
block := genesis.MustCommit(db, triedb)
|
||||||
if !bytes.Equal(block.Root().Bytes(), expected) {
|
if !bytes.Equal(block.Root().Bytes(), expected) {
|
||||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func verifyUnbrokenCanonchain(hc *HeaderChain) error {
|
func verifyUnbrokenCanonchain(hc *HeaderChain) error {
|
||||||
|
|
@ -78,7 +78,7 @@ func TestHeaderInsertion(t *testing.T) {
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
||||||
)
|
)
|
||||||
gspec.Commit(db, trie.NewDatabase(db, nil))
|
gspec.Commit(db, triedb.NewDatabase(db, nil))
|
||||||
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
cmath "github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/core/blockstm"
|
"github.com/ethereum/go-ethereum/core/blockstm"
|
||||||
|
|
@ -186,10 +187,10 @@ func (task *ExecutionTask) Settle() {
|
||||||
|
|
||||||
if *task.shouldDelayFeeCal {
|
if *task.shouldDelayFeeCal {
|
||||||
if task.config.IsLondon(task.blockNumber) {
|
if task.config.IsLondon(task.blockNumber) {
|
||||||
task.finalStateDB.AddBalance(task.result.BurntContractAddress, task.result.FeeBurnt)
|
task.finalStateDB.AddBalance(task.result.BurntContractAddress, cmath.BigIntToUint256Int(task.result.FeeBurnt))
|
||||||
}
|
}
|
||||||
|
|
||||||
task.finalStateDB.AddBalance(task.coinbase, task.result.FeeTipped)
|
task.finalStateDB.AddBalance(task.coinbase, cmath.BigIntToUint256Int(task.result.FeeTipped))
|
||||||
output1 := new(big.Int).SetBytes(task.result.SenderInitBalance.Bytes())
|
output1 := new(big.Int).SetBytes(task.result.SenderInitBalance.Bytes())
|
||||||
output2 := new(big.Int).SetBytes(coinbaseBalance.Bytes())
|
output2 := new(big.Int).SetBytes(coinbaseBalance.Bytes())
|
||||||
|
|
||||||
|
|
@ -203,7 +204,7 @@ func (task *ExecutionTask) Settle() {
|
||||||
|
|
||||||
task.result.FeeTipped,
|
task.result.FeeTipped,
|
||||||
task.result.SenderInitBalance,
|
task.result.SenderInitBalance,
|
||||||
coinbaseBalance,
|
coinbaseBalance.ToBig(),
|
||||||
output1.Sub(output1, task.result.FeeTipped),
|
output1.Sub(output1, task.result.FeeTipped),
|
||||||
output2.Add(output2, task.result.FeeTipped),
|
output2.Add(output2, task.result.FeeTipped),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,11 @@ func ReadStateScheme(db ethdb.Reader) string {
|
||||||
if len(blob) != 0 {
|
if len(blob) != 0 {
|
||||||
return PathScheme
|
return PathScheme
|
||||||
}
|
}
|
||||||
|
// The root node might be deleted during the initial snap sync, check
|
||||||
|
// the persistent state id then.
|
||||||
|
if id := ReadPersistentStateID(db); id != 0 {
|
||||||
|
return PathScheme
|
||||||
|
}
|
||||||
// In a hash-based scheme, the genesis state is consistently stored
|
// In a hash-based scheme, the genesis state is consistently stored
|
||||||
// on the disk. To assess the scheme of the persistent state, it
|
// on the disk. To assess the scheme of the persistent state, it
|
||||||
// suffices to inspect the scheme of the genesis state.
|
// suffices to inspect the scheme of the genesis state.
|
||||||
|
|
|
||||||
|
|
@ -69,14 +69,14 @@ var stateFreezerNoSnappy = map[string]bool{
|
||||||
|
|
||||||
// The list of identifiers of ancient stores.
|
// The list of identifiers of ancient stores.
|
||||||
var (
|
var (
|
||||||
chainFreezerName = "chain" // the folder name of chain segment ancient store.
|
ChainFreezerName = "chain" // the folder name of chain segment ancient store.
|
||||||
stateFreezerName = "state" // the folder name of reverse diff ancient store.
|
StateFreezerName = "state" // the folder name of reverse diff ancient store.
|
||||||
)
|
)
|
||||||
|
|
||||||
// freezers the collections of all builtin freezers.
|
// freezers the collections of all builtin freezers.
|
||||||
var freezers = []string{chainFreezerName, stateFreezerName}
|
var freezers = []string{ChainFreezerName, StateFreezerName}
|
||||||
|
|
||||||
// NewStateFreezer initializes the freezer for state history.
|
// NewStateFreezer initializes the freezer for state history.
|
||||||
func NewStateFreezer(ancientDir string, readOnly bool) (*ResettableFreezer, error) {
|
func NewStateFreezer(ancientDir string, readOnly bool) (*ResettableFreezer, error) {
|
||||||
return NewResettableFreezer(filepath.Join(ancientDir, stateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
|
return NewResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,14 +83,14 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
|
||||||
|
|
||||||
for _, freezer := range freezers {
|
for _, freezer := range freezers {
|
||||||
switch freezer {
|
switch freezer {
|
||||||
case chainFreezerName:
|
case ChainFreezerName:
|
||||||
info, err := inspect(chainFreezerName, chainFreezerNoSnappy, db)
|
info, err := inspect(ChainFreezerName, chainFreezerNoSnappy, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
infos = append(infos, info)
|
infos = append(infos, info)
|
||||||
|
|
||||||
case stateFreezerName:
|
case StateFreezerName:
|
||||||
if ReadStateScheme(db) != PathScheme {
|
if ReadStateScheme(db) != PathScheme {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +104,7 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
info, err := inspect(stateFreezerName, stateFreezerNoSnappy, f)
|
info, err := inspect(StateFreezerName, stateFreezerNoSnappy, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -129,9 +129,9 @@ func InspectFreezerTable(ancient string, freezerName string, tableName string, s
|
||||||
)
|
)
|
||||||
|
|
||||||
switch freezerName {
|
switch freezerName {
|
||||||
case chainFreezerName:
|
case ChainFreezerName:
|
||||||
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
|
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
|
||||||
case stateFreezerName:
|
case StateFreezerName:
|
||||||
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
|
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown freezer, supported ones: %v", freezers)
|
return fmt.Errorf("unknown freezer, supported ones: %v", freezers)
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case *number < threshold:
|
case *number < threshold:
|
||||||
log.Debug("Current full block not old enough", "number", *number, "hash", hash, "delay", threshold)
|
log.Debug("Current full block not old enough to freeze", "number", *number, "hash", hash, "delay", threshold)
|
||||||
|
|
||||||
backoff = true
|
backoff = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -205,11 +205,8 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
|
||||||
//
|
//
|
||||||
// There is a passed channel, the whole procedure will be interrupted if any
|
// There is a passed channel, the whole procedure will be interrupted if any
|
||||||
// signal received.
|
// signal received.
|
||||||
func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
|
func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
|
||||||
// adjust range boundary for pruned block
|
|
||||||
if offset := db.AncientOffSet(); offset > from {
|
|
||||||
from = offset
|
|
||||||
}
|
|
||||||
// short circuit for invalid range
|
// short circuit for invalid range
|
||||||
if from >= to {
|
if from >= to {
|
||||||
return
|
return
|
||||||
|
|
@ -220,13 +217,13 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
|
||||||
batch = db.NewBatch()
|
batch = db.NewBatch()
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
logged = start.Add(-7 * time.Second)
|
logged = start.Add(-7 * time.Second)
|
||||||
|
|
||||||
// Since we iterate in reverse, we expect the first number to come
|
// Since we iterate in reverse, we expect the first number to come
|
||||||
// in to be [to-1]. Therefore, setting lastNum to means that the
|
// in to be [to-1]. Therefore, setting lastNum to means that the
|
||||||
// prqueue gap-evaluation will work correctly
|
// queue gap-evaluation will work correctly
|
||||||
lastNum = to
|
lastNum = to
|
||||||
queue = prque.New[int64, *blockTxHashes](nil)
|
queue = prque.New[int64, *blockTxHashes](nil)
|
||||||
// for stats reporting
|
blocks, txs = 0, 0 // for stats reporting
|
||||||
blocks, txs = 0, 0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for chanDelivery := range hashesCh {
|
for chanDelivery := range hashesCh {
|
||||||
|
|
@ -278,11 +275,15 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
|
||||||
log.Crit("Failed writing batch to db", "error", err)
|
log.Crit("Failed writing batch to db", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logger := log.Debug
|
||||||
|
if report {
|
||||||
|
logger = log.Info
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case <-interrupt:
|
case <-interrupt:
|
||||||
log.Debug("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
|
logger("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
default:
|
default:
|
||||||
log.Debug("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
|
logger("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,24 +296,21 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
|
||||||
//
|
//
|
||||||
// There is a passed channel, the whole procedure will be interrupted if any
|
// There is a passed channel, the whole procedure will be interrupted if any
|
||||||
// signal received.
|
// signal received.
|
||||||
func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) {
|
func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, report bool) {
|
||||||
indexTransactions(db, from, to, interrupt, nil)
|
indexTransactions(db, from, to, interrupt, nil, report)
|
||||||
}
|
}
|
||||||
|
|
||||||
// indexTransactionsForTesting is the internal debug version with an additional hook.
|
// indexTransactionsForTesting is the internal debug version with an additional hook.
|
||||||
func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
|
func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
|
||||||
indexTransactions(db, from, to, interrupt, hook)
|
indexTransactions(db, from, to, interrupt, hook, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unindexTransactions removes txlookup indices of the specified block range.
|
// unindexTransactions removes txlookup indices of the specified block range.
|
||||||
//
|
//
|
||||||
// There is a passed channel, the whole procedure will be interrupted if any
|
// There is a passed channel, the whole procedure will be interrupted if any
|
||||||
// signal received.
|
// signal received.
|
||||||
func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
|
func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
|
||||||
// adjust range boundary for pruned block
|
|
||||||
if offset := db.AncientOffSet(); offset > from {
|
|
||||||
from = offset
|
|
||||||
}
|
|
||||||
// short circuit for invalid range
|
// short circuit for invalid range
|
||||||
if from >= to {
|
if from >= to {
|
||||||
return
|
return
|
||||||
|
|
@ -323,12 +321,12 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
|
||||||
batch = db.NewBatch()
|
batch = db.NewBatch()
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
logged = start.Add(-7 * time.Second)
|
logged = start.Add(-7 * time.Second)
|
||||||
|
|
||||||
// we expect the first number to come in to be [from]. Therefore, setting
|
// we expect the first number to come in to be [from]. Therefore, setting
|
||||||
// nextNum to from means that the prqueue gap-evaluation will work correctly
|
// nextNum to from means that the queue gap-evaluation will work correctly
|
||||||
nextNum = from
|
nextNum = from
|
||||||
queue = prque.New[int64, *blockTxHashes](nil)
|
queue = prque.New[int64, *blockTxHashes](nil)
|
||||||
// for stats reporting
|
blocks, txs = 0, 0 // for stats reporting
|
||||||
blocks, txs = 0, 0
|
|
||||||
)
|
)
|
||||||
// Otherwise spin up the concurrent iterator and unindexer
|
// Otherwise spin up the concurrent iterator and unindexer
|
||||||
for delivery := range hashesCh {
|
for delivery := range hashesCh {
|
||||||
|
|
@ -380,11 +378,15 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
|
||||||
log.Crit("Failed writing batch to db", "error", err)
|
log.Crit("Failed writing batch to db", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logger := log.Debug
|
||||||
|
if report {
|
||||||
|
logger = log.Info
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case <-interrupt:
|
case <-interrupt:
|
||||||
log.Debug("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
|
logger("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
default:
|
default:
|
||||||
log.Debug("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
|
logger("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -393,11 +395,11 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
|
||||||
//
|
//
|
||||||
// There is a passed channel, the whole procedure will be interrupted if any
|
// There is a passed channel, the whole procedure will be interrupted if any
|
||||||
// signal received.
|
// signal received.
|
||||||
func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) {
|
func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, report bool) {
|
||||||
unindexTransactions(db, from, to, interrupt, nil)
|
unindexTransactions(db, from, to, interrupt, nil, report)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unindexTransactionsForTesting is the internal debug version with an additional hook.
|
// unindexTransactionsForTesting is the internal debug version with an additional hook.
|
||||||
func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
|
func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
|
||||||
unindexTransactions(db, from, to, interrupt, hook)
|
unindexTransactions(db, from, to, interrupt, hook, false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -177,19 +177,18 @@ func TestIndexTransactions(t *testing.T) {
|
||||||
t.Fatalf("Transaction tail mismatch")
|
t.Fatalf("Transaction tail mismatch")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
IndexTransactions(chainDb, 5, 11, nil, false)
|
||||||
IndexTransactions(chainDb, 5, 11, nil)
|
|
||||||
verify(5, 11, true, 5)
|
verify(5, 11, true, 5)
|
||||||
verify(0, 5, false, 5)
|
verify(0, 5, false, 5)
|
||||||
|
|
||||||
IndexTransactions(chainDb, 0, 5, nil)
|
IndexTransactions(chainDb, 0, 5, nil, false)
|
||||||
verify(0, 11, true, 0)
|
verify(0, 11, true, 0)
|
||||||
|
|
||||||
UnindexTransactions(chainDb, 0, 5, nil)
|
UnindexTransactions(chainDb, 0, 5, nil, false)
|
||||||
verify(5, 11, true, 5)
|
verify(5, 11, true, 5)
|
||||||
verify(0, 5, false, 5)
|
verify(0, 5, false, 5)
|
||||||
|
|
||||||
UnindexTransactions(chainDb, 5, 11, nil)
|
UnindexTransactions(chainDb, 5, 11, nil, false)
|
||||||
verify(0, 11, false, 11)
|
verify(0, 11, false, 11)
|
||||||
|
|
||||||
// Testing corner cases
|
// Testing corner cases
|
||||||
|
|
@ -210,7 +209,7 @@ func TestIndexTransactions(t *testing.T) {
|
||||||
})
|
})
|
||||||
verify(9, 11, true, 9)
|
verify(9, 11, true, 9)
|
||||||
verify(0, 9, false, 9)
|
verify(0, 9, false, 9)
|
||||||
IndexTransactions(chainDb, 0, 9, nil)
|
IndexTransactions(chainDb, 0, 9, nil, false)
|
||||||
|
|
||||||
signal = make(chan struct{})
|
signal = make(chan struct{})
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue