Unverified Commit 017831dd authored by Péter Szilágyi's avatar Péter Szilágyi Committed by GitHub

core, eth: split eth package, implement snap protocol (#21482)

This commit splits the eth package, separating the handling of eth and snap protocols. It also includes the capability to run snap sync (https://github.com/ethereum/devp2p/blob/master/caps/snap.md) , but does not enable it by default. 
Co-authored-by: 's avatarMarius van der Wijden <m.vanderwijden@live.de>
Co-authored-by: 's avatarMartin Holst Swende <martin@swende.se>
parent 00d10e61
...@@ -25,7 +25,6 @@ import ( ...@@ -25,7 +25,6 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
...@@ -143,7 +142,6 @@ func version(ctx *cli.Context) error { ...@@ -143,7 +142,6 @@ func version(ctx *cli.Context) error {
fmt.Println("Git Commit Date:", gitDate) fmt.Println("Git Commit Date:", gitDate)
} }
fmt.Println("Architecture:", runtime.GOARCH) fmt.Println("Architecture:", runtime.GOARCH)
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
fmt.Println("Go Version:", runtime.Version()) fmt.Println("Go Version:", runtime.Version())
fmt.Println("Operating System:", runtime.GOOS) fmt.Println("Operating System:", runtime.GOOS)
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
......
...@@ -187,7 +187,7 @@ var ( ...@@ -187,7 +187,7 @@ var (
defaultSyncMode = eth.DefaultConfig.SyncMode defaultSyncMode = eth.DefaultConfig.SyncMode
SyncModeFlag = TextMarshalerFlag{ SyncModeFlag = TextMarshalerFlag{
Name: "syncmode", Name: "syncmode",
Usage: `Blockchain sync mode ("fast", "full", or "light")`, Usage: `Blockchain sync mode ("fast", "full", "snap" or "light")`,
Value: &defaultSyncMode, Value: &defaultSyncMode,
} }
GCModeFlag = cli.StringFlag{ GCModeFlag = cli.StringFlag{
...@@ -1555,8 +1555,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { ...@@ -1555,8 +1555,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100 cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100
} }
if !ctx.GlobalIsSet(SnapshotFlag.Name) { if !ctx.GlobalIsSet(SnapshotFlag.Name) {
cfg.TrieCleanCache += cfg.SnapshotCache // If snap-sync is requested, this flag is also required
cfg.SnapshotCache = 0 // Disabled if cfg.SyncMode == downloader.SnapSync {
log.Info("Snap sync requested, enabling --snapshot")
ctx.Set(SnapshotFlag.Name, "true")
} else {
cfg.TrieCleanCache += cfg.SnapshotCache
cfg.SnapshotCache = 0 // Disabled
}
} }
if ctx.GlobalIsSet(DocRootFlag.Name) { if ctx.GlobalIsSet(DocRootFlag.Name) {
cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name) cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name)
...@@ -1585,16 +1591,15 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { ...@@ -1585,16 +1591,15 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name) cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name)
} }
if ctx.GlobalIsSet(NoDiscoverFlag.Name) { if ctx.GlobalIsSet(NoDiscoverFlag.Name) {
cfg.DiscoveryURLs = []string{} cfg.EthDiscoveryURLs, cfg.SnapDiscoveryURLs = []string{}, []string{}
} else if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) { } else if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) {
urls := ctx.GlobalString(DNSDiscoveryFlag.Name) urls := ctx.GlobalString(DNSDiscoveryFlag.Name)
if urls == "" { if urls == "" {
cfg.DiscoveryURLs = []string{} cfg.EthDiscoveryURLs = []string{}
} else { } else {
cfg.DiscoveryURLs = SplitAndTrim(urls) cfg.EthDiscoveryURLs = SplitAndTrim(urls)
} }
} }
// Override any default configs for hard coded networks. // Override any default configs for hard coded networks.
switch { switch {
case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name): case ctx.GlobalBool(LegacyTestnetFlag.Name) || ctx.GlobalBool(RopstenFlag.Name):
...@@ -1676,16 +1681,20 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { ...@@ -1676,16 +1681,20 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
// SetDNSDiscoveryDefaults configures DNS discovery with the given URL if // SetDNSDiscoveryDefaults configures DNS discovery with the given URL if
// no URLs are set. // no URLs are set.
func SetDNSDiscoveryDefaults(cfg *eth.Config, genesis common.Hash) { func SetDNSDiscoveryDefaults(cfg *eth.Config, genesis common.Hash) {
if cfg.DiscoveryURLs != nil { if cfg.EthDiscoveryURLs != nil {
return // already set through flags/config return // already set through flags/config
} }
protocol := "all" protocol := "all"
if cfg.SyncMode == downloader.LightSync { if cfg.SyncMode == downloader.LightSync {
protocol = "les" protocol = "les"
} }
if url := params.KnownDNSNetwork(genesis, protocol); url != "" { if url := params.KnownDNSNetwork(genesis, protocol); url != "" {
cfg.DiscoveryURLs = []string{url} cfg.EthDiscoveryURLs = []string{url}
}
if cfg.SyncMode == downloader.SnapSync {
if url := params.KnownDNSNetwork(genesis, "snap"); url != "" {
cfg.SnapDiscoveryURLs = []string{url}
}
} }
} }
......
...@@ -659,12 +659,8 @@ func (bc *BlockChain) CurrentBlock() *types.Block { ...@@ -659,12 +659,8 @@ func (bc *BlockChain) CurrentBlock() *types.Block {
return bc.currentBlock.Load().(*types.Block) return bc.currentBlock.Load().(*types.Block)
} }
// Snapshot returns the blockchain snapshot tree. This method is mainly used for // Snapshots returns the blockchain snapshot tree.
// testing, to make it possible to verify the snapshot after execution. func (bc *BlockChain) Snapshots() *snapshot.Tree {
//
// Warning: There are no guarantees about the safety of using the returned 'snap' if the
// blockchain is simultaneously importing blocks, so take care.
func (bc *BlockChain) Snapshot() *snapshot.Tree {
return bc.snaps return bc.snaps
} }
......
...@@ -751,7 +751,7 @@ func testSnapshot(t *testing.T, tt *snapshotTest) { ...@@ -751,7 +751,7 @@ func testSnapshot(t *testing.T, tt *snapshotTest) {
t.Fatalf("Failed to recreate chain: %v", err) t.Fatalf("Failed to recreate chain: %v", err)
} }
chain.InsertChain(newBlocks) chain.InsertChain(newBlocks)
chain.Snapshot().Cap(newBlocks[len(newBlocks)-1].Root(), 0) chain.Snapshots().Cap(newBlocks[len(newBlocks)-1].Root(), 0)
// Simulate the blockchain crash // Simulate the blockchain crash
// Don't call chain.Stop here, so that no snapshot // Don't call chain.Stop here, so that no snapshot
......
...@@ -84,6 +84,15 @@ func NewID(config *params.ChainConfig, genesis common.Hash, head uint64) ID { ...@@ -84,6 +84,15 @@ func NewID(config *params.ChainConfig, genesis common.Hash, head uint64) ID {
return ID{Hash: checksumToBytes(hash), Next: next} return ID{Hash: checksumToBytes(hash), Next: next}
} }
// NewIDWithChain calculates the Ethereum fork ID from an existing chain instance.
func NewIDWithChain(chain Blockchain) ID {
return NewID(
chain.Config(),
chain.Genesis().Hash(),
chain.CurrentHeader().Number.Uint64(),
)
}
// NewFilter creates a filter that returns if a fork ID should be rejected or not // NewFilter creates a filter that returns if a fork ID should be rejected or not
// based on the local chain's status. // based on the local chain's status.
func NewFilter(chain Blockchain) Filter { func NewFilter(chain Blockchain) Filter {
......
...@@ -175,3 +175,24 @@ func DeleteSnapshotRecoveryNumber(db ethdb.KeyValueWriter) { ...@@ -175,3 +175,24 @@ func DeleteSnapshotRecoveryNumber(db ethdb.KeyValueWriter) {
log.Crit("Failed to remove snapshot recovery number", "err", err) log.Crit("Failed to remove snapshot recovery number", "err", err)
} }
} }
// ReadSanpshotSyncStatus retrieves the serialized sync status saved at shutdown.
func ReadSanpshotSyncStatus(db ethdb.KeyValueReader) []byte {
data, _ := db.Get(snapshotSyncStatusKey)
return data
}
// WriteSnapshotSyncStatus stores the serialized sync status to save at shutdown.
func WriteSnapshotSyncStatus(db ethdb.KeyValueWriter, status []byte) {
if err := db.Put(snapshotSyncStatusKey, status); err != nil {
log.Crit("Failed to store snapshot sync status", "err", err)
}
}
// DeleteSnapshotSyncStatus deletes the serialized sync status saved at the last
// shutdown
func DeleteSnapshotSyncStatus(db ethdb.KeyValueWriter) {
if err := db.Delete(snapshotSyncStatusKey); err != nil {
log.Crit("Failed to remove snapshot sync status", "err", err)
}
}
...@@ -57,6 +57,9 @@ var ( ...@@ -57,6 +57,9 @@ var (
// snapshotRecoveryKey tracks the snapshot recovery marker across restarts. // snapshotRecoveryKey tracks the snapshot recovery marker across restarts.
snapshotRecoveryKey = []byte("SnapshotRecovery") snapshotRecoveryKey = []byte("SnapshotRecovery")
// snapshotSyncStatusKey tracks the snapshot sync status across restarts.
snapshotSyncStatusKey = []byte("SnapshotSyncStatus")
// txIndexTailKey tracks the oldest block whose transactions have been indexed. // txIndexTailKey tracks the oldest block whose transactions have been indexed.
txIndexTailKey = []byte("TransactionIndexTail") txIndexTailKey = []byte("TransactionIndexTail")
......
...@@ -241,7 +241,7 @@ func (dl *diskLayer) generate(stats *generatorStats) { ...@@ -241,7 +241,7 @@ func (dl *diskLayer) generate(stats *generatorStats) {
if acc.Root != emptyRoot { if acc.Root != emptyRoot {
storeTrie, err := trie.NewSecure(acc.Root, dl.triedb) storeTrie, err := trie.NewSecure(acc.Root, dl.triedb)
if err != nil { if err != nil {
log.Error("Generator failed to access storage trie", "accroot", dl.root, "acchash", common.BytesToHash(accIt.Key), "stroot", acc.Root, "err", err) log.Error("Generator failed to access storage trie", "root", dl.root, "account", accountHash, "stroot", acc.Root, "err", err)
abort := <-dl.genAbort abort := <-dl.genAbort
abort <- stats abort <- stats
return return
......
...@@ -314,14 +314,19 @@ func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { ...@@ -314,14 +314,19 @@ func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
return common.Hash{} return common.Hash{}
} }
// GetProof returns the MerkleProof for a given Account // GetProof returns the Merkle proof for a given account.
func (s *StateDB) GetProof(a common.Address) ([][]byte, error) { func (s *StateDB) GetProof(addr common.Address) ([][]byte, error) {
return s.GetProofByHash(crypto.Keccak256Hash(addr.Bytes()))
}
// GetProofByHash returns the Merkle proof for a given account.
func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) {
var proof proofList var proof proofList
err := s.trie.Prove(crypto.Keccak256(a.Bytes()), 0, &proof) err := s.trie.Prove(addrHash[:], 0, &proof)
return proof, err return proof, err
} }
// GetStorageProof returns the StorageProof for given key // GetStorageProof returns the Merkle proof for given storage slot.
func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) { func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
var proof proofList var proof proofList
trie := s.StorageTrie(a) trie := s.StorageTrie(a)
...@@ -332,6 +337,17 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, ...@@ -332,6 +337,17 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte,
return proof, err return proof, err
} }
// GetStorageProofByHash returns the Merkle proof for given storage slot.
func (s *StateDB) GetStorageProofByHash(a common.Address, key common.Hash) ([][]byte, error) {
var proof proofList
trie := s.StorageTrie(a)
if trie == nil {
return proof, errors.New("storage trie for requested address does not exist")
}
err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
return proof, err
}
// GetCommittedState retrieves a value from the given account's committed storage trie. // GetCommittedState retrieves a value from the given account's committed storage trie.
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
stateObject := s.getStateObject(addr) stateObject := s.getStateObject(addr)
......
...@@ -56,7 +56,7 @@ func (b *EthAPIBackend) CurrentBlock() *types.Block { ...@@ -56,7 +56,7 @@ func (b *EthAPIBackend) CurrentBlock() *types.Block {
} }
func (b *EthAPIBackend) SetHead(number uint64) { func (b *EthAPIBackend) SetHead(number uint64) {
b.eth.protocolManager.downloader.Cancel() b.eth.handler.downloader.Cancel()
b.eth.blockchain.SetHead(number) b.eth.blockchain.SetHead(number)
} }
...@@ -272,10 +272,6 @@ func (b *EthAPIBackend) Downloader() *downloader.Downloader { ...@@ -272,10 +272,6 @@ func (b *EthAPIBackend) Downloader() *downloader.Downloader {
return b.eth.Downloader() return b.eth.Downloader()
} }
func (b *EthAPIBackend) ProtocolVersion() int {
return b.eth.EthVersion()
}
func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
return b.gpo.SuggestPrice(ctx) return b.gpo.SuggestPrice(ctx)
} }
......
...@@ -57,6 +57,8 @@ func (h resultHash) Swap(i, j int) { h[i], h[j] = h[j], h[i] } ...@@ -57,6 +57,8 @@ func (h resultHash) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h resultHash) Less(i, j int) bool { return bytes.Compare(h[i].Bytes(), h[j].Bytes()) < 0 } func (h resultHash) Less(i, j int) bool { return bytes.Compare(h[i].Bytes(), h[j].Bytes()) < 0 }
func TestAccountRange(t *testing.T) { func TestAccountRange(t *testing.T) {
t.Parallel()
var ( var (
statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), nil) statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), nil)
state, _ = state.New(common.Hash{}, statedb, nil) state, _ = state.New(common.Hash{}, statedb, nil)
...@@ -126,6 +128,8 @@ func TestAccountRange(t *testing.T) { ...@@ -126,6 +128,8 @@ func TestAccountRange(t *testing.T) {
} }
func TestEmptyAccountRange(t *testing.T) { func TestEmptyAccountRange(t *testing.T) {
t.Parallel()
var ( var (
statedb = state.NewDatabase(rawdb.NewMemoryDatabase()) statedb = state.NewDatabase(rawdb.NewMemoryDatabase())
state, _ = state.New(common.Hash{}, statedb, nil) state, _ = state.New(common.Hash{}, statedb, nil)
...@@ -142,6 +146,8 @@ func TestEmptyAccountRange(t *testing.T) { ...@@ -142,6 +146,8 @@ func TestEmptyAccountRange(t *testing.T) {
} }
func TestStorageRangeAt(t *testing.T) { func TestStorageRangeAt(t *testing.T) {
t.Parallel()
// Create a state where account 0x010000... has a few storage entries. // Create a state where account 0x010000... has a few storage entries.
var ( var (
state, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) state, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
......
...@@ -40,6 +40,8 @@ import ( ...@@ -40,6 +40,8 @@ import (
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
...@@ -48,7 +50,6 @@ import ( ...@@ -48,7 +50,6 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
...@@ -59,10 +60,11 @@ type Ethereum struct { ...@@ -59,10 +60,11 @@ type Ethereum struct {
config *Config config *Config
// Handlers // Handlers
txPool *core.TxPool txPool *core.TxPool
blockchain *core.BlockChain blockchain *core.BlockChain
protocolManager *ProtocolManager handler *handler
dialCandidates enode.Iterator ethDialCandidates enode.Iterator
snapDialCandidates enode.Iterator
// DB interfaces // DB interfaces
chainDb ethdb.Database // Block chain database chainDb ethdb.Database // Block chain database
...@@ -145,7 +147,7 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { ...@@ -145,7 +147,7 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) {
if bcVersion != nil { if bcVersion != nil {
dbVer = fmt.Sprintf("%d", *bcVersion) dbVer = fmt.Sprintf("%d", *bcVersion)
} }
log.Info("Initialising Ethereum protocol", "versions", ProtocolVersions, "network", config.NetworkId, "dbversion", dbVer) log.Info("Initialising Ethereum protocol", "network", config.NetworkId, "dbversion", dbVer)
if !config.SkipBcVersionCheck { if !config.SkipBcVersionCheck {
if bcVersion != nil && *bcVersion > core.BlockChainVersion { if bcVersion != nil && *bcVersion > core.BlockChainVersion {
...@@ -196,7 +198,17 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { ...@@ -196,7 +198,17 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) {
if checkpoint == nil { if checkpoint == nil {
checkpoint = params.TrustedCheckpoints[genesisHash] checkpoint = params.TrustedCheckpoints[genesisHash]
} }
if eth.protocolManager, err = NewProtocolManager(chainConfig, checkpoint, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, cacheLimit, config.Whitelist); err != nil { if eth.handler, err = newHandler(&handlerConfig{
Database: chainDb,
Chain: eth.blockchain,
TxPool: eth.txPool,
Network: config.NetworkId,
Sync: config.SyncMode,
BloomCache: uint64(cacheLimit),
EventMux: eth.eventMux,
Checkpoint: checkpoint,
Whitelist: config.Whitelist,
}); err != nil {
return nil, err return nil, err
} }
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
...@@ -209,13 +221,16 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { ...@@ -209,13 +221,16 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) {
} }
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
eth.dialCandidates, err = eth.setupDiscovery() eth.ethDialCandidates, err = setupDiscovery(eth.config.EthDiscoveryURLs)
if err != nil {
return nil, err
}
eth.snapDialCandidates, err = setupDiscovery(eth.config.SnapDiscoveryURLs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Start the RPC service // Start the RPC service
eth.netRPCService = ethapi.NewPublicNetAPI(eth.p2pServer, eth.NetVersion()) eth.netRPCService = ethapi.NewPublicNetAPI(eth.p2pServer)
// Register the backend on the node // Register the backend on the node
stack.RegisterAPIs(eth.APIs()) stack.RegisterAPIs(eth.APIs())
...@@ -310,7 +325,7 @@ func (s *Ethereum) APIs() []rpc.API { ...@@ -310,7 +325,7 @@ func (s *Ethereum) APIs() []rpc.API {
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux),
Public: true, Public: true,
}, { }, {
Namespace: "miner", Namespace: "miner",
...@@ -473,7 +488,7 @@ func (s *Ethereum) StartMining(threads int) error { ...@@ -473,7 +488,7 @@ func (s *Ethereum) StartMining(threads int) error {
} }
// If mining is started, we can disable the transaction rejection mechanism // If mining is started, we can disable the transaction rejection mechanism
// introduced to speed sync times. // introduced to speed sync times.
atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) atomic.StoreUint32(&s.handler.acceptTxs, 1)
go s.miner.Start(eb) go s.miner.Start(eb)
} }
...@@ -504,21 +519,17 @@ func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } ...@@ -504,21 +519,17 @@ func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
func (s *Ethereum) Engine() consensus.Engine { return s.engine } func (s *Ethereum) Engine() consensus.Engine { return s.engine }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) EthVersion() int { return int(ProtocolVersions[0]) } func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader }
func (s *Ethereum) NetVersion() uint64 { return s.networkID } func (s *Ethereum) Synced() bool { return atomic.LoadUint32(&s.handler.acceptTxs) == 1 }
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
func (s *Ethereum) Synced() bool { return atomic.LoadUint32(&s.protocolManager.acceptTxs) == 1 }
func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning } func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning }
func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer } func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer }
// Protocols returns all the currently configured // Protocols returns all the currently configured
// network protocols to start. // network protocols to start.
func (s *Ethereum) Protocols() []p2p.Protocol { func (s *Ethereum) Protocols() []p2p.Protocol {
protos := make([]p2p.Protocol, len(ProtocolVersions)) protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.ethDialCandidates)
for i, vsn := range ProtocolVersions { if s.config.SnapshotCache > 0 {
protos[i] = s.protocolManager.makeProtocol(vsn) protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...)
protos[i].Attributes = []enr.Entry{s.currentEthEntry()}
protos[i].DialCandidates = s.dialCandidates
} }
return protos return protos
} }
...@@ -526,7 +537,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol { ...@@ -526,7 +537,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
// Start implements node.Lifecycle, starting all internal goroutines needed by the // Start implements node.Lifecycle, starting all internal goroutines needed by the
// Ethereum protocol implementation. // Ethereum protocol implementation.
func (s *Ethereum) Start() error { func (s *Ethereum) Start() error {
s.startEthEntryUpdate(s.p2pServer.LocalNode()) eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode())
// Start the bloom bits servicing goroutines // Start the bloom bits servicing goroutines
s.startBloomHandlers(params.BloomBitsBlocks) s.startBloomHandlers(params.BloomBitsBlocks)
...@@ -540,7 +551,7 @@ func (s *Ethereum) Start() error { ...@@ -540,7 +551,7 @@ func (s *Ethereum) Start() error {
maxPeers -= s.config.LightPeers maxPeers -= s.config.LightPeers
} }
// Start the networking layer and the light server if requested // Start the networking layer and the light server if requested
s.protocolManager.Start(maxPeers) s.handler.Start(maxPeers)
return nil return nil
} }
...@@ -548,7 +559,7 @@ func (s *Ethereum) Start() error { ...@@ -548,7 +559,7 @@ func (s *Ethereum) Start() error {
// Ethereum protocol. // Ethereum protocol.
func (s *Ethereum) Stop() error { func (s *Ethereum) Stop() error {
// Stop all the peer-related stuff first. // Stop all the peer-related stuff first.
s.protocolManager.Stop() s.handler.Stop()
// Then stop everything else. // Then stop everything else.
s.bloomIndexer.Close() s.bloomIndexer.Close()
...@@ -560,5 +571,6 @@ func (s *Ethereum) Stop() error { ...@@ -560,5 +571,6 @@ func (s *Ethereum) Stop() error {
rawdb.PopUncleanShutdownMarker(s.chainDb) rawdb.PopUncleanShutdownMarker(s.chainDb)
s.chainDb.Close() s.chainDb.Close()
s.eventMux.Stop() s.eventMux.Stop()
return nil return nil
} }
...@@ -115,7 +115,8 @@ type Config struct { ...@@ -115,7 +115,8 @@ type Config struct {
// This can be set to list of enrtree:// URLs which will be queried for // This can be set to list of enrtree:// URLs which will be queried for
// for nodes to connect to. // for nodes to connect to.
DiscoveryURLs []string EthDiscoveryURLs []string
SnapDiscoveryURLs []string
NoPruning bool // Whether to disable pruning and flush everything to disk NoPruning bool // Whether to disable pruning and flush everything to disk
NoPrefetch bool // Whether to disable prefetching and only load state on demand NoPrefetch bool // Whether to disable prefetching and only load state on demand
......
...@@ -63,11 +63,12 @@ func (eth *Ethereum) currentEthEntry() *ethEntry { ...@@ -63,11 +63,12 @@ func (eth *Ethereum) currentEthEntry() *ethEntry {
eth.blockchain.CurrentHeader().Number.Uint64())} eth.blockchain.CurrentHeader().Number.Uint64())}
} }
// setupDiscovery creates the node discovery source for the eth protocol. // setupDiscovery creates the node discovery source for the `eth` and `snap`
func (eth *Ethereum) setupDiscovery() (enode.Iterator, error) { // protocols.
if len(eth.config.DiscoveryURLs) == 0 { func setupDiscovery(urls []string) (enode.Iterator, error) {
if len(urls) == 0 {
return nil, nil return nil, nil
} }
client := dnsdisc.NewClient(dnsdisc.Config{}) client := dnsdisc.NewClient(dnsdisc.Config{})
return client.NewIterator(eth.config.DiscoveryURLs...) return client.NewIterator(urls...)
} }
...@@ -29,6 +29,7 @@ import ( ...@@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -38,7 +39,6 @@ import ( ...@@ -38,7 +39,6 @@ import (
) )
var ( var (
MaxHashFetch = 512 // Amount of hashes to be fetched per retrieval request
MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request
MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request
MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly
...@@ -89,7 +89,7 @@ var ( ...@@ -89,7 +89,7 @@ var (
errCancelContentProcessing = errors.New("content processing canceled (requested)") errCancelContentProcessing = errors.New("content processing canceled (requested)")
errCanceled = errors.New("syncing canceled (requested)") errCanceled = errors.New("syncing canceled (requested)")
errNoSyncActive = errors.New("no sync active") errNoSyncActive = errors.New("no sync active")
errTooOld = errors.New("peer doesn't speak recent enough protocol version (need version >= 63)") errTooOld = errors.New("peer doesn't speak recent enough protocol version (need version >= 64)")
) )
type Downloader struct { type Downloader struct {
...@@ -131,20 +131,22 @@ type Downloader struct { ...@@ -131,20 +131,22 @@ type Downloader struct {
ancientLimit uint64 // The maximum block number which can be regarded as ancient data. ancientLimit uint64 // The maximum block number which can be regarded as ancient data.
// Channels // Channels
headerCh chan dataPack // [eth/62] Channel receiving inbound block headers headerCh chan dataPack // Channel receiving inbound block headers
bodyCh chan dataPack // [eth/62] Channel receiving inbound block bodies bodyCh chan dataPack // Channel receiving inbound block bodies
receiptCh chan dataPack // [eth/63] Channel receiving inbound receipts receiptCh chan dataPack // Channel receiving inbound receipts
bodyWakeCh chan bool // [eth/62] Channel to signal the block body fetcher of new tasks bodyWakeCh chan bool // Channel to signal the block body fetcher of new tasks
receiptWakeCh chan bool // [eth/63] Channel to signal the receipt fetcher of new tasks receiptWakeCh chan bool // Channel to signal the receipt fetcher of new tasks
headerProcCh chan []*types.Header // [eth/62] Channel to feed the header processor new tasks headerProcCh chan []*types.Header // Channel to feed the header processor new tasks
// State sync // State sync
pivotHeader *types.Header // Pivot block header to dynamically push the syncing state root pivotHeader *types.Header // Pivot block header to dynamically push the syncing state root
pivotLock sync.RWMutex // Lock protecting pivot header reads from updates 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 stateSyncStart chan *stateSync
trackStateReq chan *stateReq trackStateReq chan *stateReq
stateCh chan dataPack // [eth/63] Channel receiving inbound node state data stateCh chan dataPack // Channel receiving inbound node state data
// Cancellation and termination // Cancellation and termination
cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop) cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop)
...@@ -237,6 +239,7 @@ func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom, ...@@ -237,6 +239,7 @@ func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom,
headerProcCh: make(chan []*types.Header, 1), headerProcCh: make(chan []*types.Header, 1),
quitCh: make(chan struct{}), quitCh: make(chan struct{}),
stateCh: make(chan dataPack), stateCh: make(chan dataPack),
SnapSyncer: snap.NewSyncer(stateDb, stateBloom),
stateSyncStart: make(chan *stateSync), stateSyncStart: make(chan *stateSync),
syncStatsState: stateSyncStats{ syncStatsState: stateSyncStats{
processed: rawdb.ReadFastTrieProgress(stateDb), processed: rawdb.ReadFastTrieProgress(stateDb),
...@@ -286,19 +289,16 @@ func (d *Downloader) Synchronising() bool { ...@@ -286,19 +289,16 @@ func (d *Downloader) Synchronising() bool {
return atomic.LoadInt32(&d.synchronising) > 0 return atomic.LoadInt32(&d.synchronising) > 0
} }
// SyncBloomContains tests if the syncbloom filter contains the given hash:
// - false: the bloom definitely does not contain hash
// - true: the bloom maybe contains hash
//
// While the bloom is being initialized (or is closed), all queries will return true.
func (d *Downloader) SyncBloomContains(hash []byte) bool {
return d.stateBloom == nil || d.stateBloom.Contains(hash)
}
// RegisterPeer injects a new download peer into the set of block source to be // RegisterPeer injects a new download peer into the set of block source to be
// used for fetching hashes and blocks from. // used for fetching hashes and blocks from.
func (d *Downloader) RegisterPeer(id string, version int, peer Peer) error { func (d *Downloader) RegisterPeer(id string, version uint, peer Peer) error {
logger := log.New("peer", id) var logger log.Logger
if len(id) < 16 {
// Tests use short IDs, don't choke on them
logger = log.New("peer", id)
} else {
logger = log.New("peer", id[:16])
}
logger.Trace("Registering sync peer") logger.Trace("Registering sync peer")
if err := d.peers.Register(newPeerConnection(id, version, peer, logger)); err != nil { if err := d.peers.Register(newPeerConnection(id, version, peer, logger)); err != nil {
logger.Error("Failed to register sync peer", "err", err) logger.Error("Failed to register sync peer", "err", err)
...@@ -310,7 +310,7 @@ func (d *Downloader) RegisterPeer(id string, version int, peer Peer) error { ...@@ -310,7 +310,7 @@ func (d *Downloader) RegisterPeer(id string, version int, peer Peer) error {
} }
// RegisterLightPeer injects a light client peer, wrapping it so it appears as a regular peer. // RegisterLightPeer injects a light client peer, wrapping it so it appears as a regular peer.
func (d *Downloader) RegisterLightPeer(id string, version int, peer LightPeer) error { func (d *Downloader) RegisterLightPeer(id string, version uint, peer LightPeer) error {
return d.RegisterPeer(id, version, &lightPeerWrapper{peer}) return d.RegisterPeer(id, version, &lightPeerWrapper{peer})
} }
...@@ -319,7 +319,13 @@ func (d *Downloader) RegisterLightPeer(id string, version int, peer LightPeer) e ...@@ -319,7 +319,13 @@ func (d *Downloader) RegisterLightPeer(id string, version int, peer LightPeer) e
// the queue. // the queue.
func (d *Downloader) UnregisterPeer(id string) error { func (d *Downloader) UnregisterPeer(id string) error {
// Unregister the peer from the active peer set and revoke any fetch tasks // Unregister the peer from the active peer set and revoke any fetch tasks
logger := log.New("peer", id) var logger log.Logger
if len(id) < 16 {
// Tests use short IDs, don't choke on them
logger = log.New("peer", id)
} else {
logger = log.New("peer", id[:16])
}
logger.Trace("Unregistering sync peer") logger.Trace("Unregistering sync peer")
if err := d.peers.Unregister(id); err != nil { if err := d.peers.Unregister(id); err != nil {
logger.Error("Failed to unregister sync peer", "err", err) logger.Error("Failed to unregister sync peer", "err", err)
...@@ -381,6 +387,16 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode ...@@ -381,6 +387,16 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
if mode == FullSync && d.stateBloom != nil { if mode == FullSync && d.stateBloom != nil {
d.stateBloom.Close() d.stateBloom.Close()
} }
// If snap sync was requested, create the snap scheduler and switch to fast
// sync mode. Long term we could drop fast sync or merge the two together,
// but until snap becomes prevalent, we should support both. TODO(karalabe).
if mode == SnapSync {
if !d.snapSync {
log.Warn("Enabling snapshot sync prototype")
d.snapSync = true
}
mode = FastSync
}
// Reset the queue, peer set and wake channels to clean any internal leftover state // Reset the queue, peer set and wake channels to clean any internal leftover state
d.queue.Reset(blockCacheMaxItems, blockCacheInitialItems) d.queue.Reset(blockCacheMaxItems, blockCacheInitialItems)
d.peers.Reset() d.peers.Reset()
...@@ -443,8 +459,8 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I ...@@ -443,8 +459,8 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
d.mux.Post(DoneEvent{latest}) d.mux.Post(DoneEvent{latest})
} }
}() }()
if p.version < 63 { if p.version < 64 {
return errTooOld return fmt.Errorf("%w, peer version: %d", errTooOld, p.version)
} }
mode := d.getMode() mode := d.getMode()
...@@ -1910,27 +1926,53 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error { ...@@ -1910,27 +1926,53 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error {
// DeliverHeaders injects a new batch of block headers received from a remote // DeliverHeaders injects a new batch of block headers received from a remote
// node into the download schedule. // node into the download schedule.
func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) (err error) { func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) error {
return d.deliver(id, d.headerCh, &headerPack{id, headers}, headerInMeter, headerDropMeter) return d.deliver(d.headerCh, &headerPack{id, headers}, headerInMeter, headerDropMeter)
} }
// DeliverBodies injects a new batch of block bodies received from a remote node. // DeliverBodies injects a new batch of block bodies received from a remote node.
func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, uncles [][]*types.Header) (err error) { func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, uncles [][]*types.Header) error {
return d.deliver(id, d.bodyCh, &bodyPack{id, transactions, uncles}, bodyInMeter, bodyDropMeter) return d.deliver(d.bodyCh, &bodyPack{id, transactions, uncles}, bodyInMeter, bodyDropMeter)
} }
// DeliverReceipts injects a new batch of receipts received from a remote node. // DeliverReceipts injects a new batch of receipts received from a remote node.
func (d *Downloader) DeliverReceipts(id string, receipts [][]*types.Receipt) (err error) { func (d *Downloader) DeliverReceipts(id string, receipts [][]*types.Receipt) error {
return d.deliver(id, d.receiptCh, &receiptPack{id, receipts}, receiptInMeter, receiptDropMeter) return d.deliver(d.receiptCh, &receiptPack{id, receipts}, receiptInMeter, receiptDropMeter)
} }
// DeliverNodeData injects a new batch of node state data received from a remote node. // DeliverNodeData injects a new batch of node state data received from a remote node.
func (d *Downloader) DeliverNodeData(id string, data [][]byte) (err error) { func (d *Downloader) DeliverNodeData(id string, data [][]byte) error {
return d.deliver(id, d.stateCh, &statePack{id, data}, stateInMeter, stateDropMeter) return d.deliver(d.stateCh, &statePack{id, data}, stateInMeter, stateDropMeter)
}
// DeliverSnapPacket is invoked from a peer's message handler when it transmits a
// data packet for the local node to consume.
func (d *Downloader) DeliverSnapPacket(peer *snap.Peer, packet snap.Packet) error {
switch packet := packet.(type) {
case *snap.AccountRangePacket:
hashes, accounts, err := packet.Unpack()
if err != nil {
return err
}
return d.SnapSyncer.OnAccounts(peer, packet.ID, hashes, accounts, packet.Proof)
case *snap.StorageRangesPacket:
hashset, slotset := packet.Unpack()
return d.SnapSyncer.OnStorage(peer, packet.ID, hashset, slotset, packet.Proof)
case *snap.ByteCodesPacket:
return d.SnapSyncer.OnByteCodes(peer, packet.ID, packet.Codes)
case *snap.TrieNodesPacket:
return d.SnapSyncer.OnTrieNodes(peer, packet.ID, packet.Nodes)
default:
return fmt.Errorf("unexpected snap packet type: %T", packet)
}
} }
// deliver injects a new batch of data received from a remote node. // deliver injects a new batch of data received from a remote node.
func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) { func (d *Downloader) deliver(destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) {
// Update the delivery metrics for both good and failed deliveries // Update the delivery metrics for both good and failed deliveries
inMeter.Mark(int64(packet.Items())) inMeter.Mark(int64(packet.Items()))
defer func() { defer func() {
......
This diff is collapsed.
...@@ -24,7 +24,8 @@ type SyncMode uint32 ...@@ -24,7 +24,8 @@ type SyncMode uint32
const ( const (
FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks
FastSync // Quickly download the headers, full sync only at the chain head FastSync // Quickly download the headers, full sync only at the chain
SnapSync // Download the chain and the state via compact snashots
LightSync // Download only the headers and terminate afterwards LightSync // Download only the headers and terminate afterwards
) )
...@@ -39,6 +40,8 @@ func (mode SyncMode) String() string { ...@@ -39,6 +40,8 @@ func (mode SyncMode) String() string {
return "full" return "full"
case FastSync: case FastSync:
return "fast" return "fast"
case SnapSync:
return "snap"
case LightSync: case LightSync:
return "light" return "light"
default: default:
...@@ -52,6 +55,8 @@ func (mode SyncMode) MarshalText() ([]byte, error) { ...@@ -52,6 +55,8 @@ func (mode SyncMode) MarshalText() ([]byte, error) {
return []byte("full"), nil return []byte("full"), nil
case FastSync: case FastSync:
return []byte("fast"), nil return []byte("fast"), nil
case SnapSync:
return []byte("snap"), nil
case LightSync: case LightSync:
return []byte("light"), nil return []byte("light"), nil
default: default:
...@@ -65,6 +70,8 @@ func (mode *SyncMode) UnmarshalText(text []byte) error { ...@@ -65,6 +70,8 @@ func (mode *SyncMode) UnmarshalText(text []byte) error {
*mode = FullSync *mode = FullSync
case "fast": case "fast":
*mode = FastSync *mode = FastSync
case "snap":
*mode = SnapSync
case "light": case "light":
*mode = LightSync *mode = LightSync
default: default:
......
...@@ -69,7 +69,7 @@ type peerConnection struct { ...@@ -69,7 +69,7 @@ type peerConnection struct {
peer Peer peer Peer
version int // Eth protocol version number to switch strategies version uint // Eth protocol version number to switch strategies
log log.Logger // Contextual logger to add extra infos to peer logs log log.Logger // Contextual logger to add extra infos to peer logs
lock sync.RWMutex lock sync.RWMutex
} }
...@@ -112,7 +112,7 @@ func (w *lightPeerWrapper) RequestNodeData([]common.Hash) error { ...@@ -112,7 +112,7 @@ func (w *lightPeerWrapper) RequestNodeData([]common.Hash) error {
} }
// newPeerConnection creates a new downloader peer. // newPeerConnection creates a new downloader peer.
func newPeerConnection(id string, version int, peer Peer, logger log.Logger) *peerConnection { func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection {
return &peerConnection{ return &peerConnection{
id: id, id: id,
lacking: make(map[common.Hash]struct{}), lacking: make(map[common.Hash]struct{}),
...@@ -457,7 +457,7 @@ func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) { ...@@ -457,7 +457,7 @@ func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) {
defer p.lock.RUnlock() defer p.lock.RUnlock()
return p.headerThroughput return p.headerThroughput
} }
return ps.idlePeers(63, 65, idle, throughput) return ps.idlePeers(64, 65, idle, throughput)
} }
// BodyIdlePeers retrieves a flat list of all the currently body-idle peers within // BodyIdlePeers retrieves a flat list of all the currently body-idle peers within
...@@ -471,7 +471,7 @@ func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) { ...@@ -471,7 +471,7 @@ func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) {
defer p.lock.RUnlock() defer p.lock.RUnlock()
return p.blockThroughput return p.blockThroughput
} }
return ps.idlePeers(63, 65, idle, throughput) return ps.idlePeers(64, 65, idle, throughput)
} }
// ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers // ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers
...@@ -485,7 +485,7 @@ func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) { ...@@ -485,7 +485,7 @@ func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) {
defer p.lock.RUnlock() defer p.lock.RUnlock()
return p.receiptThroughput return p.receiptThroughput
} }
return ps.idlePeers(63, 65, idle, throughput) return ps.idlePeers(64, 65, idle, throughput)
} }
// NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle // NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle
...@@ -499,13 +499,13 @@ func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) { ...@@ -499,13 +499,13 @@ func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) {
defer p.lock.RUnlock() defer p.lock.RUnlock()
return p.stateThroughput return p.stateThroughput
} }
return ps.idlePeers(63, 65, idle, throughput) return ps.idlePeers(64, 65, idle, throughput)
} }
// idlePeers retrieves a flat list of all currently idle peers satisfying the // idlePeers retrieves a flat list of all currently idle peers satisfying the
// protocol version constraints, using the provided function to check idleness. // protocol version constraints, using the provided function to check idleness.
// The resulting set of peers are sorted by their measure throughput. // The resulting set of peers are sorted by their measure throughput.
func (ps *peerSet) idlePeers(minProtocol, maxProtocol int, idleCheck func(*peerConnection) bool, throughput func(*peerConnection) float64) ([]*peerConnection, int) { func (ps *peerSet) idlePeers(minProtocol, maxProtocol uint, idleCheck func(*peerConnection) bool, throughput func(*peerConnection) float64) ([]*peerConnection, int) {
ps.lock.RLock() ps.lock.RLock()
defer ps.lock.RUnlock() defer ps.lock.RUnlock()
......
...@@ -113,24 +113,24 @@ type queue struct { ...@@ -113,24 +113,24 @@ type queue struct {
mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching
// Headers are "special", they download in batches, supported by a skeleton chain // Headers are "special", they download in batches, supported by a skeleton chain
headerHead common.Hash // [eth/62] Hash of the last queued header to verify order headerHead common.Hash // Hash of the last queued header to verify order
headerTaskPool map[uint64]*types.Header // [eth/62] Pending header retrieval tasks, mapping starting indexes to skeleton headers headerTaskPool map[uint64]*types.Header // Pending header retrieval tasks, mapping starting indexes to skeleton headers
headerTaskQueue *prque.Prque // [eth/62] Priority queue of the skeleton indexes to fetch the filling headers for headerTaskQueue *prque.Prque // Priority queue of the skeleton indexes to fetch the filling headers for
headerPeerMiss map[string]map[uint64]struct{} // [eth/62] Set of per-peer header batches known to be unavailable headerPeerMiss map[string]map[uint64]struct{} // Set of per-peer header batches known to be unavailable
headerPendPool map[string]*fetchRequest // [eth/62] Currently pending header retrieval operations headerPendPool map[string]*fetchRequest // Currently pending header retrieval operations
headerResults []*types.Header // [eth/62] Result cache accumulating the completed headers headerResults []*types.Header // Result cache accumulating the completed headers
headerProced int // [eth/62] Number of headers already processed from the results headerProced int // Number of headers already processed from the results
headerOffset uint64 // [eth/62] Number of the first header in the result cache headerOffset uint64 // Number of the first header in the result cache
headerContCh chan bool // [eth/62] Channel to notify when header download finishes headerContCh chan bool // Channel to notify when header download finishes
// All data retrievals below are based on an already assembles header chain // All data retrievals below are based on an already assembles header chain
blockTaskPool map[common.Hash]*types.Header // [eth/62] Pending block (body) retrieval tasks, mapping hashes to headers blockTaskPool map[common.Hash]*types.Header // Pending block (body) retrieval tasks, mapping hashes to headers
blockTaskQueue *prque.Prque // [eth/62] Priority queue of the headers to fetch the blocks (bodies) for blockTaskQueue *prque.Prque // Priority queue of the headers to fetch the blocks (bodies) for
blockPendPool map[string]*fetchRequest // [eth/62] Currently pending block (body) retrieval operations blockPendPool map[string]*fetchRequest // Currently pending block (body) retrieval operations
receiptTaskPool map[common.Hash]*types.Header // [eth/63] Pending receipt retrieval tasks, mapping hashes to headers receiptTaskPool map[common.Hash]*types.Header // Pending receipt retrieval tasks, mapping hashes to headers
receiptTaskQueue *prque.Prque // [eth/63] Priority queue of the headers to fetch the receipts for receiptTaskQueue *prque.Prque // Priority queue of the headers to fetch the receipts for
receiptPendPool map[string]*fetchRequest // [eth/63] Currently pending receipt retrieval operations receiptPendPool map[string]*fetchRequest // Currently pending receipt retrieval operations
resultCache *resultStore // Downloaded but not yet delivered fetch results resultCache *resultStore // Downloaded but not yet delivered fetch results
resultSize common.StorageSize // Approximate size of a block (exponential moving average) resultSize common.StorageSize // Approximate size of a block (exponential moving average)
...@@ -690,6 +690,13 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh ...@@ -690,6 +690,13 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
var logger log.Logger
if len(id) < 16 {
// Tests use short IDs, don't choke on them
logger = log.New("peer", id)
} else {
logger = log.New("peer", id[:16])
}
// Short circuit if the data was never requested // Short circuit if the data was never requested
request := q.headerPendPool[id] request := q.headerPendPool[id]
if request == nil { if request == nil {
...@@ -704,10 +711,10 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh ...@@ -704,10 +711,10 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh
accepted := len(headers) == MaxHeaderFetch accepted := len(headers) == MaxHeaderFetch
if accepted { if accepted {
if headers[0].Number.Uint64() != request.From { if headers[0].Number.Uint64() != request.From {
log.Trace("First header broke chain ordering", "peer", id, "number", headers[0].Number, "hash", headers[0].Hash(), request.From) logger.Trace("First header broke chain ordering", "number", headers[0].Number, "hash", headers[0].Hash(), "expected", request.From)
accepted = false accepted = false
} else if headers[len(headers)-1].Hash() != target { } else if headers[len(headers)-1].Hash() != target {
log.Trace("Last header broke skeleton structure ", "peer", id, "number", headers[len(headers)-1].Number, "hash", headers[len(headers)-1].Hash(), "expected", target) logger.Trace("Last header broke skeleton structure ", "number", headers[len(headers)-1].Number, "hash", headers[len(headers)-1].Hash(), "expected", target)
accepted = false accepted = false
} }
} }
...@@ -716,12 +723,12 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh ...@@ -716,12 +723,12 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh
for i, header := range headers[1:] { for i, header := range headers[1:] {
hash := header.Hash() hash := header.Hash()
if want := request.From + 1 + uint64(i); header.Number.Uint64() != want { if want := request.From + 1 + uint64(i); header.Number.Uint64() != want {
log.Warn("Header broke chain ordering", "peer", id, "number", header.Number, "hash", hash, "expected", want) logger.Warn("Header broke chain ordering", "number", header.Number, "hash", hash, "expected", want)
accepted = false accepted = false
break break
} }
if parentHash != header.ParentHash { if parentHash != header.ParentHash {
log.Warn("Header broke chain ancestry", "peer", id, "number", header.Number, "hash", hash) logger.Warn("Header broke chain ancestry", "number", header.Number, "hash", hash)
accepted = false accepted = false
break break
} }
...@@ -731,7 +738,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh ...@@ -731,7 +738,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh
} }
// If the batch of headers wasn't accepted, mark as unavailable // If the batch of headers wasn't accepted, mark as unavailable
if !accepted { if !accepted {
log.Trace("Skeleton filling not accepted", "peer", id, "from", request.From) logger.Trace("Skeleton filling not accepted", "from", request.From)
miss := q.headerPeerMiss[id] miss := q.headerPeerMiss[id]
if miss == nil { if miss == nil {
...@@ -758,7 +765,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh ...@@ -758,7 +765,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh
select { select {
case headerProcCh <- process: case headerProcCh <- process:
log.Trace("Pre-scheduled new headers", "peer", id, "count", len(process), "from", process[0].Number) logger.Trace("Pre-scheduled new headers", "count", len(process), "from", process[0].Number)
q.headerProced += len(process) q.headerProced += len(process)
default: default:
} }
......
...@@ -101,8 +101,16 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync { ...@@ -101,8 +101,16 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync {
finished []*stateReq // Completed or failed requests finished []*stateReq // Completed or failed requests
timeout = make(chan *stateReq) // Timed out active requests timeout = make(chan *stateReq) // Timed out active requests
) )
// Run the state sync.
log.Trace("State sync starting", "root", s.root) log.Trace("State sync starting", "root", s.root)
defer func() {
// Cancel active request timers on exit. Also set peers to idle so they're
// available for the next sync.
for _, req := range active {
req.timer.Stop()
req.peer.SetNodeDataIdle(int(req.nItems), time.Now())
}
}()
go s.run() go s.run()
defer s.Cancel() defer s.Cancel()
...@@ -252,8 +260,9 @@ func (d *Downloader) spindownStateSync(active map[string]*stateReq, finished []* ...@@ -252,8 +260,9 @@ func (d *Downloader) spindownStateSync(active map[string]*stateReq, finished []*
type stateSync struct { type stateSync struct {
d *Downloader // Downloader instance to access and manage current peerset d *Downloader // Downloader instance to access and manage current peerset
sched *trie.Sync // State trie sync scheduler defining the tasks root common.Hash // State root currently being synced
keccak hash.Hash // Keccak256 hasher to verify deliveries with sched *trie.Sync // State trie sync scheduler defining the tasks
keccak hash.Hash // Keccak256 hasher to verify deliveries with
trieTasks map[common.Hash]*trieTask // Set of trie node tasks currently queued for retrieval trieTasks map[common.Hash]*trieTask // Set of trie node tasks currently queued for retrieval
codeTasks map[common.Hash]*codeTask // Set of byte code tasks currently queued for retrieval codeTasks map[common.Hash]*codeTask // Set of byte code tasks currently queued for retrieval
...@@ -268,8 +277,6 @@ type stateSync struct { ...@@ -268,8 +277,6 @@ type stateSync struct {
cancelOnce sync.Once // Ensures cancel only ever gets called once cancelOnce sync.Once // Ensures cancel only ever gets called once
done chan struct{} // Channel to signal termination completion done chan struct{} // Channel to signal termination completion
err error // Any error hit during sync (set before completion) err error // Any error hit during sync (set before completion)
root common.Hash
} }
// trieTask represents a single trie node download task, containing a set of // trieTask represents a single trie node download task, containing a set of
...@@ -290,6 +297,7 @@ type codeTask struct { ...@@ -290,6 +297,7 @@ type codeTask struct {
func newStateSync(d *Downloader, root common.Hash) *stateSync { func newStateSync(d *Downloader, root common.Hash) *stateSync {
return &stateSync{ return &stateSync{
d: d, d: d,
root: root,
sched: state.NewStateSync(root, d.stateDB, d.stateBloom), sched: state.NewStateSync(root, d.stateDB, d.stateBloom),
keccak: sha3.NewLegacyKeccak256(), keccak: sha3.NewLegacyKeccak256(),
trieTasks: make(map[common.Hash]*trieTask), trieTasks: make(map[common.Hash]*trieTask),
...@@ -298,7 +306,6 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync { ...@@ -298,7 +306,6 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync {
cancel: make(chan struct{}), cancel: make(chan struct{}),
done: make(chan struct{}), done: make(chan struct{}),
started: make(chan struct{}), started: make(chan struct{}),
root: root,
} }
} }
...@@ -306,7 +313,12 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync { ...@@ -306,7 +313,12 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync {
// it finishes, and finally notifying any goroutines waiting for the loop to // it finishes, and finally notifying any goroutines waiting for the loop to
// finish. // finish.
func (s *stateSync) run() { func (s *stateSync) run() {
s.err = s.loop() close(s.started)
if s.d.snapSync {
s.err = s.d.SnapSyncer.Sync(s.root, s.cancel)
} else {
s.err = s.loop()
}
close(s.done) close(s.done)
} }
...@@ -318,7 +330,9 @@ func (s *stateSync) Wait() error { ...@@ -318,7 +330,9 @@ func (s *stateSync) Wait() error {
// Cancel cancels the sync and waits until it has shut down. // Cancel cancels the sync and waits until it has shut down.
func (s *stateSync) Cancel() error { func (s *stateSync) Cancel() error {
s.cancelOnce.Do(func() { close(s.cancel) }) s.cancelOnce.Do(func() {
close(s.cancel)
})
return s.Wait() return s.Wait()
} }
...@@ -329,7 +343,6 @@ func (s *stateSync) Cancel() error { ...@@ -329,7 +343,6 @@ func (s *stateSync) Cancel() error {
// pushed here async. The reason is to decouple processing from data receipt // pushed here async. The reason is to decouple processing from data receipt
// and timeouts. // and timeouts.
func (s *stateSync) loop() (err error) { func (s *stateSync) loop() (err error) {
close(s.started)
// Listen for new peer events to assign tasks to them // Listen for new peer events to assign tasks to them
newPeer := make(chan *peerConnection, 1024) newPeer := make(chan *peerConnection, 1024)
peerSub := s.d.peers.SubscribeNewPeers(newPeer) peerSub := s.d.peers.SubscribeNewPeers(newPeer)
......
...@@ -20,7 +20,7 @@ func (c Config) MarshalTOML() (interface{}, error) { ...@@ -20,7 +20,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
Genesis *core.Genesis `toml:",omitempty"` Genesis *core.Genesis `toml:",omitempty"`
NetworkId uint64 NetworkId uint64
SyncMode downloader.SyncMode SyncMode downloader.SyncMode
DiscoveryURLs []string EthDiscoveryURLs []string
NoPruning bool NoPruning bool
NoPrefetch bool NoPrefetch bool
TxLookupLimit uint64 `toml:",omitempty"` TxLookupLimit uint64 `toml:",omitempty"`
...@@ -61,7 +61,7 @@ func (c Config) MarshalTOML() (interface{}, error) { ...@@ -61,7 +61,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.Genesis = c.Genesis enc.Genesis = c.Genesis
enc.NetworkId = c.NetworkId enc.NetworkId = c.NetworkId
enc.SyncMode = c.SyncMode enc.SyncMode = c.SyncMode
enc.DiscoveryURLs = c.DiscoveryURLs enc.EthDiscoveryURLs = c.EthDiscoveryURLs
enc.NoPruning = c.NoPruning enc.NoPruning = c.NoPruning
enc.NoPrefetch = c.NoPrefetch enc.NoPrefetch = c.NoPrefetch
enc.TxLookupLimit = c.TxLookupLimit enc.TxLookupLimit = c.TxLookupLimit
...@@ -106,7 +106,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { ...@@ -106,7 +106,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
Genesis *core.Genesis `toml:",omitempty"` Genesis *core.Genesis `toml:",omitempty"`
NetworkId *uint64 NetworkId *uint64
SyncMode *downloader.SyncMode SyncMode *downloader.SyncMode
DiscoveryURLs []string EthDiscoveryURLs []string
NoPruning *bool NoPruning *bool
NoPrefetch *bool NoPrefetch *bool
TxLookupLimit *uint64 `toml:",omitempty"` TxLookupLimit *uint64 `toml:",omitempty"`
...@@ -156,8 +156,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { ...@@ -156,8 +156,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.SyncMode != nil { if dec.SyncMode != nil {
c.SyncMode = *dec.SyncMode c.SyncMode = *dec.SyncMode
} }
if dec.DiscoveryURLs != nil { if dec.EthDiscoveryURLs != nil {
c.DiscoveryURLs = dec.DiscoveryURLs c.EthDiscoveryURLs = dec.EthDiscoveryURLs
} }
if dec.NoPruning != nil { if dec.NoPruning != nil {
c.NoPruning = *dec.NoPruning c.NoPruning = *dec.NoPruning
......
This diff is collapsed.
// Copyright 2015 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 (
"errors"
"fmt"
"math/big"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/trie"
)
// ethHandler implements the eth.Backend interface to handle the various network
// packets that are sent as replies or broadcasts.
type ethHandler handler
func (h *ethHandler) Chain() *core.BlockChain { return h.chain }
func (h *ethHandler) StateBloom() *trie.SyncBloom { return h.stateBloom }
func (h *ethHandler) TxPool() eth.TxPool { return h.txpool }
// RunPeer is invoked when a peer joins on the `eth` protocol.
func (h *ethHandler) RunPeer(peer *eth.Peer, hand eth.Handler) error {
return (*handler)(h).runEthPeer(peer, hand)
}
// PeerInfo retrieves all known `eth` information about a peer.
func (h *ethHandler) PeerInfo(id enode.ID) interface{} {
if p := h.peers.ethPeer(id.String()); p != nil {
return p.info()
}
return nil
}
// AcceptTxs retrieves whether transaction processing is enabled on the node
// or if inbound transactions should simply be dropped.
func (h *ethHandler) AcceptTxs() bool {
return atomic.LoadUint32(&h.acceptTxs) == 1
}
// Handle is invoked from a peer's message handler when it receives a new remote
// message that the handler couldn't consume and serve itself.
func (h *ethHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
// Consume any broadcasts and announces, forwarding the rest to the downloader
switch packet := packet.(type) {
case *eth.BlockHeadersPacket:
return h.handleHeaders(peer, *packet)
case *eth.BlockBodiesPacket:
txset, uncleset := packet.Unpack()
return h.handleBodies(peer, txset, uncleset)
case *eth.NodeDataPacket:
if err := h.downloader.DeliverNodeData(peer.ID(), *packet); err != nil {
log.Debug("Failed to deliver node state data", "err", err)
}
return nil
case *eth.ReceiptsPacket:
if err := h.downloader.DeliverReceipts(peer.ID(), *packet); err != nil {
log.Debug("Failed to deliver receipts", "err", err)
}
return nil
case *eth.NewBlockHashesPacket:
hashes, numbers := packet.Unpack()
return h.handleBlockAnnounces(peer, hashes, numbers)
case *eth.NewBlockPacket:
return h.handleBlockBroadcast(peer, packet.Block, packet.TD)
case *eth.NewPooledTransactionHashesPacket:
return h.txFetcher.Notify(peer.ID(), *packet)
case *eth.TransactionsPacket:
return h.txFetcher.Enqueue(peer.ID(), *packet, false)
case *eth.PooledTransactionsPacket:
return h.txFetcher.Enqueue(peer.ID(), *packet, true)
default:
return fmt.Errorf("unexpected eth packet type: %T", packet)
}
}
// handleHeaders is invoked from a peer's message handler when it transmits a batch
// of headers for the local node to process.
func (h *ethHandler) handleHeaders(peer *eth.Peer, headers []*types.Header) error {
p := h.peers.ethPeer(peer.ID())
if p == nil {
return errors.New("unregistered during callback")
}
// If no headers were received, but we're expencting a checkpoint header, consider it that
if len(headers) == 0 && p.syncDrop != nil {
// Stop the timer either way, decide later to drop or not
p.syncDrop.Stop()
p.syncDrop = nil
// If we're doing a fast (or snap) sync, we must enforce the checkpoint block to avoid
// eclipse attacks. Unsynced nodes are welcome to connect after we're done
// joining the network
if atomic.LoadUint32(&h.fastSync) == 1 {
peer.Log().Warn("Dropping unsynced node during sync", "addr", peer.RemoteAddr(), "type", peer.Name())
return errors.New("unsynced node cannot serve sync")
}
}
// Filter out any explicitly requested headers, deliver the rest to the downloader
filter := len(headers) == 1
if filter {
// If it's a potential sync progress check, validate the content and advertised chain weight
if p.syncDrop != nil && headers[0].Number.Uint64() == h.checkpointNumber {
// Disable the sync drop timer
p.syncDrop.Stop()
p.syncDrop = nil
// Validate the header and either drop the peer or continue
if headers[0].Hash() != h.checkpointHash {
return errors.New("checkpoint hash mismatch")
}
return nil
}
// Otherwise if it's a whitelisted block, validate against the set
if want, ok := h.whitelist[headers[0].Number.Uint64()]; ok {
if hash := headers[0].Hash(); want != hash {
peer.Log().Info("Whitelist mismatch, dropping peer", "number", headers[0].Number.Uint64(), "hash", hash, "want", want)
return errors.New("whitelist block mismatch")
}
peer.Log().Debug("Whitelist block verified", "number", headers[0].Number.Uint64(), "hash", want)
}
// Irrelevant of the fork checks, send the header to the fetcher just in case
headers = h.blockFetcher.FilterHeaders(peer.ID(), headers, time.Now())
}
if len(headers) > 0 || !filter {
err := h.downloader.DeliverHeaders(peer.ID(), headers)
if err != nil {
log.Debug("Failed to deliver headers", "err", err)
}
}
return nil
}
// handleBodies is invoked from a peer's message handler when it transmits a batch
// of block bodies for the local node to process.
func (h *ethHandler) handleBodies(peer *eth.Peer, txs [][]*types.Transaction, uncles [][]*types.Header) error {
// Filter out any explicitly requested bodies, deliver the rest to the downloader
filter := len(txs) > 0 || len(uncles) > 0
if filter {
txs, uncles = h.blockFetcher.FilterBodies(peer.ID(), txs, uncles, time.Now())
}
if len(txs) > 0 || len(uncles) > 0 || !filter {
err := h.downloader.DeliverBodies(peer.ID(), txs, uncles)
if err != nil {
log.Debug("Failed to deliver bodies", "err", err)
}
}
return nil
}
// handleBlockAnnounces is invoked from a peer's message handler when it transmits a
// batch of block announcements for the local node to process.
func (h *ethHandler) handleBlockAnnounces(peer *eth.Peer, hashes []common.Hash, numbers []uint64) error {
// Schedule all the unknown hashes for retrieval
var (
unknownHashes = make([]common.Hash, 0, len(hashes))
unknownNumbers = make([]uint64, 0, len(numbers))
)
for i := 0; i < len(hashes); i++ {
if !h.chain.HasBlock(hashes[i], numbers[i]) {
unknownHashes = append(unknownHashes, hashes[i])
unknownNumbers = append(unknownNumbers, numbers[i])
}
}
for i := 0; i < len(unknownHashes); i++ {
h.blockFetcher.Notify(peer.ID(), unknownHashes[i], unknownNumbers[i], time.Now(), peer.RequestOneHeader, peer.RequestBodies)
}
return nil
}
// handleBlockBroadcast is invoked from a peer's message handler when it transmits a
// block broadcast for the local node to process.
func (h *ethHandler) handleBlockBroadcast(peer *eth.Peer, block *types.Block, td *big.Int) error {
// Schedule the block for import
h.blockFetcher.Enqueue(peer.ID(), block)
// Assuming the block is importable by the peer, but possibly not yet done so,
// calculate the head hash and TD that the peer truly must have.
var (
trueHead = block.ParentHash()
trueTD = new(big.Int).Sub(td, block.Difficulty())
)
// Update the peer's total difficulty if better than the previous
if _, td := peer.Head(); trueTD.Cmp(td) > 0 {
peer.SetHead(trueHead, trueTD)
h.chainSync.handlePeerEvent(peer)
}
return nil
}
This diff is collapsed.
// 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/eth/protocols/snap"
"github.com/ethereum/go-ethereum/p2p/enode"
)
// snapHandler implements the snap.Backend interface to handle the various network
// packets that are sent as replies or broadcasts.
type snapHandler handler
func (h *snapHandler) Chain() *core.BlockChain { return h.chain }
// RunPeer is invoked when a peer joins on the `snap` protocol.
func (h *snapHandler) RunPeer(peer *snap.Peer, hand snap.Handler) error {
return (*handler)(h).runSnapPeer(peer, hand)
}
// PeerInfo retrieves all known `snap` information about a peer.
func (h *snapHandler) PeerInfo(id enode.ID) interface{} {
if p := h.peers.snapPeer(id.String()); p != nil {
return p.info()
}
return nil
}
// Handle is invoked from a peer's message handler when it receives a new remote
// message that the handler couldn't consume and serve itself.
func (h *snapHandler) Handle(peer *snap.Peer, packet snap.Packet) error {
return h.downloader.DeliverSnapPacket(peer, packet)
}
This diff is collapsed.
// Copyright 2015 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/>.
// This file contains some shares testing functionality, common to multiple
// different files and modules being tested.
package eth
import (
"crypto/ecdsa"
"crypto/rand"
"fmt"
"math/big"
"sort"
"sync"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
)
var (
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testBank = crypto.PubkeyToAddress(testBankKey.PublicKey)
)
// newTestProtocolManager creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification
// channels for different events.
func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, ethdb.Database, error) {
var (
evmux = new(event.TypeMux)
engine = ethash.NewFaker()
db = rawdb.NewMemoryDatabase()
gspec = &core.Genesis{
Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}},
}
genesis = gspec.MustCommit(db)
blockchain, _ = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
)
chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil {
panic(err)
}
pm, err := NewProtocolManager(gspec.Config, nil, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx, pool: make(map[common.Hash]*types.Transaction)}, engine, blockchain, db, 1, nil)
if err != nil {
return nil, nil, err
}
pm.Start(1000)
return pm, db, nil
}
// newTestProtocolManagerMust creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification
// channels for different events. In case of an error, the constructor force-
// fails the test.
func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, ethdb.Database) {
pm, db, err := newTestProtocolManager(mode, blocks, generator, newtx)
if err != nil {
t.Fatalf("Failed to create protocol manager: %v", err)
}
return pm, db
}
// testTxPool is a fake, helper transaction pool for testing purposes
type testTxPool struct {
txFeed event.Feed
pool map[common.Hash]*types.Transaction // Hash map of collected transactions
added chan<- []*types.Transaction // Notification channel for new transactions
lock sync.RWMutex // Protects the transaction pool
}
// Has returns an indicator whether txpool has a transaction
// cached with the given hash.
func (p *testTxPool) Has(hash common.Hash) bool {
p.lock.Lock()
defer p.lock.Unlock()
return p.pool[hash] != nil
}
// Get retrieves the transaction from local txpool with given
// tx hash.
func (p *testTxPool) Get(hash common.Hash) *types.Transaction {
p.lock.Lock()
defer p.lock.Unlock()
return p.pool[hash]
}
// AddRemotes appends a batch of transactions to the pool, and notifies any
// listeners if the addition channel is non nil
func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error {
p.lock.Lock()
defer p.lock.Unlock()
for _, tx := range txs {
p.pool[tx.Hash()] = tx
}
if p.added != nil {
p.added <- txs
}
p.txFeed.Send(core.NewTxsEvent{Txs: txs})
return make([]error, len(txs))
}
// Pending returns all the transactions known to the pool
func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
p.lock.RLock()
defer p.lock.RUnlock()
batches := make(map[common.Address]types.Transactions)
for _, tx := range p.pool {
from, _ := types.Sender(types.HomesteadSigner{}, tx)
batches[from] = append(batches[from], tx)
}
for _, batch := range batches {
sort.Sort(types.TxByNonce(batch))
}
return batches, nil
}
func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return p.txFeed.Subscribe(ch)
}
// newTestTransaction create a new dummy transaction.
func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction {
tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, datasize))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, from)
return tx
}
// testPeer is a simulated peer to allow testing direct network calls.
type testPeer struct {
net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging
app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side
*peer
}
// newTestPeer creates a new peer registered at the given protocol manager.
func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*testPeer, <-chan error) {
// Create a message pipe to communicate through
app, net := p2p.MsgPipe()
// Start the peer on a new thread
var id enode.ID
rand.Read(id[:])
peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net, pm.txpool.Get)
errc := make(chan error, 1)
go func() { errc <- pm.runPeer(peer) }()
tp := &testPeer{app: app, net: net, peer: peer}
// Execute any implicitly requested handshakes and return
if shake {
var (
genesis = pm.blockchain.Genesis()
head = pm.blockchain.CurrentHeader()
td = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
)
forkID := forkid.NewID(pm.blockchain.Config(), pm.blockchain.Genesis().Hash(), pm.blockchain.CurrentHeader().Number.Uint64())
tp.handshake(nil, td, head.Hash(), genesis.Hash(), forkID, forkid.NewFilter(pm.blockchain))
}
return tp, errc
}
// handshake simulates a trivial handshake that expects the same state from the
// remote side as we are simulating locally.
func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) {
var msg interface{}
switch {
case p.version == eth63:
msg = &statusData63{
ProtocolVersion: uint32(p.version),
NetworkId: DefaultConfig.NetworkId,
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
}
case p.version >= eth64:
msg = &statusData{
ProtocolVersion: uint32(p.version),
NetworkID: DefaultConfig.NetworkId,
TD: td,
Head: head,
Genesis: genesis,
ForkID: forkID,
}
default:
panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version))
}
if err := p2p.ExpectMsg(p.app, StatusMsg, msg); err != nil {
t.Fatalf("status recv: %v", err)
}
if err := p2p.Send(p.app, StatusMsg, msg); err != nil {
t.Fatalf("status send: %v", err)
}
}
// close terminates the local side of the peer, notifying the remote protocol
// manager of termination.
func (p *testPeer) close() {
p.app.Close()
}
This diff is collapsed.
// 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 (
"errors"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p"
)
var (
// errPeerSetClosed is returned if a peer is attempted to be added or removed
// from the peer set after it has been terminated.
errPeerSetClosed = errors.New("peerset closed")
// errPeerAlreadyRegistered is returned if a peer is attempted to be added
// to the peer set, but one with the same id already exists.
errPeerAlreadyRegistered = errors.New("peer already registered")
// errPeerNotRegistered is returned if a peer is attempted to be removed from
// a peer set, but no peer with the given id exists.
errPeerNotRegistered = errors.New("peer not registered")
// ethConnectTimeout is the `snap` timeout for `eth` to connect too.
ethConnectTimeout = 3 * time.Second
)
// peerSet represents the collection of active peers currently participating in
// the `eth` or `snap` protocols.
type peerSet struct {
ethPeers map[string]*ethPeer // Peers connected on the `eth` protocol
snapPeers map[string]*snapPeer // Peers connected on the `snap` protocol
ethJoinFeed event.Feed // Events when an `eth` peer successfully joins
ethDropFeed event.Feed // Events when an `eth` peer gets dropped
snapJoinFeed event.Feed // Events when a `snap` peer joins on both `eth` and `snap`
snapDropFeed event.Feed // Events when a `snap` peer gets dropped (only if fully joined)
scope event.SubscriptionScope // Subscription group to unsubscribe everyone at once
lock sync.RWMutex
closed bool
}
// newPeerSet creates a new peer set to track the active participants.
func newPeerSet() *peerSet {
return &peerSet{
ethPeers: make(map[string]*ethPeer),
snapPeers: make(map[string]*snapPeer),
}
}
// subscribeEthJoin registers a subscription for peers joining (and completing
// the handshake) on the `eth` protocol.
func (ps *peerSet) subscribeEthJoin(ch chan<- *eth.Peer) event.Subscription {
return ps.scope.Track(ps.ethJoinFeed.Subscribe(ch))
}
// subscribeEthDrop registers a subscription for peers being dropped from the
// `eth` protocol.
func (ps *peerSet) subscribeEthDrop(ch chan<- *eth.Peer) event.Subscription {
return ps.scope.Track(ps.ethDropFeed.Subscribe(ch))
}
// subscribeSnapJoin registers a subscription for peers joining (and completing
// the `eth` join) on the `snap` protocol.
func (ps *peerSet) subscribeSnapJoin(ch chan<- *snap.Peer) event.Subscription {
return ps.scope.Track(ps.snapJoinFeed.Subscribe(ch))
}
// subscribeSnapDrop registers a subscription for peers being dropped from the
// `snap` protocol.
func (ps *peerSet) subscribeSnapDrop(ch chan<- *snap.Peer) event.Subscription {
return ps.scope.Track(ps.snapDropFeed.Subscribe(ch))
}
// registerEthPeer injects a new `eth` peer into the working set, or returns an
// error if the peer is already known. The peer is announced on the `eth` join
// feed and if it completes a pending `snap` peer, also on that feed.
func (ps *peerSet) registerEthPeer(peer *eth.Peer) error {
ps.lock.Lock()
if ps.closed {
ps.lock.Unlock()
return errPeerSetClosed
}
id := peer.ID()
if _, ok := ps.ethPeers[id]; ok {
ps.lock.Unlock()
return errPeerAlreadyRegistered
}
ps.ethPeers[id] = &ethPeer{Peer: peer}
snap, ok := ps.snapPeers[id]
ps.lock.Unlock()
if ok {
// Previously dangling `snap` peer, stop it's timer since `eth` connected
snap.lock.Lock()
if snap.ethDrop != nil {
snap.ethDrop.Stop()
snap.ethDrop = nil
}
snap.lock.Unlock()
}
ps.ethJoinFeed.Send(peer)
if ok {
ps.snapJoinFeed.Send(snap.Peer)
}
return nil
}
// unregisterEthPeer removes a remote peer from the active set, disabling any further
// actions to/from that particular entity. The drop is announced on the `eth` drop
// feed and also on the `snap` feed if the eth/snap duality was broken just now.
func (ps *peerSet) unregisterEthPeer(id string) error {
ps.lock.Lock()
eth, ok := ps.ethPeers[id]
if !ok {
ps.lock.Unlock()
return errPeerNotRegistered
}
delete(ps.ethPeers, id)
snap, ok := ps.snapPeers[id]
ps.lock.Unlock()
ps.ethDropFeed.Send(eth)
if ok {
ps.snapDropFeed.Send(snap)
}
return nil
}
// registerSnapPeer injects a new `snap` peer into the working set, or returns
// an error if the peer is already known. The peer is announced on the `snap`
// join feed if it completes an existing `eth` peer.
//
// If the peer isn't yet connected on `eth` and fails to do so within a given
// amount of time, it is dropped. This enforces that `snap` is an extension to
// `eth`, not a standalone leeching protocol.
func (ps *peerSet) registerSnapPeer(peer *snap.Peer) error {
ps.lock.Lock()
if ps.closed {
ps.lock.Unlock()
return errPeerSetClosed
}
id := peer.ID()
if _, ok := ps.snapPeers[id]; ok {
ps.lock.Unlock()
return errPeerAlreadyRegistered
}
ps.snapPeers[id] = &snapPeer{Peer: peer}
_, ok := ps.ethPeers[id]
if !ok {
// Dangling `snap` peer, start a timer to drop if `eth` doesn't connect
ps.snapPeers[id].ethDrop = time.AfterFunc(ethConnectTimeout, func() {
peer.Log().Warn("Snapshot peer missing eth, dropping", "addr", peer.RemoteAddr(), "type", peer.Name())
peer.Disconnect(p2p.DiscUselessPeer)
})
}
ps.lock.Unlock()
if ok {
ps.snapJoinFeed.Send(peer)
}
return nil
}
// unregisterSnapPeer removes a remote peer from the active set, disabling any
// further actions to/from that particular entity. The drop is announced on the
// `snap` drop feed.
func (ps *peerSet) unregisterSnapPeer(id string) error {
ps.lock.Lock()
peer, ok := ps.snapPeers[id]
if !ok {
ps.lock.Unlock()
return errPeerNotRegistered
}
delete(ps.snapPeers, id)
ps.lock.Unlock()
peer.lock.Lock()
if peer.ethDrop != nil {
peer.ethDrop.Stop()
peer.ethDrop = nil
}
peer.lock.Unlock()
ps.snapDropFeed.Send(peer)
return nil
}
// ethPeer retrieves the registered `eth` peer with the given id.
func (ps *peerSet) ethPeer(id string) *ethPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
return ps.ethPeers[id]
}
// snapPeer retrieves the registered `snap` peer with the given id.
func (ps *peerSet) snapPeer(id string) *snapPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
return ps.snapPeers[id]
}
// ethPeersWithoutBlock retrieves a list of `eth` peers that do not have a given
// block in their set of known hashes so it might be propagated to them.
func (ps *peerSet) ethPeersWithoutBlock(hash common.Hash) []*ethPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*ethPeer, 0, len(ps.ethPeers))
for _, p := range ps.ethPeers {
if !p.KnownBlock(hash) {
list = append(list, p)
}
}
return list
}
// ethPeersWithoutTransacion retrieves a list of `eth` peers that do not have a
// given transaction in their set of known hashes.
func (ps *peerSet) ethPeersWithoutTransacion(hash common.Hash) []*ethPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*ethPeer, 0, len(ps.ethPeers))
for _, p := range ps.ethPeers {
if !p.KnownTransaction(hash) {
list = append(list, p)
}
}
return list
}
// Len returns if the current number of `eth` peers in the set. Since the `snap`
// peers are tied to the existnce of an `eth` connection, that will always be a
// subset of `eth`.
func (ps *peerSet) Len() int {
ps.lock.RLock()
defer ps.lock.RUnlock()
return len(ps.ethPeers)
}
// ethPeerWithHighestTD retrieves the known peer with the currently highest total
// difficulty.
func (ps *peerSet) ethPeerWithHighestTD() *eth.Peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
var (
bestPeer *eth.Peer
bestTd *big.Int
)
for _, p := range ps.ethPeers {
if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 {
bestPeer, bestTd = p.Peer, td
}
}
return bestPeer
}
// close disconnects all peers.
func (ps *peerSet) close() {
ps.lock.Lock()
defer ps.lock.Unlock()
for _, p := range ps.ethPeers {
p.Disconnect(p2p.DiscQuitting)
}
for _, p := range ps.snapPeers {
p.Disconnect(p2p.DiscQuitting)
}
ps.closed = true
}
This diff is collapsed.
// Copyright 2019 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 (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
const (
// This is the target size for the packs of transactions or announcements. A
// pack can get larger than this if a single transactions exceeds this size.
maxTxPacketSize = 100 * 1024
)
// blockPropagation is a block propagation event, waiting for its turn in the
// broadcast queue.
type blockPropagation struct {
block *types.Block
td *big.Int
}
// broadcastBlocks is a write loop that multiplexes blocks and block accouncements
// to the remote peer. The goal is to have an async writer that does not lock up
// node internals and at the same time rate limits queued data.
func (p *Peer) broadcastBlocks() {
for {
select {
case prop := <-p.queuedBlocks:
if err := p.SendNewBlock(prop.block, prop.td); err != nil {
return
}
p.Log().Trace("Propagated block", "number", prop.block.Number(), "hash", prop.block.Hash(), "td", prop.td)
case block := <-p.queuedBlockAnns:
if err := p.SendNewBlockHashes([]common.Hash{block.Hash()}, []uint64{block.NumberU64()}); err != nil {
return
}
p.Log().Trace("Announced block", "number", block.Number(), "hash", block.Hash())
case <-p.term:
return
}
}
}
// broadcastTransactions is a write loop that schedules transaction broadcasts
// to the remote peer. The goal is to have an async writer that does not lock up
// node internals and at the same time rate limits queued data.
func (p *Peer) broadcastTransactions() {
var (
queue []common.Hash // Queue of hashes to broadcast as full transactions
done chan struct{} // Non-nil if background broadcaster is running
fail = make(chan error, 1) // Channel used to receive network error
failed bool // Flag whether a send failed, discard everything onward
)
for {
// If there's no in-flight broadcast running, check if a new one is needed
if done == nil && len(queue) > 0 {
// Pile transaction until we reach our allowed network limit
var (
hashes []common.Hash
txs []*types.Transaction
size common.StorageSize
)
for i := 0; i < len(queue) && size < maxTxPacketSize; i++ {
if tx := p.txpool.Get(queue[i]); tx != nil {
txs = append(txs, tx)
size += tx.Size()
}
hashes = append(hashes, queue[i])
}
queue = queue[:copy(queue, queue[len(hashes):])]
// If there's anything available to transfer, fire up an async writer
if len(txs) > 0 {
done = make(chan struct{})
go func() {
if err := p.SendTransactions(txs); err != nil {
fail <- err
return
}
close(done)
p.Log().Trace("Sent transactions", "count", len(txs))
}()
}
}
// Transfer goroutine may or may not have been started, listen for events
select {
case hashes := <-p.txBroadcast:
// If the connection failed, discard all transaction events
if failed {
continue
}
// New batch of transactions to be broadcast, queue them (with cap)
queue = append(queue, hashes...)
if len(queue) > maxQueuedTxs {
// Fancy copy and resize to ensure buffer doesn't grow indefinitely
queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxs:])]
}
case <-done:
done = nil
case <-fail:
failed = true
case <-p.term:
return
}
}
}
// announceTransactions is a write loop that schedules transaction broadcasts
// to the remote peer. The goal is to have an async writer that does not lock up
// node internals and at the same time rate limits queued data.
func (p *Peer) announceTransactions() {
var (
queue []common.Hash // Queue of hashes to announce as transaction stubs
done chan struct{} // Non-nil if background announcer is running
fail = make(chan error, 1) // Channel used to receive network error
failed bool // Flag whether a send failed, discard everything onward
)
for {
// If there's no in-flight announce running, check if a new one is needed
if done == nil && len(queue) > 0 {
// Pile transaction hashes until we reach our allowed network limit
var (
hashes []common.Hash
pending []common.Hash
size common.StorageSize
)
for i := 0; i < len(queue) && size < maxTxPacketSize; i++ {
if p.txpool.Get(queue[i]) != nil {
pending = append(pending, queue[i])
size += common.HashLength
}
hashes = append(hashes, queue[i])
}
queue = queue[:copy(queue, queue[len(hashes):])]
// If there's anything available to transfer, fire up an async writer
if len(pending) > 0 {
done = make(chan struct{})
go func() {
if err := p.sendPooledTransactionHashes(pending); err != nil {
fail <- err
return
}
close(done)
p.Log().Trace("Sent transaction announcements", "count", len(pending))
}()
}
}
// Transfer goroutine may or may not have been started, listen for events
select {
case hashes := <-p.txAnnounce:
// If the connection failed, discard all transaction events
if failed {
continue
}
// New batch of transactions to be broadcast, queue them (with cap)
queue = append(queue, hashes...)
if len(queue) > maxQueuedTxAnns {
// Fancy copy and resize to ensure buffer doesn't grow indefinitely
queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxs:])]
}
case <-done:
done = nil
case <-fail:
failed = true
case <-p.term:
return
}
}
}
// Copyright 2019 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"
)
// enrEntry is the ENR entry which advertises `eth` protocol on the discovery.
type enrEntry 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 enrEntry) ENRKey() string {
return "eth"
}
// StartENRUpdater starts the `eth` ENR updater loop, which listens for chain
// head events and updates the requested node record whenever a fork is passed.
func StartENRUpdater(chain *core.BlockChain, ln *enode.LocalNode) {
var newHead = make(chan core.ChainHeadEvent, 10)
sub := chain.SubscribeChainHeadEvent(newHead)
go func() {
defer sub.Unsubscribe()
for {
select {
case <-newHead:
ln.Set(currentENREntry(chain))
case <-sub.Err():
// Would be nice to sync with Stop, but there is no
// good way to do that.
return
}
}
}()
}
// currentENREntry constructs an `eth` ENR entry based on the current state of the chain.
func currentENREntry(chain *core.BlockChain) *enrEntry {
return &enrEntry{
ForkID: forkid.NewID(chain.Config(), chain.Genesis().Hash(), chain.CurrentHeader().Number.Uint64()),
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// 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 snap
import (
"github.com/ethereum/go-ethereum/rlp"
)
// enrEntry is the ENR entry which advertises `snap` protocol on the discovery.
type enrEntry struct {
// Ignore additional fields (for forward compatibility).
Rest []rlp.RawValue `rlp:"tail"`
}
// ENRKey implements enr.Entry.
func (e enrEntry) ENRKey() string {
return "snap"
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -1040,10 +1040,6 @@ func (r *Resolver) GasPrice(ctx context.Context) (hexutil.Big, error) { ...@@ -1040,10 +1040,6 @@ func (r *Resolver) GasPrice(ctx context.Context) (hexutil.Big, error) {
return hexutil.Big(*price), err return hexutil.Big(*price), err
} }
func (r *Resolver) ProtocolVersion(ctx context.Context) (int32, error) {
return int32(r.backend.ProtocolVersion()), nil
}
func (r *Resolver) ChainID(ctx context.Context) (hexutil.Big, error) { func (r *Resolver) ChainID(ctx context.Context) (hexutil.Big, error) {
return hexutil.Big(*r.backend.ChainConfig().ChainID), nil return hexutil.Big(*r.backend.ChainConfig().ChainID), nil
} }
......
...@@ -310,8 +310,6 @@ const schema string = ` ...@@ -310,8 +310,6 @@ const schema string = `
# GasPrice returns the node's estimate of a gas price sufficient to # GasPrice returns the node's estimate of a gas price sufficient to
# ensure a transaction is mined in a timely fashion. # ensure a transaction is mined in a timely fashion.
gasPrice: BigInt! gasPrice: BigInt!
# ProtocolVersion returns the current wire protocol version number.
protocolVersion: Int!
# Syncing returns information on the current synchronisation state. # Syncing returns information on the current synchronisation state.
syncing: SyncState syncing: SyncState
# ChainID returns the current chain ID for transaction replay protection. # ChainID returns the current chain ID for transaction replay protection.
......
This diff is collapsed.
...@@ -41,7 +41,6 @@ import ( ...@@ -41,7 +41,6 @@ import (
type Backend interface { type Backend interface {
// General Ethereum API // General Ethereum API
Downloader() *downloader.Downloader Downloader() *downloader.Downloader
ProtocolVersion() int
SuggestPrice(ctx context.Context) (*big.Int, error) SuggestPrice(ctx context.Context) (*big.Int, error)
ChainDb() ethdb.Database ChainDb() ethdb.Database
AccountManager() *accounts.Manager AccountManager() *accounts.Manager
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment