Merge Geth v1.10.21 as well as update to Plugeth-Utils v0.0.18

This commit is contained in:
philip-morlier
2022-07-27 10:38:42 -07:00
380 changed files with 6936 additions and 6262 deletions
+58 -81
View File
@@ -41,57 +41,44 @@ import (
"github.com/ethereum/go-ethereum/trie"
)
// PublicEthereumAPI provides an API to access Ethereum full node-related
// information.
type PublicEthereumAPI struct {
// EthereumAPI provides an API to access Ethereum full node-related information.
type EthereumAPI struct {
e *Ethereum
}
// NewPublicEthereumAPI creates a new Ethereum protocol API for full nodes.
func NewPublicEthereumAPI(e *Ethereum) *PublicEthereumAPI {
return &PublicEthereumAPI{e}
// NewEthereumAPI creates a new Ethereum protocol API for full nodes.
func NewEthereumAPI(e *Ethereum) *EthereumAPI {
return &EthereumAPI{e}
}
// Etherbase is the address that mining rewards will be send to
func (api *PublicEthereumAPI) Etherbase() (common.Address, error) {
// Etherbase is the address that mining rewards will be send to.
func (api *EthereumAPI) Etherbase() (common.Address, error) {
return api.e.Etherbase()
}
// Coinbase is the address that mining rewards will be send to (alias for Etherbase)
func (api *PublicEthereumAPI) Coinbase() (common.Address, error) {
// Coinbase is the address that mining rewards will be send to (alias for Etherbase).
func (api *EthereumAPI) Coinbase() (common.Address, error) {
return api.Etherbase()
}
// Hashrate returns the POW hashrate
func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 {
// Hashrate returns the POW hashrate.
func (api *EthereumAPI) Hashrate() hexutil.Uint64 {
return hexutil.Uint64(api.e.Miner().Hashrate())
}
// PublicMinerAPI provides an API to control the miner.
// It offers only methods that operate on data that pose no security risk when it is publicly accessible.
type PublicMinerAPI struct {
e *Ethereum
}
// NewPublicMinerAPI create a new PublicMinerAPI instance.
func NewPublicMinerAPI(e *Ethereum) *PublicMinerAPI {
return &PublicMinerAPI{e}
}
// Mining returns an indication if this node is currently mining.
func (api *PublicMinerAPI) Mining() bool {
func (api *EthereumAPI) Mining() bool {
return api.e.IsMining()
}
// PrivateMinerAPI provides private RPC methods to control the miner.
// These methods can be abused by external users and must be considered insecure for use by untrusted users.
type PrivateMinerAPI struct {
// MinerAPI provides an API to control the miner.
type MinerAPI struct {
e *Ethereum
}
// NewPrivateMinerAPI create a new RPC service which controls the miner of this node.
func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI {
return &PrivateMinerAPI{e: e}
// NewMinerAPI create a new MinerAPI instance.
func NewMinerAPI(e *Ethereum) *MinerAPI {
return &MinerAPI{e}
}
// Start starts the miner with the given number of threads. If threads is nil,
@@ -99,7 +86,7 @@ func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI {
// usable by this process. If mining is already running, this method adjust the
// number of threads allowed to use and updates the minimum price required by the
// transaction pool.
func (api *PrivateMinerAPI) Start(threads *int) error {
func (api *MinerAPI) Start(threads *int) error {
if threads == nil {
return api.e.StartMining(runtime.NumCPU())
}
@@ -108,12 +95,12 @@ func (api *PrivateMinerAPI) Start(threads *int) error {
// Stop terminates the miner, both at the consensus engine level as well as at
// the block creation level.
func (api *PrivateMinerAPI) Stop() {
func (api *MinerAPI) Stop() {
api.e.StopMining()
}
// SetExtra sets the extra data string that is included when this miner mines a block.
func (api *PrivateMinerAPI) SetExtra(extra string) (bool, error) {
func (api *MinerAPI) SetExtra(extra string) (bool, error) {
if err := api.e.Miner().SetExtra([]byte(extra)); err != nil {
return false, err
}
@@ -121,7 +108,7 @@ func (api *PrivateMinerAPI) SetExtra(extra string) (bool, error) {
}
// SetGasPrice sets the minimum accepted gas price for the miner.
func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
func (api *MinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
api.e.lock.Lock()
api.e.gasPrice = (*big.Int)(&gasPrice)
api.e.lock.Unlock()
@@ -131,37 +118,36 @@ func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
}
// SetGasLimit sets the gaslimit to target towards during mining.
func (api *PrivateMinerAPI) SetGasLimit(gasLimit hexutil.Uint64) bool {
func (api *MinerAPI) SetGasLimit(gasLimit hexutil.Uint64) bool {
api.e.Miner().SetGasCeil(uint64(gasLimit))
return true
}
// SetEtherbase sets the etherbase of the miner
func (api *PrivateMinerAPI) SetEtherbase(etherbase common.Address) bool {
// SetEtherbase sets the etherbase of the miner.
func (api *MinerAPI) SetEtherbase(etherbase common.Address) bool {
api.e.SetEtherbase(etherbase)
return true
}
// SetRecommitInterval updates the interval for miner sealing work recommitting.
func (api *PrivateMinerAPI) SetRecommitInterval(interval int) {
func (api *MinerAPI) SetRecommitInterval(interval int) {
api.e.Miner().SetRecommitInterval(time.Duration(interval) * time.Millisecond)
}
// PrivateAdminAPI is the collection of Ethereum full node-related APIs
// exposed over the private admin endpoint.
type PrivateAdminAPI struct {
// AdminAPI is the collection of Ethereum full node related APIs for node
// administration.
type AdminAPI struct {
eth *Ethereum
}
// NewPrivateAdminAPI creates a new API definition for the full node private
// admin methods of the Ethereum service.
func NewPrivateAdminAPI(eth *Ethereum) *PrivateAdminAPI {
return &PrivateAdminAPI{eth: eth}
// NewAdminAPI creates a new instance of AdminAPI.
func NewAdminAPI(eth *Ethereum) *AdminAPI {
return &AdminAPI{eth: eth}
}
// ExportChain exports the current blockchain into a local file,
// or a range of blocks if first and last are non-nil
func (api *PrivateAdminAPI) ExportChain(file string, first *uint64, last *uint64) (bool, error) {
// or a range of blocks if first and last are non-nil.
func (api *AdminAPI) ExportChain(file string, first *uint64, last *uint64) (bool, error) {
if first == nil && last != nil {
return false, errors.New("last cannot be specified without first")
}
@@ -209,7 +195,7 @@ func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool {
}
// ImportChain imports a blockchain from a local file.
func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) {
func (api *AdminAPI) ImportChain(file string) (bool, error) {
// Make sure the can access the file to import
in, err := os.Open(file)
if err != nil {
@@ -257,20 +243,19 @@ func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) {
return true, nil
}
// PublicDebugAPI is the collection of Ethereum full node APIs exposed
// over the public debugging endpoint.
type PublicDebugAPI struct {
// DebugAPI is the collection of Ethereum full node APIs for debugging the
// protocol.
type DebugAPI struct {
eth *Ethereum
}
// NewPublicDebugAPI creates a new API definition for the full node-
// related public debug methods of the Ethereum service.
func NewPublicDebugAPI(eth *Ethereum) *PublicDebugAPI {
return &PublicDebugAPI{eth: eth}
// NewDebugAPI creates a new DebugAPI instance.
func NewDebugAPI(eth *Ethereum) *DebugAPI {
return &DebugAPI{eth: eth}
}
// DumpBlock retrieves the entire state of the database at a given block.
func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
opts := &state.DumpConfig{
OnlyWithAddresses: true,
Max: AccountRangeMaxResults, // Sanity limit over RPC
@@ -287,6 +272,8 @@ func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error
block = api.eth.blockchain.CurrentBlock()
} else if blockNr == rpc.FinalizedBlockNumber {
block = api.eth.blockchain.CurrentFinalizedBlock()
} else if blockNr == rpc.SafeBlockNumber {
block = api.eth.blockchain.CurrentSafeBlock()
} else {
block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
}
@@ -300,20 +287,8 @@ func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error
return stateDb.RawDump(opts), nil
}
// PrivateDebugAPI is the collection of Ethereum full node APIs exposed over
// the private debugging endpoint.
type PrivateDebugAPI struct {
eth *Ethereum
}
// NewPrivateDebugAPI creates a new API definition for the full node-related
// private debug methods of the Ethereum service.
func NewPrivateDebugAPI(eth *Ethereum) *PrivateDebugAPI {
return &PrivateDebugAPI{eth: eth}
}
// Preimage is a debug API function that returns the preimage for a sha3 hash, if known.
func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
func (api *DebugAPI) Preimage(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
if preimage := rawdb.ReadPreimage(api.eth.ChainDb(), hash); preimage != nil {
return preimage, nil
}
@@ -328,8 +303,8 @@ type BadBlockArgs struct {
}
// GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network
// and returns them as a JSON list of block-hashes
func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) {
// and returns them as a JSON list of block hashes.
func (api *DebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) {
var (
err error
blocks = rawdb.ReadAllBadBlocks(api.eth.chainDb)
@@ -343,7 +318,7 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
if rlpBytes, err := rlp.EncodeToBytes(block); err != nil {
blockRlp = err.Error() // Hacky, but hey, it works
} else {
blockRlp = fmt.Sprintf("0x%x", rlpBytes)
blockRlp = fmt.Sprintf("%#x", rlpBytes)
}
if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.APIBackend.ChainConfig()); err != nil {
blockJSON = map[string]interface{}{"error": err.Error()}
@@ -361,7 +336,7 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
const AccountRangeMaxResults = 256
// AccountRange enumerates all accounts in the given block and start point in paging request
func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start []byte, maxResults int, nocode, nostorage, incompletes bool) (state.IteratorDump, error) {
func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hexutil.Bytes, maxResults int, nocode, nostorage, incompletes bool) (state.IteratorDump, error) {
var stateDb *state.StateDB
var err error
@@ -377,6 +352,8 @@ func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, sta
block = api.eth.blockchain.CurrentBlock()
} else if number == rpc.FinalizedBlockNumber {
block = api.eth.blockchain.CurrentFinalizedBlock()
} else if number == rpc.SafeBlockNumber {
block = api.eth.blockchain.CurrentSafeBlock()
} else {
block = api.eth.blockchain.GetBlockByNumber(uint64(number))
}
@@ -428,7 +405,7 @@ type storageEntry struct {
}
// StorageRangeAt returns the storage at the given block height and transaction index.
func (api *PrivateDebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
func (api *DebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
// Retrieve the block
block := api.eth.blockchain.GetBlockByHash(blockHash)
if block == nil {
@@ -473,7 +450,7 @@ func storageRangeAt(st state.Trie, start []byte, maxResult int) (StorageRangeRes
// code hash, or storage hash.
//
// With one parameter, returns the list of accounts modified in the specified block.
func (api *PrivateDebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
func (api *DebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum *uint64) ([]common.Address, error) {
var startBlock, endBlock *types.Block
startBlock = api.eth.blockchain.GetBlockByNumber(startNum)
@@ -501,7 +478,7 @@ func (api *PrivateDebugAPI) GetModifiedAccountsByNumber(startNum uint64, endNum
// code hash, or storage hash.
//
// With one parameter, returns the list of accounts modified in the specified block.
func (api *PrivateDebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
func (api *DebugAPI) GetModifiedAccountsByHash(startHash common.Hash, endHash *common.Hash) ([]common.Address, error) {
var startBlock, endBlock *types.Block
startBlock = api.eth.blockchain.GetBlockByHash(startHash)
if startBlock == nil {
@@ -523,17 +500,17 @@ func (api *PrivateDebugAPI) GetModifiedAccountsByHash(startHash common.Hash, end
return api.getModifiedAccounts(startBlock, endBlock)
}
func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) {
func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]common.Address, error) {
if startBlock.Number().Uint64() >= endBlock.Number().Uint64() {
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
}
triedb := api.eth.BlockChain().StateCache().TrieDB()
oldTrie, err := trie.NewSecure(startBlock.Root(), triedb)
oldTrie, err := trie.NewSecure(common.Hash{}, startBlock.Root(), triedb)
if err != nil {
return nil, err
}
newTrie, err := trie.NewSecure(endBlock.Root(), triedb)
newTrie, err := trie.NewSecure(common.Hash{}, endBlock.Root(), triedb)
if err != nil {
return nil, err
}
@@ -556,7 +533,7 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc
// of the next block.
// The (from, to) parameters are the sequence of blocks to search, which can go
// either forwards or backwards
func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64, error) {
func (api *DebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64, error) {
db := api.eth.ChainDb()
var pivot uint64
if p := rawdb.ReadLastPivotNumber(db); p != nil {
@@ -609,5 +586,5 @@ func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64
return uint64(i), nil
}
}
return 0, fmt.Errorf("No state found")
return 0, fmt.Errorf("no state found")
}
+15 -1
View File
@@ -74,7 +74,18 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
return b.eth.blockchain.CurrentBlock().Header(), nil
}
if number == rpc.FinalizedBlockNumber {
return b.eth.blockchain.CurrentFinalizedBlock().Header(), nil
block := b.eth.blockchain.CurrentFinalizedBlock()
if block != nil {
return block.Header(), nil
}
return nil, errors.New("finalized block not found")
}
if number == rpc.SafeBlockNumber {
block := b.eth.blockchain.CurrentSafeBlock()
if block != nil {
return block.Header(), nil
}
return nil, errors.New("safe block not found")
}
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
}
@@ -113,6 +124,9 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
if number == rpc.FinalizedBlockNumber {
return b.eth.blockchain.CurrentFinalizedBlock(), nil
}
if number == rpc.SafeBlockNumber {
return b.eth.blockchain.CurrentSafeBlock(), nil
}
return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil
}
+1 -1
View File
@@ -213,7 +213,7 @@ func TestStorageRangeAt(t *testing.T) {
t.Error(err)
}
if !reflect.DeepEqual(result, test.want) {
t.Fatalf("wrong result for range 0x%x.., limit %d:\ngot %s\nwant %s",
t.Fatalf("wrong result for range %#x.., limit %d:\ngot %s\nwant %s",
test.start, test.limit, dumper.Sdump(result), dumper.Sdump(&test.want))
}
}
+19 -34
View File
@@ -22,6 +22,7 @@ import (
"fmt"
"math/big"
"runtime"
"strings"
"sync"
"sync/atomic"
"time"
@@ -93,7 +94,7 @@ type Ethereum struct {
etherbase common.Address
networkID uint64
netRPCService *ethapi.PublicNetAPI
netRPCService *ethapi.NetAPI
p2pServer *p2p.Server
@@ -136,11 +137,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if err != nil {
return nil, err
}
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideArrowGlacier, config.OverrideTerminalTotalDifficulty)
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideGrayGlacier, config.OverrideTerminalTotalDifficulty)
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
return nil, genesisErr
}
log.Info("Initialised chain configuration", "config", chainConfig)
log.Info("")
log.Info(strings.Repeat("-", 153))
for _, line := range strings.Split(chainConfig.String(), "\n") {
log.Info(line)
}
log.Info(strings.Repeat("-", 153))
log.Info("")
if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil {
log.Error("Failed to recover state", "error", err)
@@ -259,7 +266,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
}
// Start the RPC service
eth.netRPCService = ethapi.NewPublicNetAPI(eth.p2pServer, config.NetworkId)
eth.netRPCService = ethapi.NewNetAPI(eth.p2pServer, config.NetworkId)
// Register the backend on the node
stack.RegisterAPIs(eth.APIs())
@@ -301,47 +308,25 @@ func (s *Ethereum) APIs() []rpc.API {
return append(apis, []rpc.API{
{
Namespace: "eth",
Version: "1.0",
Service: NewPublicEthereumAPI(s),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicMinerAPI(s),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux),
Public: true,
Service: NewEthereumAPI(s),
}, {
Namespace: "miner",
Version: "1.0",
Service: NewPrivateMinerAPI(s),
Public: false,
Service: NewMinerAPI(s),
}, {
Namespace: "eth",
Version: "1.0",
Service: filters.NewPublicFilterAPI(s.APIBackend, false, 5*time.Minute),
Public: true,
Service: downloader.NewDownloaderAPI(s.handler.downloader, s.eventMux),
}, {
Namespace: "eth",
Service: filters.NewFilterAPI(s.APIBackend, false, 5*time.Minute),
}, {
Namespace: "admin",
Version: "1.0",
Service: NewPrivateAdminAPI(s),
Service: NewAdminAPI(s),
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPublicDebugAPI(s),
Public: true,
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPrivateDebugAPI(s),
Service: NewDebugAPI(s),
}, {
Namespace: "net",
Version: "1.0",
Service: s.netRPCService,
Public: true,
},
}...)
}
+196 -54
View File
@@ -31,57 +31,89 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
)
// Register adds catalyst APIs to the full node.
// Register adds the engine API to the full node.
func Register(stack *node.Node, backend *eth.Ethereum) error {
log.Warn("Catalyst mode enabled", "protocol", "eth")
log.Warn("Engine API enabled", "protocol", "eth")
stack.RegisterAPIs([]rpc.API{
{
Namespace: "engine",
Version: "1.0",
Service: NewConsensusAPI(backend),
Public: true,
Authenticated: true,
},
{
Namespace: "engine",
Version: "1.0",
Service: NewConsensusAPI(backend),
Public: true,
Authenticated: false,
},
})
return nil
}
const (
// invalidBlockHitEviction is the number of times an invalid block can be
// referenced in forkchoice update or new payload before it is attempted
// to be reprocessed again.
invalidBlockHitEviction = 128
// invalidTipsetsCap is the max number of recent block hashes tracked that
// have lead to some bad ancestor block. It's just an OOM protection.
invalidTipsetsCap = 512
)
type ConsensusAPI struct {
eth *eth.Ethereum
eth *eth.Ethereum
remoteBlocks *headerQueue // Cache of remote payloads received
localBlocks *payloadQueue // Cache of local payloads generated
// Lock for the forkChoiceUpdated method
forkChoiceLock sync.Mutex
// The forkchoice update and new payload method require us to return the
// latest valid hash in an invalid chain. To support that return, we need
// to track historical bad blocks as well as bad tipsets in case a chain
// is constantly built on it.
//
// There are a few important caveats in this mechanism:
// - The bad block tracking is ephemeral, in-memory only. We must never
// persist any bad block information to disk as a bug in Geth could end
// up blocking a valid chain, even if a later Geth update would accept
// it.
// - Bad blocks will get forgotten after a certain threshold of import
// attempts and will be retried. The rationale is that if the network
// really-really-really tries to feed us a block, we should give it a
// new chance, perhaps us being racey instead of the block being legit
// bad (this happened in Geth at a point with import vs. pending race).
// - Tracking all the blocks built on top of the bad one could be a bit
// problematic, so we will only track the head chain segment of a bad
// chain to allow discarding progressing bad chains and side chains,
// without tracking too much bad data.
invalidBlocksHits map[common.Hash]int // Emhemeral cache to track invalid blocks and their hit count
invalidTipsets map[common.Hash]*types.Header // Ephemeral cache to track invalid tipsets and their bad ancestor
invalidLock sync.Mutex // Protects the invalid maps from concurrent access
forkChoiceLock sync.Mutex // Lock for the forkChoiceUpdated method
}
// NewConsensusAPI creates a new consensus api for the given backend.
// The underlying blockchain needs to have a valid terminal total difficulty set.
func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI {
if eth.BlockChain().Config().TerminalTotalDifficulty == nil {
panic("Catalyst started without valid total difficulty")
log.Warn("Engine API started but chain not configured for merge yet")
}
return &ConsensusAPI{
eth: eth,
remoteBlocks: newHeaderQueue(),
localBlocks: newPayloadQueue(),
api := &ConsensusAPI{
eth: eth,
remoteBlocks: newHeaderQueue(),
localBlocks: newPayloadQueue(),
invalidBlocksHits: make(map[common.Hash]int),
invalidTipsets: make(map[common.Hash]*types.Header),
}
eth.Downloader().SetBadBlockCallback(api.setInvalidAncestor)
return api
}
// ForkchoiceUpdatedV1 has several responsibilities:
// If the method is called with an empty head block:
// we return success, which can be used to check if the catalyst mode is enabled
// we return success, which can be used to check if the engine API is enabled
// If the total difficulty was not reached:
// we return INVALID
// If the finalizedBlockHash is set:
@@ -104,6 +136,10 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
// reason.
block := api.eth.BlockChain().GetBlockByHash(update.HeadBlockHash)
if block == nil {
// If this block was previously invalidated, keep rejecting it here too
if res := api.checkInvalidAncestor(update.HeadBlockHash, update.HeadBlockHash); res != nil {
return beacon.ForkChoiceResponse{PayloadStatus: *res, PayloadID: nil}, nil
}
// If the head hash is unknown (was not given to us in a newPayload request),
// we cannot resolve the header, so not much to do. This could be extended in
// the future to resolve from the `eth` network, but it's an unexpected case
@@ -138,21 +174,35 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
log.Error("TDs unavailable for TTD check", "number", block.NumberU64(), "hash", update.HeadBlockHash, "td", td, "parent", block.ParentHash(), "ptd", ptd)
return beacon.STATUS_INVALID, errors.New("TDs unavailable for TDD check")
}
if td.Cmp(ttd) < 0 || (block.NumberU64() > 0 && ptd.Cmp(ttd) > 0) {
if td.Cmp(ttd) < 0 {
log.Error("Refusing beacon update to pre-merge", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
return beacon.ForkChoiceResponse{PayloadStatus: beacon.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
}
if block.NumberU64() > 0 && ptd.Cmp(ttd) >= 0 {
log.Error("Parent block is already post-ttd", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
return beacon.ForkChoiceResponse{PayloadStatus: beacon.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
}
}
valid := func(id *beacon.PayloadID) beacon.ForkChoiceResponse {
return beacon.ForkChoiceResponse{
PayloadStatus: beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &update.HeadBlockHash},
PayloadID: id,
}
}
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash {
// Block is not canonical, set head.
if latestValid, err := api.eth.BlockChain().SetCanonical(block); err != nil {
return beacon.ForkChoiceResponse{PayloadStatus: beacon.PayloadStatusV1{Status: beacon.INVALID, LatestValidHash: &latestValid}}, err
}
} else if api.eth.BlockChain().CurrentBlock().Hash() == update.HeadBlockHash {
// If the specified head matches with our local head, do nothing and keep
// generating the payload. It's a special corner case that a few slots are
// missing and we are requested to generate the payload in slot.
} else {
// If the head block is already in our canonical chain, the beacon client is
// probably resyncing. Ignore the update.
log.Info("Ignoring beacon update to old head", "number", block.NumberU64(), "hash", update.HeadBlockHash, "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)), "have", api.eth.BlockChain().CurrentBlock().NumberU64())
return valid(nil), nil
}
api.eth.SetSynced()
@@ -185,12 +235,8 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
log.Warn("Safe block not in canonical chain")
return beacon.STATUS_INVALID, beacon.InvalidForkChoiceState.With(errors.New("safe block not in canonical chain"))
}
}
valid := func(id *beacon.PayloadID) beacon.ForkChoiceResponse {
return beacon.ForkChoiceResponse{
PayloadStatus: beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &update.HeadBlockHash},
PayloadID: id,
}
// Set the safe block
api.eth.BlockChain().SetSafe(safeBlock)
}
// If payload generation was requested, create a new block to be potentially
// sealed by the beacon client. The payload will be requested later, and we
@@ -223,7 +269,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config beacon.Transit
return nil, errors.New("invalid terminal total difficulty")
}
ttd := api.eth.BlockChain().Config().TerminalTotalDifficulty
if ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
if ttd == nil || ttd.Cmp(config.TerminalTotalDifficulty.ToInt()) != 0 {
log.Warn("Invalid TTD configured", "geth", ttd, "beacon", config.TerminalTotalDifficulty)
return nil, fmt.Errorf("invalid ttd: execution %v consensus %v", ttd, config.TerminalTotalDifficulty)
}
@@ -266,6 +312,10 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
hash := block.Hash()
return beacon.PayloadStatusV1{Status: beacon.VALID, LatestValidHash: &hash}, nil
}
// If this block was rejected previously, keep rejecting it
if res := api.checkInvalidAncestor(block.Hash(), block.Hash()); res != nil {
return *res, nil
}
// If the parent is missing, we - in theory - could trigger a sync, but that
// would also entail a reorg. That is problematic if multiple sibling blocks
// are being fed to us, and even more so, if some semi-distant uncle shortens
@@ -274,37 +324,33 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
// update after legit payload executions.
parent := api.eth.BlockChain().GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
// Stash the block away for a potential forced forckchoice update to it
// at a later time.
api.remoteBlocks.put(block.Hash(), block.Header())
// Although we don't want to trigger a sync, if there is one already in
// progress, try to extend if with the current payload request to relieve
// some strain from the forkchoice update.
if err := api.eth.Downloader().BeaconExtend(api.eth.SyncMode(), block.Header()); err == nil {
log.Debug("Payload accepted for sync extension", "number", params.Number, "hash", params.BlockHash)
return beacon.PayloadStatusV1{Status: beacon.SYNCING}, nil
}
// Either no beacon sync was started yet, or it rejected the delivered
// payload as non-integratable on top of the existing sync. We'll just
// have to rely on the beacon client to forcefully update the head with
// a forkchoice update request.
log.Warn("Ignoring payload with missing parent", "number", params.Number, "hash", params.BlockHash, "parent", params.ParentHash)
return beacon.PayloadStatusV1{Status: beacon.ACCEPTED}, nil
return api.delayPayloadImport(block)
}
// We have an existing parent, do some sanity checks to avoid the beacon client
// triggering too early
var (
td = api.eth.BlockChain().GetTd(parent.Hash(), parent.NumberU64())
ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty
ptd = api.eth.BlockChain().GetTd(parent.Hash(), parent.NumberU64())
ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty
gptd = api.eth.BlockChain().GetTd(parent.ParentHash(), parent.NumberU64()-1)
)
if td.Cmp(ttd) < 0 {
log.Warn("Ignoring pre-merge payload", "number", params.Number, "hash", params.BlockHash, "td", td, "ttd", ttd)
if ptd.Cmp(ttd) < 0 {
log.Warn("Ignoring pre-merge payload", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
return beacon.INVALID_TERMINAL_BLOCK, nil
}
if parent.Difficulty().BitLen() > 0 && gptd != nil && gptd.Cmp(ttd) >= 0 {
log.Error("Ignoring pre-merge parent block", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
return beacon.INVALID_TERMINAL_BLOCK, nil
}
if block.Time() <= parent.Time() {
log.Warn("Invalid timestamp", "parent", block.Time(), "block", block.Time())
return api.invalid(errors.New("invalid timestamp"), parent), nil
return api.invalid(errors.New("invalid timestamp"), parent.Header()), nil
}
// Another cornercase: if the node is in snap sync mode, but the CL client
// tries to make it import a block. That should be denied as pushing something
// into the database directly will conflict with the assumptions of snap sync
// that it has an empty db that it can fill itself.
if api.eth.SyncMode() != downloader.FullSync {
return api.delayPayloadImport(block)
}
if !api.eth.BlockChain().HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
api.remoteBlocks.put(block.Hash(), block.Header())
@@ -314,7 +360,13 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number)
if err := api.eth.BlockChain().InsertBlockWithoutSetHead(block); err != nil {
log.Warn("NewPayloadV1: inserting block failed", "error", err)
return api.invalid(err, parent), nil
api.invalidLock.Lock()
api.invalidBlocksHits[block.Hash()] = 1
api.invalidTipsets[block.Hash()] = block.Header()
api.invalidLock.Unlock()
return api.invalid(err, parent.Header()), nil
}
// We've accepted a valid payload from the beacon client. Mark the local
// chain transitions to notify other subsystems (e.g. downloader) of the
@@ -340,12 +392,102 @@ func computePayloadId(headBlockHash common.Hash, params *beacon.PayloadAttribute
return out
}
// delayPayloadImport stashes the given block away for import at a later time,
// either via a forkchoice update or a sync extension. This method is meant to
// be called by the newpayload command when the block seems to be ok, but some
// prerequisite prevents it from being processed (e.g. no parent, or snap sync).
func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (beacon.PayloadStatusV1, error) {
// Sanity check that this block's parent is not on a previously invalidated
// chain. If it is, mark the block as invalid too.
if res := api.checkInvalidAncestor(block.ParentHash(), block.Hash()); res != nil {
return *res, nil
}
// Stash the block away for a potential forced forkchoice update to it
// at a later time.
api.remoteBlocks.put(block.Hash(), block.Header())
// Although we don't want to trigger a sync, if there is one already in
// progress, try to extend if with the current payload request to relieve
// some strain from the forkchoice update.
if err := api.eth.Downloader().BeaconExtend(api.eth.SyncMode(), block.Header()); err == nil {
log.Debug("Payload accepted for sync extension", "number", block.NumberU64(), "hash", block.Hash())
return beacon.PayloadStatusV1{Status: beacon.SYNCING}, nil
}
// Either no beacon sync was started yet, or it rejected the delivered
// payload as non-integratable on top of the existing sync. We'll just
// have to rely on the beacon client to forcefully update the head with
// a forkchoice update request.
log.Warn("Ignoring payload with missing parent", "number", block.NumberU64(), "hash", block.Hash(), "parent", block.ParentHash())
return beacon.PayloadStatusV1{Status: beacon.ACCEPTED}, nil
}
// setInvalidAncestor is a callback for the downloader to notify us if a bad block
// is encountered during the async sync.
func (api *ConsensusAPI) setInvalidAncestor(invalid *types.Header, origin *types.Header) {
api.invalidLock.Lock()
defer api.invalidLock.Unlock()
api.invalidTipsets[origin.Hash()] = invalid
api.invalidBlocksHits[invalid.Hash()]++
}
// checkInvalidAncestor checks whether the specified chain end links to a known
// bad ancestor. If yes, it constructs the payload failure response to return.
func (api *ConsensusAPI) checkInvalidAncestor(check common.Hash, head common.Hash) *beacon.PayloadStatusV1 {
api.invalidLock.Lock()
defer api.invalidLock.Unlock()
// If the hash to check is unknown, return valid
invalid, ok := api.invalidTipsets[check]
if !ok {
return nil
}
// If the bad hash was hit too many times, evict it and try to reprocess in
// the hopes that we have a data race that we can exit out of.
badHash := invalid.Hash()
api.invalidBlocksHits[badHash]++
if api.invalidBlocksHits[badHash] >= invalidBlockHitEviction {
log.Warn("Too many bad block import attempt, trying", "number", invalid.Number, "hash", badHash)
delete(api.invalidBlocksHits, badHash)
for descendant, badHeader := range api.invalidTipsets {
if badHeader.Hash() == badHash {
delete(api.invalidTipsets, descendant)
}
}
return nil
}
// Not too many failures yet, mark the head of the invalid chain as invalid
if check != head {
log.Warn("Marked new chain head as invalid", "hash", head, "badnumber", invalid.Number, "badhash", badHash)
for len(api.invalidTipsets) >= invalidTipsetsCap {
for key := range api.invalidTipsets {
delete(api.invalidTipsets, key)
break
}
}
api.invalidTipsets[head] = invalid
}
failure := "links to previously rejected block"
return &beacon.PayloadStatusV1{
Status: beacon.INVALID,
LatestValidHash: &invalid.ParentHash,
ValidationError: &failure,
}
}
// invalid returns a response "INVALID" with the latest valid hash supplied by latest or to the current head
// if no latestValid block was provided.
func (api *ConsensusAPI) invalid(err error, latestValid *types.Block) beacon.PayloadStatusV1 {
func (api *ConsensusAPI) invalid(err error, latestValid *types.Header) beacon.PayloadStatusV1 {
currentHash := api.eth.BlockChain().CurrentBlock().Hash()
if latestValid != nil {
currentHash = latestValid.Hash()
// Set latest valid hash to 0x0 if parent is PoW block
currentHash = common.Hash{}
if latestValid.Difficulty.BitLen() == 0 {
// Otherwise set latest valid hash to parent hash
currentHash = latestValid.Hash()
}
}
errorMsg := err.Error()
return beacon.PayloadStatusV1{Status: beacon.INVALID, LatestValidHash: &currentHash, ValidationError: &errorMsg}
+78 -13
View File
@@ -205,7 +205,6 @@ func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan co
func TestInvalidPayloadTimestamp(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(10)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
ethservice.Merger().ReachTTD()
defer n.Close()
var (
api = NewConsensusAPI(ethservice)
@@ -250,7 +249,6 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
func TestEth2NewBlock(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(10)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
ethservice.Merger().ReachTTD()
defer n.Close()
var (
@@ -405,7 +403,7 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
t.Fatal("can't create node:", err)
}
ethcfg := &ethconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, SyncMode: downloader.SnapSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256}
ethcfg := &ethconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256}
ethservice, err := eth.New(n, ethcfg)
if err != nil {
t.Fatal("can't create eth service:", err)
@@ -427,7 +425,6 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
func TestFullAPI(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(10)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
ethservice.Merger().ReachTTD()
defer n.Close()
var (
parent = ethservice.BlockChain().CurrentBlock()
@@ -480,7 +477,6 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Bl
func TestExchangeTransitionConfig(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(10)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
ethservice.Merger().ReachTTD()
defer n.Close()
var (
api = NewConsensusAPI(ethservice)
@@ -543,7 +539,6 @@ CommonAncestor◄─▲── P1 ◄── P2 ◄─ P3 ◄─ ... ◄─ Pn
func TestNewPayloadOnInvalidChain(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(10)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
ethservice.Merger().ReachTTD()
defer n.Close()
var (
@@ -618,7 +613,6 @@ func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.Pay
func TestEmptyBlocks(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(10)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
ethservice.Merger().ReachTTD()
defer n.Close()
commonAncestor := ethservice.BlockChain().CurrentBlock()
@@ -653,7 +647,8 @@ func TestEmptyBlocks(t *testing.T) {
if status.Status != beacon.INVALID {
t.Errorf("invalid status: expected INVALID got: %v", status.Status)
}
expected := commonAncestor.Hash()
// Expect 0x0 on INVALID block on top of PoW block
expected := common.Hash{}
if !bytes.Equal(status.LatestValidHash[:], expected[:]) {
t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, expected)
}
@@ -734,8 +729,6 @@ func TestTrickRemoteBlockCache(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(10)
nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
ethserviceA.Merger().ReachTTD()
ethserviceB.Merger().ReachTTD()
defer nodeA.Close()
defer nodeB.Close()
for nodeB.Server().NodeInfo().Ports.Listener == 0 {
@@ -780,8 +773,8 @@ func TestTrickRemoteBlockCache(t *testing.T) {
if err != nil {
panic(err)
}
if status.Status == beacon.INVALID {
panic("success")
if status.Status == beacon.VALID {
t.Error("invalid status: VALID on an invalid chain")
}
// Now reorg to the head of the invalid chain
resp, err := apiB.ForkchoiceUpdatedV1(beacon.ForkchoiceStateV1{HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.BlockHash, FinalizedBlockHash: payload.ParentHash}, nil)
@@ -789,8 +782,80 @@ func TestTrickRemoteBlockCache(t *testing.T) {
t.Fatal(err)
}
if resp.PayloadStatus.Status == beacon.VALID {
t.Errorf("invalid status: expected INVALID got: %v", resp.PayloadStatus.Status)
t.Error("invalid status: VALID on an invalid chain")
}
time.Sleep(100 * time.Millisecond)
}
}
func TestInvalidBloom(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(10)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
ethservice.Merger().ReachTTD()
defer n.Close()
commonAncestor := ethservice.BlockChain().CurrentBlock()
api := NewConsensusAPI(ethservice)
// Setup 10 blocks on the canonical chain
setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Block) {})
// (1) check LatestValidHash by sending a normal payload (P1'')
payload := getNewPayload(t, api, commonAncestor)
payload.LogsBloom = append(payload.LogsBloom, byte(1))
status, err := api.NewPayloadV1(*payload)
if err != nil {
t.Fatal(err)
}
if status.Status != beacon.INVALIDBLOCKHASH {
t.Errorf("invalid status: expected VALID got: %v", status.Status)
}
}
func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
genesis, preMergeBlocks := generatePreMergeChain(100)
fmt.Println(genesis.Config.TerminalTotalDifficulty)
genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty())
fmt.Println(genesis.Config.TerminalTotalDifficulty)
n, ethservice := startEthService(t, genesis, preMergeBlocks)
defer n.Close()
var (
api = NewConsensusAPI(ethservice)
parent = preMergeBlocks[len(preMergeBlocks)-1]
)
// Test parent already post TTD in FCU
fcState := beacon.ForkchoiceStateV1{
HeadBlockHash: parent.Hash(),
SafeBlockHash: common.Hash{},
FinalizedBlockHash: common.Hash{},
}
resp, err := api.ForkchoiceUpdatedV1(fcState, nil)
if err != nil {
t.Fatalf("error sending forkchoice, err=%v", err)
}
if resp.PayloadStatus != beacon.INVALID_TERMINAL_BLOCK {
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
}
// Test parent already post TTD in NewPayload
params := beacon.PayloadAttributesV1{
Timestamp: parent.Time() + 1,
Random: crypto.Keccak256Hash([]byte{byte(1)}),
SuggestedFeeRecipient: parent.Coinbase(),
}
empty, err := api.eth.Miner().GetSealingBlockSync(parent.Hash(), params.Timestamp, params.SuggestedFeeRecipient, params.Random, true)
if err != nil {
t.Fatalf("error preparing payload, err=%v", err)
}
data := *beacon.BlockToExecutableData(empty)
resp2, err := api.NewPayloadV1(data)
if err != nil {
t.Fatalf("error sending NewPayload, err=%v", err)
}
if resp2 != beacon.INVALID_TERMINAL_BLOCK {
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
}
}
-63
View File
@@ -1,63 +0,0 @@
// Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package eth
import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/rlp"
)
// ethEntry is the "eth" ENR entry which advertises eth protocol
// on the discovery network.
type ethEntry struct {
ForkID forkid.ID // Fork identifier per EIP-2124
// Ignore additional fields (for forward compatibility).
Rest []rlp.RawValue `rlp:"tail"`
}
// ENRKey implements enr.Entry.
func (e ethEntry) ENRKey() string {
return "eth"
}
// startEthEntryUpdate starts the ENR updater loop.
func (eth *Ethereum) startEthEntryUpdate(ln *enode.LocalNode) {
var newHead = make(chan core.ChainHeadEvent, 10)
sub := eth.blockchain.SubscribeChainHeadEvent(newHead)
go func() {
defer sub.Unsubscribe()
for {
select {
case <-newHead:
ln.Set(eth.currentEthEntry())
case <-sub.Err():
// Would be nice to sync with eth.Stop, but there is no
// good way to do that.
return
}
}
}()
}
func (eth *Ethereum) currentEthEntry() *ethEntry {
return &ethEntry{ForkID: forkid.NewID(eth.blockchain.Config(), eth.blockchain.Genesis().Hash(),
eth.blockchain.CurrentHeader().Number.Uint64())}
}
+12 -12
View File
@@ -25,21 +25,21 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)
// PublicDownloaderAPI provides an API which gives information about the current synchronisation status.
// DownloaderAPI provides an API which gives information about the current synchronisation status.
// It offers only methods that operates on data that can be available to anyone without security risks.
type PublicDownloaderAPI struct {
type DownloaderAPI struct {
d *Downloader
mux *event.TypeMux
installSyncSubscription chan chan interface{}
uninstallSyncSubscription chan *uninstallSyncSubscriptionRequest
}
// NewPublicDownloaderAPI create a new PublicDownloaderAPI. The API has an internal event loop that
// NewDownloaderAPI create a new DownloaderAPI. The API has an internal event loop that
// listens for events from the downloader through the global event mux. In case it receives one of
// these events it broadcasts it to all syncing subscriptions that are installed through the
// installSyncSubscription channel.
func NewPublicDownloaderAPI(d *Downloader, m *event.TypeMux) *PublicDownloaderAPI {
api := &PublicDownloaderAPI{
func NewDownloaderAPI(d *Downloader, m *event.TypeMux) *DownloaderAPI {
api := &DownloaderAPI{
d: d,
mux: m,
installSyncSubscription: make(chan chan interface{}),
@@ -53,7 +53,7 @@ func NewPublicDownloaderAPI(d *Downloader, m *event.TypeMux) *PublicDownloaderAP
// eventLoop runs a loop until the event mux closes. It will install and uninstall new
// sync subscriptions and broadcasts sync status updates to the installed sync subscriptions.
func (api *PublicDownloaderAPI) eventLoop() {
func (api *DownloaderAPI) eventLoop() {
var (
sub = api.mux.Subscribe(StartEvent{}, DoneEvent{}, FailedEvent{})
syncSubscriptions = make(map[chan interface{}]struct{})
@@ -90,7 +90,7 @@ func (api *PublicDownloaderAPI) eventLoop() {
}
// Syncing provides information when this nodes starts synchronising with the Ethereum network and when it's finished.
func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error) {
func (api *DownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
@@ -133,9 +133,9 @@ type uninstallSyncSubscriptionRequest struct {
// SyncStatusSubscription represents a syncing subscription.
type SyncStatusSubscription struct {
api *PublicDownloaderAPI // register subscription in event loop of this api instance
c chan interface{} // channel where events are broadcasted to
unsubOnce sync.Once // make sure unsubscribe logic is executed once
api *DownloaderAPI // register subscription in event loop of this api instance
c chan interface{} // channel where events are broadcasted to
unsubOnce sync.Once // make sure unsubscribe logic is executed once
}
// Unsubscribe uninstalls the subscription from the DownloadAPI event loop.
@@ -159,8 +159,8 @@ func (s *SyncStatusSubscription) Unsubscribe() {
}
// SubscribeSyncStatus creates a subscription that will broadcast new synchronisation updates.
// The given channel must receive interface values, the result can either
func (api *PublicDownloaderAPI) SubscribeSyncStatus(status chan interface{}) *SyncStatusSubscription {
// The given channel must receive interface values, the result can either.
func (api *DownloaderAPI) SubscribeSyncStatus(status chan interface{}) *SyncStatusSubscription {
api.installSyncSubscription <- status
return &SyncStatusSubscription{api: api, c: status}
}
+7
View File
@@ -137,6 +137,13 @@ func (b *beaconBackfiller) setMode(mode SyncMode) {
b.resume()
}
// SetBadBlockCallback sets the callback to run when a bad block is hit by the
// block processor. This method is not thread safe and should be set only once
// on startup before system events are fired.
func (d *Downloader) SetBadBlockCallback(onBadBlock badBlockFn) {
d.badBlock = onBadBlock
}
// BeaconSync is the post-merge version of the chain synchronization, where the
// chain is not downloaded from genesis onward, rather from trusted head announces
// backwards.
+16 -2
View File
@@ -85,6 +85,10 @@ var (
// peerDropFn is a callback type for dropping a peer detected as malicious.
type peerDropFn func(id string)
// badBlockFn is a callback for the async beacon sync to notify the caller that
// the origin header requested to sync to, produced a chain with a bad block.
type badBlockFn func(invalid *types.Header, origin *types.Header)
// headerTask is a set of downloaded headers to queue along with their precomputed
// hashes to avoid constant rehashing.
type headerTask struct {
@@ -113,6 +117,7 @@ type Downloader struct {
// Callbacks
dropPeer peerDropFn // Drops a peer for misbehaving
badBlock badBlockFn // Reports a block as rejected by the chain
// Status
synchroniseMock func(id string, hash common.Hash) error // Replacement for synchronise during testing
@@ -131,7 +136,6 @@ type Downloader struct {
pivotHeader *types.Header // Pivot block header to dynamically push the syncing state root
pivotLock sync.RWMutex // Lock protecting pivot header reads from updates
snapSync bool // Whether to run state sync over the snap protocol
SnapSyncer *snap.Syncer // TODO(karalabe): make private! hack for now
stateSyncStart chan *stateSync
@@ -1529,7 +1533,7 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
return errCancelContentProcessing
default:
}
// Retrieve the a batch of results to import
// Retrieve a batch of results to import
first, last := results[0].Header, results[len(results)-1].Header
log.Debug("Inserting downloaded chain", "items", len(results),
"firstnum", first.Number, "firsthash", first.Hash(),
@@ -1545,6 +1549,16 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
if index, err := d.blockchain.InsertChain(blocks); err != nil {
if index < len(results) {
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
// In post-merge, notify the engine API of encountered bad chains
if d.badBlock != nil {
head, _, err := d.skeleton.Bounds()
if err != nil {
log.Error("Failed to retrieve beacon bounds for bad block reporting", "err", err)
} else {
d.badBlock(blocks[index].Header(), head)
}
}
} else {
// The InsertChain method in blockchain.go will sometimes return an out-of-bounds index,
// when it needs to preprocess blocks to import a sidechain.
+71 -2
View File
@@ -437,6 +437,9 @@ func assertOwnChain(t *testing.T, tester *downloadTester, length int) {
func TestCanonicalSynchronisation66Full(t *testing.T) { testCanonSync(t, eth.ETH66, FullSync) }
func TestCanonicalSynchronisation66Snap(t *testing.T) { testCanonSync(t, eth.ETH66, SnapSync) }
func TestCanonicalSynchronisation66Light(t *testing.T) { testCanonSync(t, eth.ETH66, LightSync) }
func TestCanonicalSynchronisation67Full(t *testing.T) { testCanonSync(t, eth.ETH67, FullSync) }
func TestCanonicalSynchronisation67Snap(t *testing.T) { testCanonSync(t, eth.ETH67, SnapSync) }
func TestCanonicalSynchronisation67Light(t *testing.T) { testCanonSync(t, eth.ETH67, LightSync) }
func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -457,6 +460,8 @@ func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
// until the cached blocks are retrieved.
func TestThrottling66Full(t *testing.T) { testThrottling(t, eth.ETH66, FullSync) }
func TestThrottling66Snap(t *testing.T) { testThrottling(t, eth.ETH66, SnapSync) }
func TestThrottling67Full(t *testing.T) { testThrottling(t, eth.ETH67, FullSync) }
func TestThrottling67Snap(t *testing.T) { testThrottling(t, eth.ETH67, SnapSync) }
func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -537,6 +542,9 @@ func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
func TestForkedSync66Full(t *testing.T) { testForkedSync(t, eth.ETH66, FullSync) }
func TestForkedSync66Snap(t *testing.T) { testForkedSync(t, eth.ETH66, SnapSync) }
func TestForkedSync66Light(t *testing.T) { testForkedSync(t, eth.ETH66, LightSync) }
func TestForkedSync67Full(t *testing.T) { testForkedSync(t, eth.ETH67, FullSync) }
func TestForkedSync67Snap(t *testing.T) { testForkedSync(t, eth.ETH67, SnapSync) }
func TestForkedSync67Light(t *testing.T) { testForkedSync(t, eth.ETH67, LightSync) }
func testForkedSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -564,6 +572,9 @@ func testForkedSync(t *testing.T, protocol uint, mode SyncMode) {
func TestHeavyForkedSync66Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FullSync) }
func TestHeavyForkedSync66Snap(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, SnapSync) }
func TestHeavyForkedSync66Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, LightSync) }
func TestHeavyForkedSync67Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH67, FullSync) }
func TestHeavyForkedSync67Snap(t *testing.T) { testHeavyForkedSync(t, eth.ETH67, SnapSync) }
func TestHeavyForkedSync67Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH67, LightSync) }
func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -593,6 +604,9 @@ func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
func TestBoundedForkedSync66Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FullSync) }
func TestBoundedForkedSync66Snap(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, SnapSync) }
func TestBoundedForkedSync66Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, LightSync) }
func TestBoundedForkedSync67Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH67, FullSync) }
func TestBoundedForkedSync67Snap(t *testing.T) { testBoundedForkedSync(t, eth.ETH67, SnapSync) }
func TestBoundedForkedSync67Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH67, LightSync) }
func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -627,6 +641,15 @@ func TestBoundedHeavyForkedSync66Snap(t *testing.T) {
func TestBoundedHeavyForkedSync66Light(t *testing.T) {
testBoundedHeavyForkedSync(t, eth.ETH66, LightSync)
}
func TestBoundedHeavyForkedSync67Full(t *testing.T) {
testBoundedHeavyForkedSync(t, eth.ETH67, FullSync)
}
func TestBoundedHeavyForkedSync67Snap(t *testing.T) {
testBoundedHeavyForkedSync(t, eth.ETH67, SnapSync)
}
func TestBoundedHeavyForkedSync67Light(t *testing.T) {
testBoundedHeavyForkedSync(t, eth.ETH67, LightSync)
}
func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -654,6 +677,9 @@ func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
func TestCancel66Full(t *testing.T) { testCancel(t, eth.ETH66, FullSync) }
func TestCancel66Snap(t *testing.T) { testCancel(t, eth.ETH66, SnapSync) }
func TestCancel66Light(t *testing.T) { testCancel(t, eth.ETH66, LightSync) }
func TestCancel67Full(t *testing.T) { testCancel(t, eth.ETH67, FullSync) }
func TestCancel67Snap(t *testing.T) { testCancel(t, eth.ETH67, SnapSync) }
func TestCancel67Light(t *testing.T) { testCancel(t, eth.ETH67, LightSync) }
func testCancel(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -681,6 +707,9 @@ func testCancel(t *testing.T, protocol uint, mode SyncMode) {
func TestMultiSynchronisation66Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FullSync) }
func TestMultiSynchronisation66Snap(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, SnapSync) }
func TestMultiSynchronisation66Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, LightSync) }
func TestMultiSynchronisation67Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH67, FullSync) }
func TestMultiSynchronisation67Snap(t *testing.T) { testMultiSynchronisation(t, eth.ETH67, SnapSync) }
func TestMultiSynchronisation67Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH67, LightSync) }
func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -705,6 +734,9 @@ func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) {
func TestMultiProtoSynchronisation66Full(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FullSync) }
func TestMultiProtoSynchronisation66Snap(t *testing.T) { testMultiProtoSync(t, eth.ETH66, SnapSync) }
func TestMultiProtoSynchronisation66Light(t *testing.T) { testMultiProtoSync(t, eth.ETH66, LightSync) }
func TestMultiProtoSynchronisation67Full(t *testing.T) { testMultiProtoSync(t, eth.ETH67, FullSync) }
func TestMultiProtoSynchronisation67Snap(t *testing.T) { testMultiProtoSync(t, eth.ETH67, SnapSync) }
func TestMultiProtoSynchronisation67Light(t *testing.T) { testMultiProtoSync(t, eth.ETH67, LightSync) }
func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -715,7 +747,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
// Create peers of every type
tester.newPeer("peer 66", eth.ETH66, chain.blocks[1:])
//tester.newPeer("peer 65", eth.ETH67, chain.blocks[1:)
tester.newPeer("peer 67", eth.ETH67, chain.blocks[1:])
// Synchronise with the requested peer and make sure all blocks were retrieved
if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil {
@@ -724,7 +756,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
assertOwnChain(t, tester, len(chain.blocks))
// Check that no peers have been dropped off
for _, version := range []int{66} {
for _, version := range []int{66, 67} {
peer := fmt.Sprintf("peer %d", version)
if _, ok := tester.peers[peer]; !ok {
t.Errorf("%s dropped", peer)
@@ -737,6 +769,9 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
func TestEmptyShortCircuit66Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FullSync) }
func TestEmptyShortCircuit66Snap(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, SnapSync) }
func TestEmptyShortCircuit66Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, LightSync) }
func TestEmptyShortCircuit67Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH67, FullSync) }
func TestEmptyShortCircuit67Snap(t *testing.T) { testEmptyShortCircuit(t, eth.ETH67, SnapSync) }
func TestEmptyShortCircuit67Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH67, LightSync) }
func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -785,6 +820,9 @@ func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
func TestMissingHeaderAttack66Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FullSync) }
func TestMissingHeaderAttack66Snap(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, SnapSync) }
func TestMissingHeaderAttack66Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, LightSync) }
func TestMissingHeaderAttack67Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH67, FullSync) }
func TestMissingHeaderAttack67Snap(t *testing.T) { testMissingHeaderAttack(t, eth.ETH67, SnapSync) }
func TestMissingHeaderAttack67Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH67, LightSync) }
func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -811,6 +849,9 @@ func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
func TestShiftedHeaderAttack66Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FullSync) }
func TestShiftedHeaderAttack66Snap(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, SnapSync) }
func TestShiftedHeaderAttack66Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, LightSync) }
func TestShiftedHeaderAttack67Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH67, FullSync) }
func TestShiftedHeaderAttack67Snap(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH67, SnapSync) }
func TestShiftedHeaderAttack67Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH67, LightSync) }
func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -837,6 +878,7 @@ func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
// for various failure scenarios. Afterwards a full sync is attempted to make
// sure no state was corrupted.
func TestInvalidHeaderRollback66Snap(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, SnapSync) }
func TestInvalidHeaderRollback67Snap(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH67, SnapSync) }
func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -923,6 +965,15 @@ func TestHighTDStarvationAttack66Snap(t *testing.T) {
func TestHighTDStarvationAttack66Light(t *testing.T) {
testHighTDStarvationAttack(t, eth.ETH66, LightSync)
}
func TestHighTDStarvationAttack67Full(t *testing.T) {
testHighTDStarvationAttack(t, eth.ETH67, FullSync)
}
func TestHighTDStarvationAttack67Snap(t *testing.T) {
testHighTDStarvationAttack(t, eth.ETH67, SnapSync)
}
func TestHighTDStarvationAttack67Light(t *testing.T) {
testHighTDStarvationAttack(t, eth.ETH67, LightSync)
}
func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -937,6 +988,7 @@ func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) {
// Tests that misbehaving peers are disconnected, whilst behaving ones are not.
func TestBlockHeaderAttackerDropping66(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH66) }
func TestBlockHeaderAttackerDropping67(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH67) }
func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) {
// Define the disconnection requirement for individual hash fetch errors
@@ -987,6 +1039,9 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) {
func TestSyncProgress66Full(t *testing.T) { testSyncProgress(t, eth.ETH66, FullSync) }
func TestSyncProgress66Snap(t *testing.T) { testSyncProgress(t, eth.ETH66, SnapSync) }
func TestSyncProgress66Light(t *testing.T) { testSyncProgress(t, eth.ETH66, LightSync) }
func TestSyncProgress67Full(t *testing.T) { testSyncProgress(t, eth.ETH67, FullSync) }
func TestSyncProgress67Snap(t *testing.T) { testSyncProgress(t, eth.ETH67, SnapSync) }
func TestSyncProgress67Light(t *testing.T) { testSyncProgress(t, eth.ETH67, LightSync) }
func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -1064,6 +1119,9 @@ func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.Sync
func TestForkedSyncProgress66Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FullSync) }
func TestForkedSyncProgress66Snap(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, SnapSync) }
func TestForkedSyncProgress66Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, LightSync) }
func TestForkedSyncProgress67Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH67, FullSync) }
func TestForkedSyncProgress67Snap(t *testing.T) { testForkedSyncProgress(t, eth.ETH67, SnapSync) }
func TestForkedSyncProgress67Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH67, LightSync) }
func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -1135,6 +1193,9 @@ func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
func TestFailedSyncProgress66Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FullSync) }
func TestFailedSyncProgress66Snap(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, SnapSync) }
func TestFailedSyncProgress66Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, LightSync) }
func TestFailedSyncProgress67Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH67, FullSync) }
func TestFailedSyncProgress67Snap(t *testing.T) { testFailedSyncProgress(t, eth.ETH67, SnapSync) }
func TestFailedSyncProgress67Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH67, LightSync) }
func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -1201,6 +1262,9 @@ func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
func TestFakedSyncProgress66Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FullSync) }
func TestFakedSyncProgress66Snap(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, SnapSync) }
func TestFakedSyncProgress66Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, LightSync) }
func TestFakedSyncProgress67Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH67, FullSync) }
func TestFakedSyncProgress67Snap(t *testing.T) { testFakedSyncProgress(t, eth.ETH67, SnapSync) }
func TestFakedSyncProgress67Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH67, LightSync) }
func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t)
@@ -1347,6 +1411,11 @@ func TestCheckpointEnforcement66Snap(t *testing.T) { testCheckpointEnforcement(t
func TestCheckpointEnforcement66Light(t *testing.T) {
testCheckpointEnforcement(t, eth.ETH66, LightSync)
}
func TestCheckpointEnforcement67Full(t *testing.T) { testCheckpointEnforcement(t, eth.ETH67, FullSync) }
func TestCheckpointEnforcement67Snap(t *testing.T) { testCheckpointEnforcement(t, eth.ETH67, SnapSync) }
func TestCheckpointEnforcement67Light(t *testing.T) {
testCheckpointEnforcement(t, eth.ETH67, LightSync)
}
func testCheckpointEnforcement(t *testing.T, protocol uint, mode SyncMode) {
// Create a new tester with a particular hard coded checkpoint block
-1
View File
@@ -817,7 +817,6 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
reqTimer metrics.Timer, resInMeter metrics.Meter, resDropMeter metrics.Meter,
results int, validate func(index int, header *types.Header) error,
reconstruct func(index int, result *fetchResult)) (int, error) {
// Short circuit if the data was never requested
request := pendPool[id]
if request == nil {
-8
View File
@@ -185,7 +185,6 @@ func TestBasics(t *testing.T) {
if got, exp := fetchReq.Headers[0].Number.Uint64(), uint64(1); got != exp {
t.Fatalf("expected header %d, got %d", exp, got)
}
}
if exp, got := q.blockTaskQueue.Size(), numOfBlocks-10; exp != got {
t.Errorf("expected block task queue to be %d, got %d", exp, got)
@@ -239,7 +238,6 @@ func TestEmptyBlocks(t *testing.T) {
if fetchReq != nil {
t.Fatal("there should be no body fetch tasks remaining")
}
}
if q.blockTaskQueue.Size() != numOfBlocks-10 {
t.Errorf("expected block task queue to be %d, got %d", numOfBlocks-10, q.blockTaskQueue.Size())
@@ -280,7 +278,6 @@ func XTestDelivery(t *testing.T) {
world.progress(10)
if false {
log.Root().SetHandler(log.StdoutHandler)
}
q := newQueue(10, 10)
var wg sync.WaitGroup
@@ -315,7 +312,6 @@ func XTestDelivery(t *testing.T) {
fmt.Printf("got %d results, %d tot\n", len(res), tot)
// Now we can forget about these
world.forget(res[len(res)-1].Header.Number.Uint64())
}
}()
wg.Add(1)
@@ -396,7 +392,6 @@ func XTestDelivery(t *testing.T) {
}
for i := 0; i < 50; i++ {
time.Sleep(2990 * time.Millisecond)
}
}()
wg.Add(1)
@@ -447,10 +442,8 @@ func (n *network) forget(blocknum uint64) {
n.chain = n.chain[index:]
n.receipts = n.receipts[index:]
n.offset = int(blocknum)
}
func (n *network) progress(numBlocks int) {
n.lock.Lock()
defer n.lock.Unlock()
//fmt.Printf("progressing...\n")
@@ -458,7 +451,6 @@ func (n *network) progress(numBlocks int) {
n.chain = append(n.chain, newBlocks...)
n.receipts = append(n.receipts, newR...)
n.cond.Broadcast()
}
func (n *network) headers(from int) []*types.Header {
+1 -3
View File
@@ -21,7 +21,6 @@ import (
"errors"
"fmt"
"math/big"
"os"
"sync/atomic"
"testing"
"time"
@@ -515,7 +514,7 @@ func TestSkeletonSyncExtend(t *testing.T) {
// Tests that the skeleton sync correctly retrieves headers from one or more
// peers without duplicates or other strange side effects.
func TestSkeletonSyncRetrievals(t *testing.T) {
log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
// Since skeleton headers don't need to be meaningful, beyond a parent hash
// progression, create a long fake chain to test with.
@@ -791,7 +790,6 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
check := func() error {
if len(progress.Subchains) != len(tt.midstate) {
return fmt.Errorf("test %d, mid state: subchain count mismatch: have %d, want %d", i, len(progress.Subchains), len(tt.midstate))
}
for j := 0; j < len(progress.Subchains); j++ {
if progress.Subchains[j].Head != tt.midstate[j].Head {
+2 -2
View File
@@ -205,8 +205,8 @@ type Config struct {
// CheckpointOracle is the configuration for checkpoint oracle.
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
// Arrow Glacier block override (TODO: remove after the fork)
OverrideArrowGlacier *big.Int `toml:",omitempty"`
// Gray Glacier block override (TODO: remove after the fork)
OverrideGrayGlacier *big.Int `toml:",omitempty"`
// OverrideTerminalTotalDifficulty (TODO: remove after the fork)
OverrideTerminalTotalDifficulty *big.Int `toml:",omitempty"`
+5 -5
View File
@@ -59,7 +59,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
RPCTxFeeCap float64
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
OverrideArrowGlacier *big.Int `toml:",omitempty"`
OverrideGrayGlacier *big.Int `toml:",omitempty"`
OverrideTerminalTotalDifficulty *big.Int `toml:",omitempty"`
}
var enc Config
@@ -104,7 +104,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.RPCTxFeeCap = c.RPCTxFeeCap
enc.Checkpoint = c.Checkpoint
enc.CheckpointOracle = c.CheckpointOracle
enc.OverrideArrowGlacier = c.OverrideArrowGlacier
enc.OverrideGrayGlacier = c.OverrideGrayGlacier
enc.OverrideTerminalTotalDifficulty = c.OverrideTerminalTotalDifficulty
return &enc, nil
}
@@ -153,7 +153,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
RPCTxFeeCap *float64
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
OverrideArrowGlacier *big.Int `toml:",omitempty"`
OverrideGrayGlacier *big.Int `toml:",omitempty"`
OverrideTerminalTotalDifficulty *big.Int `toml:",omitempty"`
}
var dec Config
@@ -283,8 +283,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.CheckpointOracle != nil {
c.CheckpointOracle = dec.CheckpointOracle
}
if dec.OverrideArrowGlacier != nil {
c.OverrideArrowGlacier = dec.OverrideArrowGlacier
if dec.OverrideGrayGlacier != nil {
c.OverrideGrayGlacier = dec.OverrideGrayGlacier
}
if dec.OverrideTerminalTotalDifficulty != nil {
c.OverrideTerminalTotalDifficulty = dec.OverrideTerminalTotalDifficulty
-1
View File
@@ -692,7 +692,6 @@ func (f *BlockFetcher) loop() {
} else {
f.forgetHash(hash)
}
}
if matched {
task.transactions = append(task.transactions[:i], task.transactions[i+1:]...)
+17 -31
View File
@@ -43,9 +43,9 @@ type filter struct {
s *Subscription // associated subscription in event system
}
// PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various
// FilterAPI offers support to create and manage filters. This will allow external clients to retrieve various
// information related to the Ethereum protocol such als blocks, transactions and logs.
type PublicFilterAPI struct {
type FilterAPI struct {
backend Backend
events *EventSystem
filtersMu sync.Mutex
@@ -53,9 +53,9 @@ type PublicFilterAPI struct {
timeout time.Duration
}
// NewPublicFilterAPI returns a new PublicFilterAPI instance.
func NewPublicFilterAPI(backend Backend, lightMode bool, timeout time.Duration) *PublicFilterAPI {
api := &PublicFilterAPI{
// NewFilterAPI returns a new FilterAPI instance.
func NewFilterAPI(backend Backend, lightMode bool, timeout time.Duration) *FilterAPI {
api := &FilterAPI{
backend: backend,
events: NewEventSystem(backend, lightMode),
filters: make(map[rpc.ID]*filter),
@@ -68,7 +68,7 @@ func NewPublicFilterAPI(backend Backend, lightMode bool, timeout time.Duration)
// timeoutLoop runs at the interval set by 'timeout' and deletes filters
// that have not been recently used. It is started when the API is created.
func (api *PublicFilterAPI) timeoutLoop(timeout time.Duration) {
func (api *FilterAPI) timeoutLoop(timeout time.Duration) {
var toUninstall []*Subscription
ticker := time.NewTicker(timeout)
defer ticker.Stop()
@@ -101,9 +101,7 @@ func (api *PublicFilterAPI) timeoutLoop(timeout time.Duration) {
//
// It is part of the filter package because this filter can be used through the
// `eth_getFilterChanges` polling method that is also used for log filters.
//
// https://eth.wiki/json-rpc/API#eth_newpendingtransactionfilter
func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID {
func (api *FilterAPI) NewPendingTransactionFilter() rpc.ID {
var (
pendingTxs = make(chan []common.Hash)
pendingTxSub = api.events.SubscribePendingTxs(pendingTxs)
@@ -136,7 +134,7 @@ func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID {
// NewPendingTransactions creates a subscription that is triggered each time a transaction
// enters the transaction pool and was signed from one of the transactions this nodes manages.
func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscription, error) {
func (api *FilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
@@ -171,9 +169,7 @@ func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Su
// NewBlockFilter creates a filter that fetches blocks that are imported into the chain.
// It is part of the filter package since polling goes with eth_getFilterChanges.
//
// https://eth.wiki/json-rpc/API#eth_newblockfilter
func (api *PublicFilterAPI) NewBlockFilter() rpc.ID {
func (api *FilterAPI) NewBlockFilter() rpc.ID {
var (
headers = make(chan *types.Header)
headerSub = api.events.SubscribeNewHeads(headers)
@@ -205,7 +201,7 @@ func (api *PublicFilterAPI) NewBlockFilter() rpc.ID {
}
// NewHeads send a notification each time a new (header) block is appended to the chain.
func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) {
func (api *FilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
@@ -235,7 +231,7 @@ func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, er
}
// Logs creates a subscription that fires for all new log that match the given filter criteria.
func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subscription, error) {
func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
@@ -252,11 +248,11 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc
}
go func() {
for {
select {
case logs := <-matchedLogs:
for _, log := range logs {
log := log
notifier.Notify(rpcSub.ID, &log)
}
case <-rpcSub.Err(): // client send an unsubscribe request
@@ -287,9 +283,7 @@ type FilterCriteria ethereum.FilterQuery
// again but with the removed property set to true.
//
// In case "fromBlock" > "toBlock" an error is returned.
//
// https://eth.wiki/json-rpc/API#eth_newfilter
func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
func (api *FilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
logs := make(chan []*types.Log)
logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), logs)
if err != nil {
@@ -322,9 +316,7 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
}
// GetLogs returns logs matching the given argument that are stored within the state.
//
// https://eth.wiki/json-rpc/API#eth_getlogs
func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) {
func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) {
var filter *Filter
if crit.BlockHash != nil {
// Block filter requested, construct a single-shot filter
@@ -351,9 +343,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([
}
// UninstallFilter removes the filter with the given filter id.
//
// https://eth.wiki/json-rpc/API#eth_uninstallfilter
func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool {
func (api *FilterAPI) UninstallFilter(id rpc.ID) bool {
api.filtersMu.Lock()
f, found := api.filters[id]
if found {
@@ -369,9 +359,7 @@ func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool {
// GetFilterLogs returns the logs for the filter with the given id.
// If the filter could not be found an empty array of logs is returned.
//
// https://eth.wiki/json-rpc/API#eth_getfilterlogs
func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Log, error) {
func (api *FilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Log, error) {
api.filtersMu.Lock()
f, found := api.filters[id]
api.filtersMu.Unlock()
@@ -410,9 +398,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ty
//
// For pending transaction and block filters the result is []common.Hash.
// (pending)Log filters return []Log.
//
// https://eth.wiki/json-rpc/API#eth_getfilterchanges
func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
func (api *FilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
api.filtersMu.Lock()
defer api.filtersMu.Unlock()
+1 -1
View File
@@ -56,7 +56,7 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) {
// from, to block number
var test1 FilterCriteria
vector := fmt.Sprintf(`{"fromBlock":"0x%x","toBlock":"0x%x"}`, fromBlock, toBlock)
vector := fmt.Sprintf(`{"fromBlock":"%#x","toBlock":"%#x"}`, fromBlock, toBlock)
if err := json.Unmarshal([]byte(vector), &test1); err != nil {
t.Fatal(err)
}
+4 -4
View File
@@ -93,9 +93,9 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
var header *types.Header
for i := sectionIdx * sectionSize; i < (sectionIdx+1)*sectionSize; i++ {
hash := rawdb.ReadCanonicalHash(db, i)
header = rawdb.ReadHeader(db, hash, i)
if header == nil {
if header = rawdb.ReadHeader(db, hash, i); header == nil {
b.Fatalf("Error creating bloomBits data")
return
}
bc.AddBloom(uint(i-sectionIdx*sectionSize), header.Bloom)
}
@@ -144,9 +144,9 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
db.Close()
}
var bloomBitsPrefix = []byte("bloomBits-")
//nolint:unused
func clearBloomBits(db ethdb.Database) {
var bloomBitsPrefix = []byte("bloomBits-")
fmt.Println("Clearing bloombits data...")
it := db.NewIterator(bloomBitsPrefix, nil)
for it.Next() {
+38 -8
View File
@@ -36,6 +36,7 @@ type Backend interface {
HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error)
PendingBlockAndReceipts() (*types.Block, types.Receipts)
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
@@ -128,26 +129,35 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
}
return f.blockLogs(ctx, header)
}
// Short-cut if all we care about is pending logs
if f.begin == rpc.PendingBlockNumber.Int64() {
if f.end != rpc.PendingBlockNumber.Int64() {
return nil, errors.New("invalid block range")
}
return f.pendingLogs()
}
// Figure out the limits of the filter range
header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
if header == nil {
return nil, nil
}
head := header.Number.Uint64()
if f.begin == -1 {
var (
head = header.Number.Uint64()
end = uint64(f.end)
pending = f.end == rpc.PendingBlockNumber.Int64()
)
if f.begin == rpc.LatestBlockNumber.Int64() {
f.begin = int64(head)
}
end := uint64(f.end)
if f.end == -1 {
if f.end == rpc.LatestBlockNumber.Int64() || f.end == rpc.PendingBlockNumber.Int64() {
end = head
}
// Gather all indexed logs, and finish with non indexed ones
var (
logs []*types.Log
err error
logs []*types.Log
err error
size, sections = f.backend.BloomStatus()
)
size, sections := f.backend.BloomStatus()
if indexed := sections * size; indexed > uint64(f.begin) {
if indexed > end {
logs, err = f.indexedLogs(ctx, end)
@@ -160,6 +170,13 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
}
rest, err := f.unindexedLogs(ctx, end)
logs = append(logs, rest...)
if pending {
pendingLogs, err := f.pendingLogs()
if err != nil {
return nil, err
}
logs = append(logs, pendingLogs...)
}
return logs, err
}
@@ -272,6 +289,19 @@ func (f *Filter) checkMatches(ctx context.Context, header *types.Header) (logs [
return nil, nil
}
// pendingLogs returns the logs matching the filter criteria within the pending block.
func (f *Filter) pendingLogs() ([]*types.Log, error) {
block, receipts := f.backend.PendingBlockAndReceipts()
if bloomFilter(block.Bloom(), f.addresses, f.topics) {
var unfiltered []*types.Log
for _, r := range receipts {
unfiltered = append(unfiltered, r.Logs...)
}
return filterLogs(unfiltered, nil, nil, f.addresses, f.topics), nil
}
return nil, nil
}
func includes(addresses []common.Address, a common.Address) bool {
for _, addr := range addresses {
if addr == a {
+13 -10
View File
@@ -44,7 +44,6 @@ var (
)
type testBackend struct {
mux *event.TypeMux
db ethdb.Database
sections uint64
txFeed event.Feed
@@ -106,6 +105,10 @@ func (b *testBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types
return logs, nil
}
func (b *testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
return nil, nil
}
func (b *testBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return b.txFeed.Subscribe(ch)
}
@@ -168,7 +171,7 @@ func TestBlockSubscription(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
backend = &testBackend{db: db}
api = NewPublicFilterAPI(backend, false, deadline)
api = NewFilterAPI(backend, false, deadline)
genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {})
chainEvents = []core.ChainEvent{}
@@ -220,7 +223,7 @@ func TestPendingTxFilter(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
backend = &testBackend{db: db}
api = NewPublicFilterAPI(backend, false, deadline)
api = NewFilterAPI(backend, false, deadline)
transactions = []*types.Transaction{
types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
@@ -275,7 +278,7 @@ func TestLogFilterCreation(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
backend = &testBackend{db: db}
api = NewPublicFilterAPI(backend, false, deadline)
api = NewFilterAPI(backend, false, deadline)
testCases = []struct {
crit FilterCriteria
@@ -322,7 +325,7 @@ func TestInvalidLogFilterCreation(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
backend = &testBackend{db: db}
api = NewPublicFilterAPI(backend, false, deadline)
api = NewFilterAPI(backend, false, deadline)
)
// different situations where log filter creation should fail.
@@ -344,7 +347,7 @@ func TestInvalidGetLogsRequest(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
backend = &testBackend{db: db}
api = NewPublicFilterAPI(backend, false, deadline)
api = NewFilterAPI(backend, false, deadline)
blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")
)
@@ -369,7 +372,7 @@ func TestLogFilter(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
backend = &testBackend{db: db}
api = NewPublicFilterAPI(backend, false, deadline)
api = NewFilterAPI(backend, false, deadline)
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
@@ -483,7 +486,7 @@ func TestPendingLogsSubscription(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
backend = &testBackend{db: db}
api = NewPublicFilterAPI(backend, false, deadline)
api = NewFilterAPI(backend, false, deadline)
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
@@ -592,7 +595,7 @@ func TestPendingLogsSubscription(t *testing.T) {
// (some) events are posted.
for i := range testCases {
testCases[i].c = make(chan []*types.Log)
testCases[i].err = make(chan error)
testCases[i].err = make(chan error, 1)
var err error
testCases[i].sub, err = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
@@ -667,7 +670,7 @@ func TestPendingTxFilterDeadlock(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
backend = &testBackend{db: db}
api = NewPublicFilterAPI(backend, false, timeout)
api = NewFilterAPI(backend, false, timeout)
done = make(chan struct{})
)
-1
View File
@@ -72,7 +72,6 @@ func BenchmarkFilters(b *testing.B) {
receipt := makeReceipt(addr4)
gen.AddUncheckedReceipt(receipt)
gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
}
})
for i, block := range chain {
+1
View File
@@ -108,6 +108,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
)
config.LondonBlock = londonBlock
config.ArrowGlacierBlock = londonBlock
config.GrayGlacierBlock = londonBlock
engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()
genesis, err := gspec.Commit(db)
+2 -2
View File
@@ -249,7 +249,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
// out a way yet where nodes can decide unilaterally whether the network is new
// or not. This should be fixed if we figure out a solution.
if atomic.LoadUint32(&h.snapSync) == 1 {
log.Warn("Fast syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash())
log.Warn("Snap syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash())
return 0, nil
}
if h.merger.TDDReached() {
@@ -296,7 +296,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
}
// runEthPeer registers an eth peer into the joint eth/snap peerset, adds it to
// various subsistems and starts handling messages.
// various subsystems and starts handling messages.
func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
// If the peer has a `snap` extension, wait for it to connect so we can have
// a uniform initialization/teardown mechanism
-1
View File
@@ -490,7 +490,6 @@ func TestCheckpointChallenge(t *testing.T) {
}
func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) {
// Reduce the checkpoint handshake challenge timeout
defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout)
syncChallengeTimeout = 250 * time.Millisecond
+1 -2
View File
@@ -34,8 +34,7 @@ type ethPeerInfo struct {
// ethPeer is a wrapper around eth.Peer to maintain a few extra metadata.
type ethPeer struct {
*eth.Peer
snapExt *snapPeer // Satellite `snap` connection
snapWait chan struct{} // Notification channel for snap connections
snapExt *snapPeer // Satellite `snap` connection
}
// info gathers and returns some `eth` protocol metadata known about a peer.
+18 -3
View File
@@ -181,6 +181,21 @@ var eth66 = map[uint64]msgHandler{
PooledTransactionsMsg: handlePooledTransactions66,
}
var eth67 = map[uint64]msgHandler{
NewBlockHashesMsg: handleNewBlockhashes,
NewBlockMsg: handleNewBlock,
TransactionsMsg: handleTransactions,
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
GetBlockHeadersMsg: handleGetBlockHeaders66,
BlockHeadersMsg: handleBlockHeaders66,
GetBlockBodiesMsg: handleGetBlockBodies66,
BlockBodiesMsg: handleBlockBodies66,
GetReceiptsMsg: handleGetReceipts66,
ReceiptsMsg: handleReceipts66,
GetPooledTransactionsMsg: handleGetPooledTransactions66,
PooledTransactionsMsg: handlePooledTransactions66,
}
// handleMessage is invoked whenever an inbound message is received from a remote
// peer. The remote connection is torn down upon returning any error.
func handleMessage(backend Backend, peer *Peer) error {
@@ -195,9 +210,9 @@ func handleMessage(backend Backend, peer *Peer) error {
defer msg.Discard()
var handlers = eth66
//if peer.Version() >= ETH67 { // Left in as a sample when new protocol is added
// handlers = eth67
//}
if peer.Version() >= ETH67 {
handlers = eth67
}
// Track the amount of time it takes to serve the request and run the handler
if metrics.Enabled {
+3 -2
View File
@@ -31,6 +31,7 @@ import (
// Constants to match up protocol versions and messages
const (
ETH66 = 66
ETH67 = 67
)
// ProtocolName is the official short name of the `eth` protocol used during
@@ -39,11 +40,11 @@ const ProtocolName = "eth"
// ProtocolVersions are the supported versions of the `eth` protocol (first
// is primary).
var ProtocolVersions = []uint{ETH66}
var ProtocolVersions = []uint{ETH67, ETH66}
// protocolLengths are the number of implemented message corresponding to
// different protocol versions.
var protocolLengths = map[uint]uint64{ETH66: 17}
var protocolLengths = map[uint]uint64{ETH67: 17, ETH66: 17}
// maxMessageSize is the maximum cap on the size of a protocol message.
const maxMessageSize = 10 * 1024 * 1024
-2
View File
@@ -115,12 +115,10 @@ func TestEth66EmptyMessages(t *testing.T) {
t.Errorf("test %d, type %T, have\n\t%x\nwant\n\t%x", i, msg, have, want)
}
}
}
// TestEth66Messages tests the encoding of all redefined eth66 messages
func TestEth66Messages(t *testing.T) {
// Some basic structs used during testing
var (
header *types.Header
+5 -5
View File
@@ -285,7 +285,7 @@ func ServiceGetAccountRangeQuery(chain *core.BlockChain, req *GetAccountRangePac
req.Bytes = softResponseLimit
}
// Retrieve the requested state and bail out if non existent
tr, err := trie.New(req.Root, chain.StateCache().TrieDB())
tr, err := trie.New(common.Hash{}, req.Root, chain.StateCache().TrieDB())
if err != nil {
return nil, nil
}
@@ -415,7 +415,7 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP
if origin != (common.Hash{}) || (abort && len(storage) > 0) {
// Request started at a non-zero hash or was capped prematurely, add
// the endpoint Merkle proofs
accTrie, err := trie.New(req.Root, chain.StateCache().TrieDB())
accTrie, err := trie.New(common.Hash{}, req.Root, chain.StateCache().TrieDB())
if err != nil {
return nil, nil
}
@@ -423,7 +423,7 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP
if err := rlp.DecodeBytes(accTrie.Get(account[:]), &acc); err != nil {
return nil, nil
}
stTrie, err := trie.New(acc.Root, chain.StateCache().TrieDB())
stTrie, err := trie.New(account, acc.Root, chain.StateCache().TrieDB())
if err != nil {
return nil, nil
}
@@ -489,7 +489,7 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
// Make sure we have the state associated with the request
triedb := chain.StateCache().TrieDB()
accTrie, err := trie.NewSecure(req.Root, triedb)
accTrie, err := trie.NewSecure(common.Hash{}, req.Root, triedb)
if err != nil {
// We don't have the requested state available, bail out
return nil, nil
@@ -531,7 +531,7 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket, s
if err != nil || account == nil {
break
}
stTrie, err := trie.NewSecure(common.BytesToHash(account.Root), triedb)
stTrie, err := trie.NewSecure(common.BytesToHash(pathset[0]), common.BytesToHash(account.Root), triedb)
loads++ // always account database reads, even for failures
if err != nil {
break
+9 -17
View File
@@ -22,7 +22,6 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/trie"
)
func hexToNibbles(s string) []byte {
@@ -38,22 +37,17 @@ func hexToNibbles(s string) []byte {
}
func TestRequestSorting(t *testing.T) {
// - Path 0x9 -> {0x19}
// - Path 0x99 -> {0x0099}
// - Path 0x01234567890123456789012345678901012345678901234567890123456789019 -> {0x0123456789012345678901234567890101234567890123456789012345678901, 0x19}
// - Path 0x012345678901234567890123456789010123456789012345678901234567890199 -> {0x0123456789012345678901234567890101234567890123456789012345678901, 0x0099}
var f = func(path string) (trie.SyncPath, TrieNodePathSet, common.Hash) {
var f = func(path string) string {
data := hexToNibbles(path)
sp := trie.NewSyncPath(data)
tnps := TrieNodePathSet([][]byte(sp))
hash := common.Hash{}
return sp, tnps, hash
return string(data)
}
var (
hashes []common.Hash
paths []trie.SyncPath
pathsets []TrieNodePathSet
hashes []common.Hash
paths []string
)
for _, x := range []string{
"0x9",
@@ -67,16 +61,14 @@ func TestRequestSorting(t *testing.T) {
"0x01234567890123456789012345678901012345678901234567890123456789010",
"0x01234567890123456789012345678901012345678901234567890123456789011",
} {
sp, tnps, hash := f(x)
hashes = append(hashes, hash)
paths = append(paths, sp)
pathsets = append(pathsets, tnps)
paths = append(paths, f(x))
hashes = append(hashes, common.Hash{})
}
_, paths, pathsets = sortByAccountPath(hashes, paths)
_, _, syncPaths, pathsets := sortByAccountPath(paths, hashes)
{
var b = new(bytes.Buffer)
for i := 0; i < len(paths); i++ {
fmt.Fprintf(b, "\n%d. paths %x", i, paths[i])
for i := 0; i < len(syncPaths); i++ {
fmt.Fprintf(b, "\n%d. paths %x", i, syncPaths[i])
}
want := `
0. paths [0099]
+52 -47
View File
@@ -230,8 +230,8 @@ type trienodeHealRequest struct {
timeout *time.Timer // Timer to track delivery timeout
stale chan struct{} // Channel to signal the request was dropped
hashes []common.Hash // Trie node hashes to validate responses
paths []trie.SyncPath // Trie node paths requested for rescheduling
paths []string // Trie node paths for identifying trie node
hashes []common.Hash // Trie node hashes to validate responses
task *healTask // Task which this request is filling (only access fields through the runloop!!)
}
@@ -240,9 +240,9 @@ type trienodeHealRequest struct {
type trienodeHealResponse struct {
task *healTask // Task which this request is filling
hashes []common.Hash // Hashes of the trie nodes to avoid double hashing
paths []trie.SyncPath // Trie node paths requested for rescheduling missing ones
nodes [][]byte // Actual trie nodes to store into the database (nil = missing)
paths []string // Paths of the trie nodes
hashes []common.Hash // Hashes of the trie nodes to avoid double hashing
nodes [][]byte // Actual trie nodes to store into the database (nil = missing)
}
// bytecodeHealRequest tracks a pending bytecode request to ensure responses are to
@@ -321,8 +321,8 @@ type storageTask struct {
type healTask struct {
scheduler *trie.Sync // State trie sync scheduler defining the tasks
trieTasks map[common.Hash]trie.SyncPath // Set of trie node tasks currently queued for retrieval
codeTasks map[common.Hash]struct{} // Set of byte code tasks currently queued for retrieval
trieTasks map[string]common.Hash // Set of trie node tasks currently queued for retrieval, indexed by node path
codeTasks map[common.Hash]struct{} // Set of byte code tasks currently queued for retrieval, indexed by code hash
}
// SyncProgress is a database entry to allow suspending and resuming a snapshot state
@@ -540,7 +540,7 @@ func (s *Syncer) Unregister(id string) error {
return nil
}
// Sync starts (or resumes a previous) sync cycle to iterate over an state trie
// Sync starts (or resumes a previous) sync cycle to iterate over a state trie
// with the given root and reconstruct the nodes based on the snapshot leaves.
// Previously downloaded segments will not be redownloaded of fixed, rather any
// errors will be healed after the leaves are fully accumulated.
@@ -551,7 +551,7 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error {
s.root = root
s.healer = &healTask{
scheduler: state.NewStateSync(root, s.db, s.onHealState),
trieTasks: make(map[common.Hash]trie.SyncPath),
trieTasks: make(map[string]common.Hash),
codeTasks: make(map[common.Hash]struct{}),
}
s.statelessPeers = make(map[string]struct{})
@@ -712,7 +712,7 @@ func (s *Syncer) loadSyncStatus() {
}
task.genTrie = trie.NewStackTrie(task.genBatch)
for _, subtasks := range task.SubTasks {
for accountHash, subtasks := range task.SubTasks {
for _, subtask := range subtasks {
subtask.genBatch = ethdb.HookedBatch{
Batch: s.db.NewBatch(),
@@ -720,7 +720,7 @@ func (s *Syncer) loadSyncStatus() {
s.storageBytes += common.StorageSize(len(key) + len(value))
},
}
subtask.genTrie = trie.NewStackTrie(subtask.genBatch)
subtask.genTrie = trie.NewStackTrieWithOwner(subtask.genBatch, accountHash)
}
}
}
@@ -743,7 +743,7 @@ func (s *Syncer) loadSyncStatus() {
return
}
}
// Either we've failed to decode the previus state, or there was none.
// Either we've failed to decode the previous state, or there was none.
// Start a fresh sync by chunking up the account range and scheduling
// them for retrieval.
s.tasks = nil
@@ -1280,9 +1280,9 @@ func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fai
want = maxTrieRequestCount + maxCodeRequestCount
)
if have < want {
nodes, paths, codes := s.healer.scheduler.Missing(want - have)
for i, hash := range nodes {
s.healer.trieTasks[hash] = paths[i]
paths, hashes, codes := s.healer.scheduler.Missing(want - have)
for i, path := range paths {
s.healer.trieTasks[path] = hashes[i]
}
for _, hash := range codes {
s.healer.codeTasks[hash] = struct{}{}
@@ -1323,21 +1323,20 @@ func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fai
}
var (
hashes = make([]common.Hash, 0, cap)
paths = make([]trie.SyncPath, 0, cap)
paths = make([]string, 0, cap)
pathsets = make([]TrieNodePathSet, 0, cap)
)
for hash, pathset := range s.healer.trieTasks {
delete(s.healer.trieTasks, hash)
for path, hash := range s.healer.trieTasks {
delete(s.healer.trieTasks, path)
paths = append(paths, path)
hashes = append(hashes, hash)
paths = append(paths, pathset)
if len(hashes) >= cap {
if len(paths) >= cap {
break
}
}
// Group requests by account hash
hashes, paths, pathsets = sortByAccountPath(hashes, paths)
paths, hashes, _, pathsets = sortByAccountPath(paths, hashes)
req := &trienodeHealRequest{
peer: idle,
id: reqid,
@@ -1346,8 +1345,8 @@ func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fai
revert: fail,
cancel: cancel,
stale: make(chan struct{}),
hashes: hashes,
paths: paths,
hashes: hashes,
task: s.healer,
}
req.timeout = time.AfterFunc(s.rates.TargetTimeout(), func() {
@@ -1405,9 +1404,9 @@ func (s *Syncer) assignBytecodeHealTasks(success chan *bytecodeHealResponse, fai
want = maxTrieRequestCount + maxCodeRequestCount
)
if have < want {
nodes, paths, codes := s.healer.scheduler.Missing(want - have)
for i, hash := range nodes {
s.healer.trieTasks[hash] = paths[i]
paths, hashes, codes := s.healer.scheduler.Missing(want - have)
for i, path := range paths {
s.healer.trieTasks[path] = hashes[i]
}
for _, hash := range codes {
s.healer.codeTasks[hash] = struct{}{}
@@ -1703,10 +1702,10 @@ func (s *Syncer) revertTrienodeHealRequest(req *trienodeHealRequest) {
s.lock.Unlock()
// If there's a timeout timer still running, abort it and mark the trie node
// retrievals as not-pending, ready for resheduling
// retrievals as not-pending, ready for rescheduling
req.timeout.Stop()
for i, hash := range req.hashes {
req.task.trieTasks[hash] = req.paths[i]
for i, path := range req.paths {
req.task.trieTasks[path] = req.hashes[i]
}
}
@@ -1971,7 +1970,7 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
Last: r.End(),
root: acc.Root,
genBatch: batch,
genTrie: trie.NewStackTrie(batch),
genTrie: trie.NewStackTrieWithOwner(batch, account),
})
for r.Next() {
batch := ethdb.HookedBatch{
@@ -1985,7 +1984,7 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
Last: r.End(),
root: acc.Root,
genBatch: batch,
genTrie: trie.NewStackTrie(batch),
genTrie: trie.NewStackTrieWithOwner(batch, account),
})
}
for _, task := range tasks {
@@ -2030,7 +2029,7 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
slots += len(res.hashes[i])
if i < len(res.hashes)-1 || res.subTask == nil {
tr := trie.NewStackTrie(batch)
tr := trie.NewStackTrieWithOwner(batch, account)
for j := 0; j < len(res.hashes[i]); j++ {
tr.Update(res.hashes[i][j][:], res.slots[i][j])
}
@@ -2096,14 +2095,14 @@ func (s *Syncer) processTrienodeHealResponse(res *trienodeHealResponse) {
// If the trie node was not delivered, reschedule it
if node == nil {
res.task.trieTasks[hash] = res.paths[i]
res.task.trieTasks[res.paths[i]] = res.hashes[i]
continue
}
// Push the trie node into the state syncer
s.trienodeHealSynced++
s.trienodeHealBytes += common.StorageSize(len(node))
err := s.healer.scheduler.Process(trie.SyncResult{Hash: hash, Data: node})
err := s.healer.scheduler.ProcessNode(trie.NodeSyncResult{Path: res.paths[i], Data: node})
switch err {
case nil:
case trie.ErrAlreadyProcessed:
@@ -2139,7 +2138,7 @@ func (s *Syncer) processBytecodeHealResponse(res *bytecodeHealResponse) {
s.bytecodeHealSynced++
s.bytecodeHealBytes += common.StorageSize(len(node))
err := s.healer.scheduler.Process(trie.SyncResult{Hash: hash, Data: node})
err := s.healer.scheduler.ProcessCode(trie.CodeSyncResult{Hash: hash, Data: node})
switch err {
case nil:
case trie.ErrAlreadyProcessed:
@@ -2666,9 +2665,9 @@ func (s *Syncer) OnTrieNodes(peer SyncPeer, id uint64, trienodes [][]byte) error
}
// Response validated, send it to the scheduler for filling
response := &trienodeHealResponse{
paths: req.paths,
task: req.task,
hashes: req.hashes,
paths: req.paths,
nodes: nodes,
}
select {
@@ -2781,7 +2780,7 @@ func (s *Syncer) onHealState(paths [][]byte, value []byte) error {
if len(paths) == 1 {
var account types.StateAccount
if err := rlp.DecodeBytes(value, &account); err != nil {
return nil
return nil // Returning the error here would drop the remote peer
}
blob := snapshot.SlimAccountRLP(account.Nonce, account.Balance, account.Root, account.CodeHash)
rawdb.WriteAccountSnapshot(s.stateWriter, common.BytesToHash(paths[0]), blob)
@@ -2913,8 +2912,9 @@ func (s *capacitySort) Swap(i, j int) {
// healRequestSort implements the Sort interface, allowing sorting trienode
// heal requests, which is a prerequisite for merging storage-requests.
type healRequestSort struct {
hashes []common.Hash
paths []trie.SyncPath
paths []string
hashes []common.Hash
syncPaths []trie.SyncPath
}
func (t *healRequestSort) Len() int {
@@ -2922,8 +2922,8 @@ func (t *healRequestSort) Len() int {
}
func (t *healRequestSort) Less(i, j int) bool {
a := t.paths[i]
b := t.paths[j]
a := t.syncPaths[i]
b := t.syncPaths[j]
switch bytes.Compare(a[0], b[0]) {
case -1:
return true
@@ -2944,8 +2944,9 @@ func (t *healRequestSort) Less(i, j int) bool {
}
func (t *healRequestSort) Swap(i, j int) {
t.hashes[i], t.hashes[j] = t.hashes[j], t.hashes[i]
t.paths[i], t.paths[j] = t.paths[j], t.paths[i]
t.hashes[i], t.hashes[j] = t.hashes[j], t.hashes[i]
t.syncPaths[i], t.syncPaths[j] = t.syncPaths[j], t.syncPaths[i]
}
// Merge merges the pathsets, so that several storage requests concerning the
@@ -2953,7 +2954,7 @@ func (t *healRequestSort) Swap(i, j int) {
// OBS: This operation is moot if t has not first been sorted.
func (t *healRequestSort) Merge() []TrieNodePathSet {
var result []TrieNodePathSet
for _, path := range t.paths {
for _, path := range t.syncPaths {
pathset := TrieNodePathSet([][]byte(path))
if len(path) == 1 {
// It's an account reference.
@@ -2962,7 +2963,7 @@ func (t *healRequestSort) Merge() []TrieNodePathSet {
// It's a storage reference.
end := len(result) - 1
if len(result) == 0 || !bytes.Equal(pathset[0], result[end][0]) {
// The account doesn't doesn't match last, create a new entry.
// The account doesn't match last, create a new entry.
result = append(result, pathset)
} else {
// It's the same account as the previous one, add to the storage
@@ -2976,9 +2977,13 @@ func (t *healRequestSort) Merge() []TrieNodePathSet {
// sortByAccountPath takes hashes and paths, and sorts them. After that, it generates
// the TrieNodePaths and merges paths which belongs to the same account path.
func sortByAccountPath(hashes []common.Hash, paths []trie.SyncPath) ([]common.Hash, []trie.SyncPath, []TrieNodePathSet) {
n := &healRequestSort{hashes, paths}
func sortByAccountPath(paths []string, hashes []common.Hash) ([]string, []common.Hash, []trie.SyncPath, []TrieNodePathSet) {
var syncPaths []trie.SyncPath
for _, path := range paths {
syncPaths = append(syncPaths, trie.NewSyncPath([]byte(path)))
}
n := &healRequestSort{paths, hashes, syncPaths}
sort.Sort(n)
pathsets := n.Merge()
return n.hashes, n.paths, pathsets
return n.paths, n.hashes, n.syncPaths, pathsets
}
+26 -26
View File
@@ -1349,7 +1349,7 @@ func getCodeByHash(hash common.Hash) []byte {
// makeAccountTrieNoStorage spits out a trie, along with the leafs
func makeAccountTrieNoStorage(n int) (*trie.Trie, entrySlice) {
db := trie.NewDatabase(rawdb.NewMemoryDatabase())
accTrie, _ := trie.New(common.Hash{}, db)
accTrie := trie.NewEmpty(db)
var entries entrySlice
for i := uint64(1); i <= uint64(n); i++ {
value, _ := rlp.EncodeToBytes(&types.StateAccount{
@@ -1376,8 +1376,8 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
entries entrySlice
boundaries []common.Hash
db = trie.NewDatabase(rawdb.NewMemoryDatabase())
trie, _ = trie.New(common.Hash{}, db)
db = trie.NewDatabase(rawdb.NewMemoryDatabase())
trie = trie.NewEmpty(db)
)
// Initialize boundaries
var next common.Hash
@@ -1429,7 +1429,7 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) (*trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) {
var (
db = trie.NewDatabase(rawdb.NewMemoryDatabase())
accTrie, _ = trie.New(common.Hash{}, db)
accTrie = trie.NewEmpty(db)
entries entrySlice
storageTries = make(map[common.Hash]*trie.Trie)
storageEntries = make(map[common.Hash]entrySlice)
@@ -1442,7 +1442,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool)
codehash = getCodeHash(i)
}
// Create a storage trie
stTrie, stEntries := makeStorageTrieWithSeed(uint64(slots), i, db)
stTrie, stEntries := makeStorageTrieWithSeed(common.BytesToHash(key), uint64(slots), i, db)
stRoot := stTrie.Hash()
stTrie.Commit(nil)
value, _ := rlp.EncodeToBytes(&types.StateAccount{
@@ -1468,23 +1468,11 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool)
func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie.Trie, entrySlice, map[common.Hash]*trie.Trie, map[common.Hash]entrySlice) {
var (
db = trie.NewDatabase(rawdb.NewMemoryDatabase())
accTrie, _ = trie.New(common.Hash{}, db)
accTrie = trie.NewEmpty(db)
entries entrySlice
storageTries = make(map[common.Hash]*trie.Trie)
storageEntries = make(map[common.Hash]entrySlice)
)
// Make a storage trie which we reuse for the whole lot
var (
stTrie *trie.Trie
stEntries entrySlice
)
if boundary {
stTrie, stEntries = makeBoundaryStorageTrie(slots, db)
} else {
stTrie, stEntries = makeStorageTrieWithSeed(uint64(slots), 0, db)
}
stRoot := stTrie.Hash()
// Create n accounts in the trie
for i := uint64(1); i <= uint64(accounts); i++ {
key := key32(i)
@@ -1492,6 +1480,19 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie
if code {
codehash = getCodeHash(i)
}
// Make a storage trie
var (
stTrie *trie.Trie
stEntries entrySlice
)
if boundary {
stTrie, stEntries = makeBoundaryStorageTrie(common.BytesToHash(key), slots, db)
} else {
stTrie, stEntries = makeStorageTrieWithSeed(common.BytesToHash(key), uint64(slots), 0, db)
}
stRoot := stTrie.Hash()
stTrie.Commit(nil)
value, _ := rlp.EncodeToBytes(&types.StateAccount{
Nonce: i,
Balance: big.NewInt(int64(i)),
@@ -1506,7 +1507,6 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie
storageEntries[common.BytesToHash(key)] = stEntries
}
sort.Sort(entries)
stTrie.Commit(nil)
accTrie.Commit(nil)
return accTrie, entries, storageTries, storageEntries
}
@@ -1514,8 +1514,8 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie
// makeStorageTrieWithSeed fills a storage trie with n items, returning the
// not-yet-committed trie and the sorted entries. The seeds can be used to ensure
// that tries are unique.
func makeStorageTrieWithSeed(n, seed uint64, db *trie.Database) (*trie.Trie, entrySlice) {
trie, _ := trie.New(common.Hash{}, db)
func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *trie.Database) (*trie.Trie, entrySlice) {
trie, _ := trie.New(owner, common.Hash{}, db)
var entries entrySlice
for i := uint64(1); i <= n; i++ {
// store 'x' at slot 'x'
@@ -1537,11 +1537,11 @@ func makeStorageTrieWithSeed(n, seed uint64, db *trie.Database) (*trie.Trie, ent
// makeBoundaryStorageTrie constructs a storage trie. Instead of filling
// storage slots normally, this function will fill a few slots which have
// boundary hash.
func makeBoundaryStorageTrie(n int, db *trie.Database) (*trie.Trie, entrySlice) {
func makeBoundaryStorageTrie(owner common.Hash, n int, db *trie.Database) (*trie.Trie, entrySlice) {
var (
entries entrySlice
boundaries []common.Hash
trie, _ = trie.New(common.Hash{}, db)
trie, _ = trie.New(owner, common.Hash{}, db)
)
// Initialize boundaries
var next common.Hash
@@ -1588,7 +1588,7 @@ func makeBoundaryStorageTrie(n int, db *trie.Database) (*trie.Trie, entrySlice)
func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) {
t.Helper()
triedb := trie.NewDatabase(db)
accTrie, err := trie.New(root, triedb)
accTrie, err := trie.New(common.Hash{}, root, triedb)
if err != nil {
t.Fatal(err)
}
@@ -1606,7 +1606,7 @@ func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) {
}
accounts++
if acc.Root != emptyRoot {
storeTrie, err := trie.NewSecure(acc.Root, triedb)
storeTrie, err := trie.NewSecure(common.BytesToHash(accIt.Key), acc.Root, triedb)
if err != nil {
t.Fatal(err)
}
@@ -1661,7 +1661,7 @@ func TestSyncAccountPerformance(t *testing.T) {
// Doing so would bring this number down to zero in this artificial testcase,
// but only add extra IO for no reason in practice.
if have, want := src.nTrienodeRequests, 1; have != want {
fmt.Printf(src.Stats())
fmt.Print(src.Stats())
t.Errorf("trie node heal requests wrong, want %d, have %d", want, have)
}
}
+1
View File
@@ -30,6 +30,7 @@ import (
// Tests that snap sync is disabled after a successful sync cycle.
func TestSnapSyncDisabling66(t *testing.T) { testSnapSyncDisabling(t, eth.ETH66, snap.SNAP1) }
func TestSnapSyncDisabling67(t *testing.T) { testSnapSyncDisabling(t, eth.ETH67, snap.SNAP1) }
// Tests that snap sync gets disabled as soon as a real block is successfully
// imported into the blockchain.
+12 -5
View File
@@ -179,6 +179,7 @@ type TraceCallConfig struct {
Timeout *string
Reexec *uint64
StateOverrides *ethapi.StateOverride
BlockOverrides *ethapi.BlockOverrides
}
// StdTraceConfig holds extra parameters to standard-json trace functions.
@@ -806,7 +807,6 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
// TraceCall lets you trace a given eth_call. It collects the structured logs
// created during the execution of EVM if the given transaction was added on
// top of the provided block and returns them as a JSON object.
// You can provide -2 as a block number to trace on top of the pending block.
func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
// Try to retrieve the specified block
var (
@@ -816,6 +816,14 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if hash, ok := blockNrOrHash.Hash(); ok {
block, err = api.blockByHash(ctx, hash)
} else if number, ok := blockNrOrHash.Number(); ok {
if number == rpc.PendingBlockNumber {
// We don't have access to the miner here. For tracing 'future' transactions,
// it can be done with block- and state-overrides instead, which offers
// more flexibility and stability than trying to trace on 'pending', since
// the contents of 'pending' is unstable and probably not a true representation
// of what the next actual block is likely to contain.
return nil, errors.New("tracing on top of pending is not supported")
}
block, err = api.blockByNumber(ctx, number)
} else {
return nil, errors.New("invalid arguments; neither block nor hash specified")
@@ -832,18 +840,19 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if err != nil {
return nil, err
}
// Apply the customized state rules if required.
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
// Apply the customization rules if required.
if config != nil {
if err := config.StateOverrides.Apply(statedb); err != nil {
return nil, err
}
config.BlockOverrides.Apply(&vmctx)
}
// Execute the trace
msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee())
if err != nil {
return nil, err
}
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
var traceConfig *TraceConfig
if config != nil {
@@ -917,9 +926,7 @@ func APIs(backend Backend) []rpc.API {
return []rpc.API{
{
Namespace: "debug",
Version: "1.0",
Service: NewAPI(backend),
Public: false,
},
}
}
+68 -39
View File
@@ -196,13 +196,12 @@ func TestTraceCall(t *testing.T) {
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
b.AddTx(tx)
}))
var testSuite = []struct {
blockNumber rpc.BlockNumber
call ethapi.TransactionArgs
config *TraceCallConfig
expectErr error
expect interface{}
expect string
}{
// Standard JSON trace upon the genesis, plain transfer.
{
@@ -214,12 +213,7 @@ func TestTraceCall(t *testing.T) {
},
config: nil,
expectErr: nil,
expect: &logger.ExecutionResult{
Gas: params.TxGas,
Failed: false,
ReturnValue: "",
StructLogs: []logger.StructLogRes{},
},
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
// Standard JSON trace upon the head, plain transfer.
{
@@ -231,12 +225,7 @@ func TestTraceCall(t *testing.T) {
},
config: nil,
expectErr: nil,
expect: &logger.ExecutionResult{
Gas: params.TxGas,
Failed: false,
ReturnValue: "",
StructLogs: []logger.StructLogRes{},
},
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
// Standard JSON trace upon the non-existent block, error expects
{
@@ -248,7 +237,7 @@ func TestTraceCall(t *testing.T) {
},
config: nil,
expectErr: fmt.Errorf("block #%d not found", genBlocks+1),
expect: nil,
//expect: nil,
},
// Standard JSON trace upon the latest block
{
@@ -260,14 +249,9 @@ func TestTraceCall(t *testing.T) {
},
config: nil,
expectErr: nil,
expect: &logger.ExecutionResult{
Gas: params.TxGas,
Failed: false,
ReturnValue: "",
StructLogs: []logger.StructLogRes{},
},
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
// Standard JSON trace upon the pending block
// Tracing on 'pending' should fail:
{
blockNumber: rpc.PendingBlockNumber,
call: ethapi.TransactionArgs{
@@ -276,36 +260,48 @@ func TestTraceCall(t *testing.T) {
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: nil,
expectErr: nil,
expect: &logger.ExecutionResult{
Gas: params.TxGas,
Failed: false,
ReturnValue: "",
StructLogs: []logger.StructLogRes{},
expectErr: errors.New("tracing on top of pending is not supported"),
},
{
blockNumber: rpc.LatestBlockNumber,
call: ethapi.TransactionArgs{
From: &accounts[0].addr,
Input: &hexutil.Bytes{0x43}, // blocknumber
},
config: &TraceCallConfig{
BlockOverrides: &ethapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
},
expectErr: nil,
expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[
{"pc":0,"op":"NUMBER","gas":24946984,"gasCost":2,"depth":1,"stack":[]},
{"pc":1,"op":"STOP","gas":24946982,"gasCost":0,"depth":1,"stack":["0x1337"]}]}`,
},
}
for _, testspec := range testSuite {
for i, testspec := range testSuite {
result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config)
if testspec.expectErr != nil {
if err == nil {
t.Errorf("Expect error %v, get nothing", testspec.expectErr)
t.Errorf("test %d: expect error %v, got nothing", i, testspec.expectErr)
continue
}
if !reflect.DeepEqual(err, testspec.expectErr) {
t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err)
t.Errorf("test %d: error mismatch, want %v, git %v", i, testspec.expectErr, err)
}
} else {
if err != nil {
t.Errorf("Expect no error, get %v", err)
t.Errorf("test %d: expect no error, got %v", i, err)
continue
}
var have *logger.ExecutionResult
if err := json.Unmarshal(result.(json.RawMessage), &have); err != nil {
t.Errorf("failed to unmarshal result %v", err)
t.Errorf("test %d: failed to unmarshal result %v", i, err)
}
if !reflect.DeepEqual(have, testspec.expect) {
t.Errorf("Result mismatch, want %v, get %v", testspec.expect, have)
var want *logger.ExecutionResult
if err := json.Unmarshal([]byte(testspec.expect), &want); err != nil {
t.Errorf("test %d: failed to unmarshal result %v", i, err)
}
if !reflect.DeepEqual(have, want) {
t.Errorf("test %d: result mismatch, want %v, got %v", i, testspec.expect, string(result.(json.RawMessage)))
}
}
}
@@ -446,7 +442,7 @@ func TestTracingWithOverrides(t *testing.T) {
type res struct {
Gas int
Failed bool
returnValue string
ReturnValue string
}
var testSuite = []struct {
blockNumber rpc.BlockNumber
@@ -457,7 +453,7 @@ func TestTracingWithOverrides(t *testing.T) {
}{
// Call which can only succeed if state is state overridden
{
blockNumber: rpc.PendingBlockNumber,
blockNumber: rpc.LatestBlockNumber,
call: ethapi.TransactionArgs{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
@@ -472,7 +468,7 @@ func TestTracingWithOverrides(t *testing.T) {
},
// Invalid call without state overriding
{
blockNumber: rpc.PendingBlockNumber,
blockNumber: rpc.LatestBlockNumber,
call: ethapi.TransactionArgs{
From: &randomAccounts[0].addr,
To: &randomAccounts[1].addr,
@@ -498,7 +494,7 @@ func TestTracingWithOverrides(t *testing.T) {
// }
// }
{
blockNumber: rpc.PendingBlockNumber,
blockNumber: rpc.LatestBlockNumber,
call: ethapi.TransactionArgs{
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr,
@@ -515,6 +511,39 @@ func TestTracingWithOverrides(t *testing.T) {
},
want: `{"gas":23347,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000007b"}`,
},
{ // Override blocknumber
blockNumber: rpc.LatestBlockNumber,
call: ethapi.TransactionArgs{
From: &accounts[0].addr,
// BLOCKNUMBER PUSH1 MSTORE
Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")),
//&hexutil.Bytes{0x43}, // blocknumber
},
config: &TraceCallConfig{
BlockOverrides: &ethapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
},
want: `{"gas":59537,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000001337"}`,
},
{ // Override blocknumber, and query a blockhash
blockNumber: rpc.LatestBlockNumber,
call: ethapi.TransactionArgs{
From: &accounts[0].addr,
Input: &hexutil.Bytes{
0x60, 0x00, 0x40, // BLOCKHASH(0)
0x60, 0x00, 0x52, // STORE memory offset 0
0x61, 0x13, 0x36, 0x40, // BLOCKHASH(0x1336)
0x60, 0x20, 0x52, // STORE memory offset 32
0x61, 0x13, 0x37, 0x40, // BLOCKHASH(0x1337)
0x60, 0x40, 0x52, // STORE memory offset 64
0x60, 0x60, 0x60, 0x00, 0xf3, // RETURN (0-96)
}, // blocknumber
},
config: &TraceCallConfig{
BlockOverrides: &ethapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
},
want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`,
},
}
for i, tc := range testSuite {
result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config)
+8 -15
View File
@@ -55,11 +55,7 @@ type fromBufFn = func(vm *goja.Runtime, buf goja.Value, allowString bool) ([]byt
func toBuf(vm *goja.Runtime, bufType goja.Value, val []byte) (goja.Value, error) {
// bufType is usually Uint8Array. This is equivalent to `new Uint8Array(val)` in JS.
res, err := vm.New(bufType, vm.ToValue(val))
if err != nil {
return nil, err
}
return vm.ToValue(res), nil
return vm.New(bufType, vm.ToValue(vm.NewArrayBuffer(val)))
}
func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString bool) ([]byte, error) {
@@ -70,6 +66,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
break
}
return common.FromHex(obj.String()), nil
case "Array":
var b []byte
if err := vm.ExportTo(buf, &b); err != nil {
@@ -81,10 +78,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
if !obj.Get("constructor").SameAs(bufType) {
break
}
var b []byte
if err := vm.ExportTo(buf, &b); err != nil {
return nil, err
}
b := obj.Get("buffer").Export().(goja.ArrayBuffer).Bytes()
return b, nil
}
return nil, fmt.Errorf("invalid buffer type")
@@ -554,10 +548,10 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
return []byte{}, nil
}
if end < begin || begin < 0 {
return nil, fmt.Errorf("Tracer accessed out of bound memory: offset %d, end %d", begin, end)
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
}
if mo.memory.Len() < int(end) {
return nil, fmt.Errorf("Tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), begin, end-begin)
return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), begin, end-begin)
}
return mo.memory.GetCopy(begin, end-begin), nil
}
@@ -579,7 +573,7 @@ func (mo *memoryObj) GetUint(addr int64) goja.Value {
// getUint returns the 32 bytes at the specified address interpreted as a uint.
func (mo *memoryObj) getUint(addr int64) (*big.Int, error) {
if mo.memory.Len() < int(addr)+32 || addr < 0 {
return nil, fmt.Errorf("Tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32)
return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32)
}
return new(big.Int).SetBytes(mo.memory.GetPtr(addr, 32)), nil
}
@@ -619,7 +613,7 @@ func (s *stackObj) Peek(idx int) goja.Value {
// peek returns the nth-from-the-top element of the stack.
func (s *stackObj) peek(idx int) (*big.Int, error) {
if len(s.stack.Data()) <= idx || idx < 0 {
return nil, fmt.Errorf("Tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx)
return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx)
}
return s.stack.Back(idx).ToBig(), nil
}
@@ -765,7 +759,7 @@ func (co *contractObj) GetValue() goja.Value {
}
func (co *contractObj) GetInput() goja.Value {
input := co.contract.Input
input := common.CopyBytes(co.contract.Input)
res, err := co.toBuf(co.vm, input)
if err != nil {
co.vm.Interrupt(err)
@@ -884,7 +878,6 @@ func (r *callframeResult) GetError() goja.Value {
return r.vm.ToValue(r.err.Error())
}
return goja.Undefined()
}
func (r *callframeResult) setupObject() *goja.Object {
@@ -46,7 +46,7 @@
return false;
},
// store save the given indentifier and datasize.
// store save the given identifier and datasize.
store: function(id, size){
var key = "" + toHex(id) + "-" + size;
this.ids[key] = this.ids[key] + 1 || 1;
+5 -5
View File
@@ -103,15 +103,15 @@ func TestTracer(t *testing.T) {
{ // tests that we don't panic on bad arguments to memory access
code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
want: ``,
fail: "Tracer accessed out of bound memory: offset -1, end -2 at step (<eval>:1:53(15)) in server-side tracer function 'step'",
fail: "tracer accessed out of bound memory: offset -1, end -2 at step (<eval>:1:53(15)) in server-side tracer function 'step'",
}, { // tests that we don't panic on bad arguments to stack peeks
code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}",
want: ``,
fail: "Tracer accessed out of bound stack: size 0, index -1 at step (<eval>:1:53(13)) in server-side tracer function 'step'",
fail: "tracer accessed out of bound stack: size 0, index -1 at step (<eval>:1:53(13)) in server-side tracer function 'step'",
}, { // tests that we don't panic on bad arguments to memory getUint
code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}",
want: ``,
fail: "Tracer accessed out of bound memory: available 0, offset -64, size 32 at step (<eval>:1:58(13)) in server-side tracer function 'step'",
fail: "tracer accessed out of bound memory: available 0, offset -64, size 32 at step (<eval>:1:58(13)) in server-side tracer function 'step'",
}, { // tests some general counting
code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}",
want: `3`,
@@ -232,7 +232,7 @@ func TestIsPrecompile(t *testing.T) {
t.Error(err)
}
if string(res) != "false" {
t.Errorf("Tracer should not consider blake2f as precompile in byzantium")
t.Errorf("tracer should not consider blake2f as precompile in byzantium")
}
tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil)
@@ -242,7 +242,7 @@ func TestIsPrecompile(t *testing.T) {
t.Error(err)
}
if string(res) != "true" {
t.Errorf("Tracer should consider blake2f as precompile in istanbul")
t.Errorf("tracer should consider blake2f as precompile in istanbul")
}
}
+5 -5
View File
@@ -40,7 +40,7 @@ type Storage map[common.Hash]common.Hash
// Copy duplicates the current storage.
func (s Storage) Copy() Storage {
cpy := make(Storage)
cpy := make(Storage, len(s))
for key, value := range s {
cpy[key] = value
}
@@ -224,7 +224,7 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
l.output = output
l.err = err
if l.cfg.Debug {
fmt.Printf("0x%x\n", output)
fmt.Printf("%#x\n", output)
if err != nil {
fmt.Printf(" error: %v\n", err)
}
@@ -346,11 +346,11 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger {
func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
t.env = env
if !create {
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(),
input, gas, value)
} else {
fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(),
input, gas, value)
}
@@ -387,7 +387,7 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
}
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) {
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
+108
View File
@@ -0,0 +1,108 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package native
import (
"bytes"
"encoding/json"
"math/big"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers"
)
func init() {
register("revertReasonTracer", newRevertReasonTracer)
}
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
// revertReasonTracer is a go implementation of the Tracer interface which
// track the error message or revert reason return by the contract.
type revertReasonTracer struct {
env *vm.EVM
revertReason string // The revert reason return from the tx, if tx success, empty string return
interrupt uint32 // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
}
// newRevertReasonTracer returns a new revert reason tracer.
func newRevertReasonTracer(_ *tracers.Context) tracers.Tracer {
return &revertReasonTracer{}
}
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *revertReasonTracer) CaptureStart(env *vm.EVM, _ common.Address, _ common.Address, _ bool, _ []byte, _ uint64, _ *big.Int) {
t.env = env
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (t *revertReasonTracer) CaptureEnd(output []byte, _ uint64, _ time.Duration, err error) {
if err != nil {
if err == vm.ErrExecutionReverted && len(output) > 4 && bytes.Equal(output[:4], revertSelector) {
errMsg, _ := abi.UnpackRevert(output)
t.revertReason = err.Error() + ": " + errMsg
} else {
t.revertReason = err.Error()
}
}
}
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
func (t *revertReasonTracer) CaptureState(_ uint64, _ vm.OpCode, _, _ uint64, _ *vm.ScopeContext, _ []byte, _ int, _ error) {
}
// CaptureFault implements the EVMLogger interface to trace an execution fault.
func (t *revertReasonTracer) CaptureFault(_ uint64, _ vm.OpCode, _, _ uint64, _ *vm.ScopeContext, _ int, _ error) {
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *revertReasonTracer) CaptureEnter(_ vm.OpCode, _ common.Address, _ common.Address, _ []byte, _ uint64, _ *big.Int) {
// Skip if tracing was interrupted
if atomic.LoadUint32(&t.interrupt) > 0 {
t.env.Cancel()
return
}
}
// CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code.
func (t *revertReasonTracer) CaptureExit(_ []byte, _ uint64, _ error) {}
func (t *revertReasonTracer) CaptureTxStart(_ uint64) {}
func (t *revertReasonTracer) CaptureTxEnd(_ uint64) {}
// GetResult returns an error message json object.
func (t *revertReasonTracer) GetResult() (json.RawMessage, error) {
res, err := json.Marshal(t.revertReason)
if err != nil {
return nil, err
}
return res, t.reason
}
// Stop terminates execution of the tracer at the first opportune moment.
func (t *revertReasonTracer) Stop(err error) {
t.reason = err
atomic.StoreUint32(&t.interrupt, 1)
}
-15
View File
@@ -21,7 +21,6 @@ import (
"testing"
"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/rawdb"
"github.com/ethereum/go-ethereum/core/types"
@@ -32,20 +31,6 @@ import (
"github.com/ethereum/go-ethereum/tests"
)
// callTrace is the result of a callTracer run.
type callTrace struct {
Type string `json:"type"`
From common.Address `json:"from"`
To common.Address `json:"to"`
Input hexutil.Bytes `json:"input"`
Output hexutil.Bytes `json:"output"`
Gas *hexutil.Uint64 `json:"gas,omitempty"`
GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"`
Value *hexutil.Big `json:"value,omitempty"`
Error string `json:"error,omitempty"`
Calls []callTrace `json:"calls,omitempty"`
}
func BenchmarkTransactionTrace(b *testing.B) {
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
from := crypto.PubkeyToAddress(key.PublicKey)