Commit 5caff3bc authored by Jeffrey Wilcke's avatar Jeffrey Wilcke

Merge pull request #1351 from karalabe/eth61

Implement eth/61
parents 507869bf d6f2c0a7
...@@ -277,7 +277,6 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso ...@@ -277,7 +277,6 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.ExecFlag, utils.ExecFlag,
utils.WhisperEnabledFlag, utils.WhisperEnabledFlag,
utils.VMDebugFlag, utils.VMDebugFlag,
utils.ProtocolVersionFlag,
utils.NetworkIdFlag, utils.NetworkIdFlag,
utils.RPCCORSDomainFlag, utils.RPCCORSDomainFlag,
utils.VerbosityFlag, utils.VerbosityFlag,
...@@ -644,7 +643,7 @@ func version(c *cli.Context) { ...@@ -644,7 +643,7 @@ func version(c *cli.Context) {
if gitCommit != "" { if gitCommit != "" {
fmt.Println("Git Commit:", gitCommit) fmt.Println("Git Commit:", gitCommit)
} }
fmt.Println("Protocol Version:", c.GlobalInt(utils.ProtocolVersionFlag.Name)) fmt.Println("Protocol Versions:", eth.ProtocolVersions)
fmt.Println("Network Id:", c.GlobalInt(utils.NetworkIdFlag.Name)) fmt.Println("Network Id:", c.GlobalInt(utils.NetworkIdFlag.Name))
fmt.Println("Go Version:", runtime.Version()) fmt.Println("Go Version:", runtime.Version())
fmt.Println("OS:", runtime.GOOS) fmt.Println("OS:", runtime.GOOS)
......
...@@ -82,11 +82,6 @@ var ( ...@@ -82,11 +82,6 @@ var (
Usage: "Data directory to be used", Usage: "Data directory to be used",
Value: DirectoryString{common.DefaultDataDir()}, Value: DirectoryString{common.DefaultDataDir()},
} }
ProtocolVersionFlag = cli.IntFlag{
Name: "protocolversion",
Usage: "ETH protocol version (integer)",
Value: eth.ProtocolVersion,
}
NetworkIdFlag = cli.IntFlag{ NetworkIdFlag = cli.IntFlag{
Name: "networkid", Name: "networkid",
Usage: "Network Id (integer)", Usage: "Network Id (integer)",
...@@ -359,7 +354,6 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { ...@@ -359,7 +354,6 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
return &eth.Config{ return &eth.Config{
Name: common.MakeName(clientID, version), Name: common.MakeName(clientID, version),
DataDir: ctx.GlobalString(DataDirFlag.Name), DataDir: ctx.GlobalString(DataDirFlag.Name),
ProtocolVersion: ctx.GlobalInt(ProtocolVersionFlag.Name),
GenesisNonce: ctx.GlobalInt(GenesisNonceFlag.Name), GenesisNonce: ctx.GlobalInt(GenesisNonceFlag.Name),
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name), BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name),
SkipBcVersionCheck: false, SkipBcVersionCheck: false,
......
...@@ -11,8 +11,6 @@ import ( ...@@ -11,8 +11,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -26,6 +24,7 @@ import ( ...@@ -26,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
...@@ -58,7 +57,6 @@ var ( ...@@ -58,7 +57,6 @@ var (
type Config struct { type Config struct {
Name string Name string
ProtocolVersion int
NetworkId int NetworkId int
GenesisNonce int GenesisNonce int
...@@ -226,7 +224,6 @@ type Ethereum struct { ...@@ -226,7 +224,6 @@ type Ethereum struct {
autodagquit chan bool autodagquit chan bool
etherbase common.Address etherbase common.Address
clientVersion string clientVersion string
ethVersionId int
netVersionId int netVersionId int
shhVersionId int shhVersionId int
} }
...@@ -291,6 +288,11 @@ func New(config *Config) (*Ethereum, error) { ...@@ -291,6 +288,11 @@ func New(config *Config) (*Ethereum, error) {
nodeDb := filepath.Join(config.DataDir, "nodes") nodeDb := filepath.Join(config.DataDir, "nodes")
// Perform database sanity checks // Perform database sanity checks
/*
// The databases were previously tied to protocol versions. Currently we
// are moving away from this decision as approaching Frontier. The below
// check was left in for now but should eventually be just dropped.
d, _ := blockDb.Get([]byte("ProtocolVersion")) d, _ := blockDb.Get([]byte("ProtocolVersion"))
protov := int(common.NewValue(d).Uint()) protov := int(common.NewValue(d).Uint())
if protov != config.ProtocolVersion && protov != 0 { if protov != config.ProtocolVersion && protov != 0 {
...@@ -298,7 +300,8 @@ func New(config *Config) (*Ethereum, error) { ...@@ -298,7 +300,8 @@ func New(config *Config) (*Ethereum, error) {
return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, config.ProtocolVersion, path) return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, config.ProtocolVersion, path)
} }
saveProtocolVersion(blockDb, config.ProtocolVersion) saveProtocolVersion(blockDb, config.ProtocolVersion)
glog.V(logger.Info).Infof("Protocol Version: %v, Network Id: %v", config.ProtocolVersion, config.NetworkId) */
glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v", ProtocolVersions, config.NetworkId)
if !config.SkipBcVersionCheck { if !config.SkipBcVersionCheck {
b, _ := blockDb.Get([]byte("BlockchainVersion")) b, _ := blockDb.Get([]byte("BlockchainVersion"))
...@@ -321,7 +324,6 @@ func New(config *Config) (*Ethereum, error) { ...@@ -321,7 +324,6 @@ func New(config *Config) (*Ethereum, error) {
DataDir: config.DataDir, DataDir: config.DataDir,
etherbase: common.HexToAddress(config.Etherbase), etherbase: common.HexToAddress(config.Etherbase),
clientVersion: config.Name, // TODO should separate from Name clientVersion: config.Name, // TODO should separate from Name
ethVersionId: config.ProtocolVersion,
netVersionId: config.NetworkId, netVersionId: config.NetworkId,
NatSpec: config.NatSpec, NatSpec: config.NatSpec,
MinerThreads: config.MinerThreads, MinerThreads: config.MinerThreads,
...@@ -345,7 +347,7 @@ func New(config *Config) (*Ethereum, error) { ...@@ -345,7 +347,7 @@ func New(config *Config) (*Ethereum, error) {
eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.chainManager, eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockProcessor) eth.chainManager.SetProcessor(eth.blockProcessor)
eth.protocolManager = NewProtocolManager(config.ProtocolVersion, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.chainManager) eth.protocolManager = NewProtocolManager(config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.chainManager)
eth.miner = miner.New(eth, eth.EventMux(), eth.pow) eth.miner = miner.New(eth, eth.EventMux(), eth.pow)
eth.miner.SetGasPrice(config.GasPrice) eth.miner.SetGasPrice(config.GasPrice)
...@@ -358,7 +360,7 @@ func New(config *Config) (*Ethereum, error) { ...@@ -358,7 +360,7 @@ func New(config *Config) (*Ethereum, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
protocols := []p2p.Protocol{eth.protocolManager.SubProtocol} protocols := append([]p2p.Protocol{}, eth.protocolManager.SubProtocols...)
if config.Shh { if config.Shh {
protocols = append(protocols, eth.whisper.Protocol()) protocols = append(protocols, eth.whisper.Protocol())
} }
...@@ -495,7 +497,7 @@ func (s *Ethereum) PeerCount() int { return s.net.PeerCoun ...@@ -495,7 +497,7 @@ func (s *Ethereum) PeerCount() int { return s.net.PeerCoun
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
func (s *Ethereum) ClientVersion() string { return s.clientVersion } func (s *Ethereum) ClientVersion() string { return s.clientVersion }
func (s *Ethereum) EthVersion() int { return s.ethVersionId } func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *Ethereum) NetVersion() int { return s.netVersionId } func (s *Ethereum) NetVersion() int { return s.netVersionId }
func (s *Ethereum) ShhVersion() int { return s.shhVersionId } func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
...@@ -504,7 +506,7 @@ func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolMana ...@@ -504,7 +506,7 @@ func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolMana
func (s *Ethereum) Start() error { func (s *Ethereum) Start() error {
jsonlogger.LogJson(&logger.LogStarting{ jsonlogger.LogJson(&logger.LogStarting{
ClientString: s.net.Name, ClientString: s.net.Name,
ProtocolVersion: ProtocolVersion, ProtocolVersion: s.EthVersion(),
}) })
err := s.net.Start() err := s.net.Start()
if err != nil { if err != nil {
...@@ -560,7 +562,7 @@ done: ...@@ -560,7 +562,7 @@ done:
func (s *Ethereum) StartForTest() { func (s *Ethereum) StartForTest() {
jsonlogger.LogJson(&logger.LogStarting{ jsonlogger.LogJson(&logger.LogStarting{
ClientString: s.net.Name, ClientString: s.net.Name,
ProtocolVersion: ProtocolVersion, ProtocolVersion: s.EthVersion(),
}) })
} }
...@@ -667,14 +669,20 @@ func (self *Ethereum) StopAutoDAG() { ...@@ -667,14 +669,20 @@ func (self *Ethereum) StopAutoDAG() {
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir) glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
} }
func saveProtocolVersion(db common.Database, protov int) { /*
// The databases were previously tied to protocol versions. Currently we
// are moving away from this decision as approaching Frontier. The below
// code was left in for now but should eventually be just dropped.
func saveProtocolVersion(db common.Database, protov int) {
d, _ := db.Get([]byte("ProtocolVersion")) d, _ := db.Get([]byte("ProtocolVersion"))
protocolVersion := common.NewValue(d).Uint() protocolVersion := common.NewValue(d).Uint()
if protocolVersion == 0 { if protocolVersion == 0 {
db.Put([]byte("ProtocolVersion"), common.NewValue(protov).Bytes()) db.Put([]byte("ProtocolVersion"), common.NewValue(protov).Bytes())
} }
} }
*/
func saveBlockchainVersion(db common.Database, bcVersion int) { func saveBlockchainVersion(db common.Database, bcVersion int) {
d, _ := db.Get([]byte("BlockchainVersion")) d, _ := db.Get([]byte("BlockchainVersion"))
......
This diff is collapsed.
This diff is collapsed.
...@@ -15,7 +15,8 @@ import ( ...@@ -15,7 +15,8 @@ import (
"gopkg.in/fatih/set.v0" "gopkg.in/fatih/set.v0"
) )
type hashFetcherFn func(common.Hash) error type relativeHashFetcherFn func(common.Hash) error
type absoluteHashFetcherFn func(uint64, int) error
type blockFetcherFn func([]common.Hash) error type blockFetcherFn func([]common.Hash) error
var ( var (
...@@ -37,20 +38,25 @@ type peer struct { ...@@ -37,20 +38,25 @@ type peer struct {
ignored *set.Set // Set of hashes not to request (didn't have previously) ignored *set.Set // Set of hashes not to request (didn't have previously)
getHashes hashFetcherFn // Method to retrieve a batch of hashes (mockable for testing) getRelHashes relativeHashFetcherFn // Method to retrieve a batch of hashes from an origin hash
getBlocks blockFetcherFn // Method to retrieve a batch of blocks (mockable for testing) getAbsHashes absoluteHashFetcherFn // Method to retrieve a batch of hashes from an absolute position
getBlocks blockFetcherFn // Method to retrieve a batch of blocks
version int // Eth protocol version number to switch strategies
} }
// newPeer create a new downloader peer, with specific hash and block retrieval // newPeer create a new downloader peer, with specific hash and block retrieval
// mechanisms. // mechanisms.
func newPeer(id string, head common.Hash, getHashes hashFetcherFn, getBlocks blockFetcherFn) *peer { func newPeer(id string, version int, head common.Hash, getRelHashes relativeHashFetcherFn, getAbsHashes absoluteHashFetcherFn, getBlocks blockFetcherFn) *peer {
return &peer{ return &peer{
id: id, id: id,
head: head, head: head,
capacity: 1, capacity: 1,
getHashes: getHashes, getRelHashes: getRelHashes,
getAbsHashes: getAbsHashes,
getBlocks: getBlocks, getBlocks: getBlocks,
ignored: set.New(), ignored: set.New(),
version: version,
} }
} }
......
...@@ -40,9 +40,9 @@ type queue struct { ...@@ -40,9 +40,9 @@ type queue struct {
pendPool map[string]*fetchRequest // Currently pending block retrieval operations pendPool map[string]*fetchRequest // Currently pending block retrieval operations
blockPool map[common.Hash]int // Hash-set of the downloaded data blocks, mapping to cache indexes blockPool map[common.Hash]uint64 // Hash-set of the downloaded data blocks, mapping to cache indexes
blockCache []*Block // Downloaded but not yet delivered blocks blockCache []*Block // Downloaded but not yet delivered blocks
blockOffset int // Offset of the first cached block in the block-chain blockOffset uint64 // Offset of the first cached block in the block-chain
lock sync.RWMutex lock sync.RWMutex
} }
...@@ -53,7 +53,7 @@ func newQueue() *queue { ...@@ -53,7 +53,7 @@ func newQueue() *queue {
hashPool: make(map[common.Hash]int), hashPool: make(map[common.Hash]int),
hashQueue: prque.New(), hashQueue: prque.New(),
pendPool: make(map[string]*fetchRequest), pendPool: make(map[string]*fetchRequest),
blockPool: make(map[common.Hash]int), blockPool: make(map[common.Hash]uint64),
blockCache: make([]*Block, blockCacheLimit), blockCache: make([]*Block, blockCacheLimit),
} }
} }
...@@ -69,7 +69,7 @@ func (q *queue) Reset() { ...@@ -69,7 +69,7 @@ func (q *queue) Reset() {
q.pendPool = make(map[string]*fetchRequest) q.pendPool = make(map[string]*fetchRequest)
q.blockPool = make(map[common.Hash]int) q.blockPool = make(map[common.Hash]uint64)
q.blockOffset = 0 q.blockOffset = 0
q.blockCache = make([]*Block, blockCacheLimit) q.blockCache = make([]*Block, blockCacheLimit)
} }
...@@ -130,7 +130,7 @@ func (q *queue) Has(hash common.Hash) bool { ...@@ -130,7 +130,7 @@ func (q *queue) Has(hash common.Hash) bool {
// Insert adds a set of hashes for the download queue for scheduling, returning // Insert adds a set of hashes for the download queue for scheduling, returning
// the new hashes encountered. // the new hashes encountered.
func (q *queue) Insert(hashes []common.Hash) []common.Hash { func (q *queue) Insert(hashes []common.Hash, fifo bool) []common.Hash {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
...@@ -147,8 +147,12 @@ func (q *queue) Insert(hashes []common.Hash) []common.Hash { ...@@ -147,8 +147,12 @@ func (q *queue) Insert(hashes []common.Hash) []common.Hash {
inserts = append(inserts, hash) inserts = append(inserts, hash)
q.hashPool[hash] = q.hashCounter q.hashPool[hash] = q.hashCounter
if fifo {
q.hashQueue.Push(hash, -float32(q.hashCounter)) // Lowest gets schedules first
} else {
q.hashQueue.Push(hash, float32(q.hashCounter)) // Highest gets schedules first q.hashQueue.Push(hash, float32(q.hashCounter)) // Highest gets schedules first
} }
}
return inserts return inserts
} }
...@@ -175,7 +179,7 @@ func (q *queue) GetBlock(hash common.Hash) *Block { ...@@ -175,7 +179,7 @@ func (q *queue) GetBlock(hash common.Hash) *Block {
return nil return nil
} }
// Return the block if it's still available in the cache // Return the block if it's still available in the cache
if q.blockOffset <= index && index < q.blockOffset+len(q.blockCache) { if q.blockOffset <= index && index < q.blockOffset+uint64(len(q.blockCache)) {
return q.blockCache[index-q.blockOffset] return q.blockCache[index-q.blockOffset]
} }
return nil return nil
...@@ -202,7 +206,7 @@ func (q *queue) TakeBlocks() []*Block { ...@@ -202,7 +206,7 @@ func (q *queue) TakeBlocks() []*Block {
for k, n := len(q.blockCache)-len(blocks), len(q.blockCache); k < n; k++ { for k, n := len(q.blockCache)-len(blocks), len(q.blockCache); k < n; k++ {
q.blockCache[k] = nil q.blockCache[k] = nil
} }
q.blockOffset += len(blocks) q.blockOffset += uint64(len(blocks))
return blocks return blocks
} }
...@@ -318,7 +322,7 @@ func (q *queue) Deliver(id string, blocks []*types.Block) (err error) { ...@@ -318,7 +322,7 @@ func (q *queue) Deliver(id string, blocks []*types.Block) (err error) {
continue continue
} }
// If a requested block falls out of the range, the hash chain is invalid // If a requested block falls out of the range, the hash chain is invalid
index := int(block.NumberU64()) - q.blockOffset index := int(int64(block.NumberU64()) - int64(q.blockOffset))
if index >= len(q.blockCache) || index < 0 { if index >= len(q.blockCache) || index < 0 {
return errInvalidChain return errInvalidChain
} }
...@@ -329,7 +333,7 @@ func (q *queue) Deliver(id string, blocks []*types.Block) (err error) { ...@@ -329,7 +333,7 @@ func (q *queue) Deliver(id string, blocks []*types.Block) (err error) {
} }
delete(request.Hashes, hash) delete(request.Hashes, hash)
delete(q.hashPool, hash) delete(q.hashPool, hash)
q.blockPool[hash] = int(block.NumberU64()) q.blockPool[hash] = block.NumberU64()
} }
// Return all failed or missing fetches to the queue // Return all failed or missing fetches to the queue
for hash, index := range request.Hashes { for hash, index := range request.Hashes {
...@@ -346,7 +350,7 @@ func (q *queue) Deliver(id string, blocks []*types.Block) (err error) { ...@@ -346,7 +350,7 @@ func (q *queue) Deliver(id string, blocks []*types.Block) (err error) {
} }
// Prepare configures the block cache offset to allow accepting inbound blocks. // Prepare configures the block cache offset to allow accepting inbound blocks.
func (q *queue) Prepare(offset int) { func (q *queue) Prepare(offset uint64) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
......
This diff is collapsed.
package eth
import (
"github.com/ethereum/go-ethereum/metrics"
)
var (
propTxnInPacketsMeter = metrics.NewMeter("eth/prop/txns/in/packets")
propTxnInTrafficMeter = metrics.NewMeter("eth/prop/txns/in/traffic")
propTxnOutPacketsMeter = metrics.NewMeter("eth/prop/txns/out/packets")
propTxnOutTrafficMeter = metrics.NewMeter("eth/prop/txns/out/traffic")
propHashInPacketsMeter = metrics.NewMeter("eth/prop/hashes/in/packets")
propHashInTrafficMeter = metrics.NewMeter("eth/prop/hashes/in/traffic")
propHashOutPacketsMeter = metrics.NewMeter("eth/prop/hashes/out/packets")
propHashOutTrafficMeter = metrics.NewMeter("eth/prop/hashes/out/traffic")
propBlockInPacketsMeter = metrics.NewMeter("eth/prop/blocks/in/packets")
propBlockInTrafficMeter = metrics.NewMeter("eth/prop/blocks/in/traffic")
propBlockOutPacketsMeter = metrics.NewMeter("eth/prop/blocks/out/packets")
propBlockOutTrafficMeter = metrics.NewMeter("eth/prop/blocks/out/traffic")
reqHashInPacketsMeter = metrics.NewMeter("eth/req/hashes/in/packets")
reqHashInTrafficMeter = metrics.NewMeter("eth/req/hashes/in/traffic")
reqHashOutPacketsMeter = metrics.NewMeter("eth/req/hashes/out/packets")
reqHashOutTrafficMeter = metrics.NewMeter("eth/req/hashes/out/traffic")
reqBlockInPacketsMeter = metrics.NewMeter("eth/req/blocks/in/packets")
reqBlockInTrafficMeter = metrics.NewMeter("eth/req/blocks/in/traffic")
reqBlockOutPacketsMeter = metrics.NewMeter("eth/req/blocks/out/packets")
reqBlockOutTrafficMeter = metrics.NewMeter("eth/req/blocks/out/traffic")
)
...@@ -20,25 +20,18 @@ var ( ...@@ -20,25 +20,18 @@ var (
errNotRegistered = errors.New("peer is not registered") errNotRegistered = errors.New("peer is not registered")
) )
type statusMsgData struct { const (
ProtocolVersion uint32 maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
NetworkId uint32 maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS)
TD *big.Int )
CurrentBlock common.Hash
GenesisBlock common.Hash
}
type getBlockHashesMsgData struct {
Hash common.Hash
Amount uint64
}
type peer struct { type peer struct {
*p2p.Peer *p2p.Peer
rw p2p.MsgReadWriter rw p2p.MsgReadWriter
protv, netid int version int // Protocol version negotiated
network int // Network ID being on
id string id string
...@@ -46,27 +39,21 @@ type peer struct { ...@@ -46,27 +39,21 @@ type peer struct {
td *big.Int td *big.Int
lock sync.RWMutex lock sync.RWMutex
genesis, ourHash common.Hash knownTxs *set.Set // Set of transaction hashes known to be known by this peer
ourTd *big.Int knownBlocks *set.Set // Set of block hashes known to be known by this peer
txHashes *set.Set
blockHashes *set.Set
} }
func newPeer(protv, netid int, genesis, head common.Hash, td *big.Int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
id := p.ID() id := p.ID()
return &peer{ return &peer{
Peer: p, Peer: p,
rw: rw, rw: rw,
genesis: genesis, version: version,
ourHash: head, network: network,
ourTd: td,
protv: protv,
netid: netid,
id: fmt.Sprintf("%x", id[:8]), id: fmt.Sprintf("%x", id[:8]),
txHashes: set.New(), knownTxs: set.New(),
blockHashes: set.New(), knownBlocks: set.New(),
} }
} }
...@@ -103,68 +90,110 @@ func (p *peer) SetTd(td *big.Int) { ...@@ -103,68 +90,110 @@ func (p *peer) SetTd(td *big.Int) {
p.td.Set(td) p.td.Set(td)
} }
// sendTransactions sends transactions to the peer and includes the hashes // MarkBlock marks a block as known for the peer, ensuring that the block will
// in it's tx hash set for future reference. The tx hash will allow the // never be propagated to this particular peer.
// manager to check whether the peer has already received this particular func (p *peer) MarkBlock(hash common.Hash) {
// transaction // If we reached the memory allowance, drop a previously known block hash
func (p *peer) sendTransactions(txs types.Transactions) error { for p.knownBlocks.Size() >= maxKnownBlocks {
for _, tx := range txs { p.knownBlocks.Pop()
p.txHashes.Add(tx.Hash())
} }
p.knownBlocks.Add(hash)
}
// MarkTransaction marks a transaction as known for the peer, ensuring that it
// will never be propagated to this particular peer.
func (p *peer) MarkTransaction(hash common.Hash) {
// If we reached the memory allowance, drop a previously known transaction hash
for p.knownTxs.Size() >= maxKnownTxs {
p.knownTxs.Pop()
}
p.knownTxs.Add(hash)
}
// SendTransactions sends transactions to the peer and includes the hashes
// in its transaction hash set for future reference.
func (p *peer) SendTransactions(txs types.Transactions) error {
propTxnOutPacketsMeter.Mark(1)
for _, tx := range txs {
propTxnOutTrafficMeter.Mark(tx.Size().Int64())
p.knownTxs.Add(tx.Hash())
}
return p2p.Send(p.rw, TxMsg, txs) return p2p.Send(p.rw, TxMsg, txs)
} }
func (p *peer) sendBlockHashes(hashes []common.Hash) error { // SendBlockHashes sends a batch of known hashes to the remote peer.
func (p *peer) SendBlockHashes(hashes []common.Hash) error {
reqHashOutPacketsMeter.Mark(1)
reqHashOutTrafficMeter.Mark(int64(32 * len(hashes)))
return p2p.Send(p.rw, BlockHashesMsg, hashes) return p2p.Send(p.rw, BlockHashesMsg, hashes)
} }
func (p *peer) sendBlocks(blocks []*types.Block) error { // SendBlocks sends a batch of blocks to the remote peer.
func (p *peer) SendBlocks(blocks []*types.Block) error {
reqBlockOutPacketsMeter.Mark(1)
for _, block := range blocks {
reqBlockOutTrafficMeter.Mark(block.Size().Int64())
}
return p2p.Send(p.rw, BlocksMsg, blocks) return p2p.Send(p.rw, BlocksMsg, blocks)
} }
func (p *peer) sendNewBlockHashes(hashes []common.Hash) error { // SendNewBlockHashes announces the availability of a number of blocks through
// a hash notification.
func (p *peer) SendNewBlockHashes(hashes []common.Hash) error {
propHashOutPacketsMeter.Mark(1)
propHashOutTrafficMeter.Mark(int64(32 * len(hashes)))
for _, hash := range hashes { for _, hash := range hashes {
p.blockHashes.Add(hash) p.knownBlocks.Add(hash)
} }
return p2p.Send(p.rw, NewBlockHashesMsg, hashes) return p2p.Send(p.rw, NewBlockHashesMsg, hashes)
} }
func (p *peer) sendNewBlock(block *types.Block) error { // SendNewBlock propagates an entire block to a remote peer.
p.blockHashes.Add(block.Hash()) func (p *peer) SendNewBlock(block *types.Block) error {
propBlockOutPacketsMeter.Mark(1)
propBlockOutTrafficMeter.Mark(block.Size().Int64())
p.knownBlocks.Add(block.Hash())
return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, block.Td}) return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, block.Td})
} }
func (p *peer) sendTransaction(tx *types.Transaction) error { // RequestHashes fetches a batch of hashes from a peer, starting at from, going
p.txHashes.Add(tx.Hash()) // towards the genesis block.
func (p *peer) RequestHashes(from common.Hash) error {
return p2p.Send(p.rw, TxMsg, []*types.Transaction{tx}) glog.V(logger.Debug).Infof("Peer [%s] fetching hashes (%d) from %x...\n", p.id, downloader.MaxHashFetch, from[:4])
return p2p.Send(p.rw, GetBlockHashesMsg, getBlockHashesData{from, uint64(downloader.MaxHashFetch)})
} }
func (p *peer) requestHashes(from common.Hash) error { // RequestHashesFromNumber fetches a batch of hashes from a peer, starting at the
glog.V(logger.Debug).Infof("[%s] fetching hashes (%d) %x...\n", p.id, downloader.MaxHashFetch, from[:4]) // requested block number, going upwards towards the genesis block.
return p2p.Send(p.rw, GetBlockHashesMsg, getBlockHashesMsgData{from, uint64(downloader.MaxHashFetch)}) func (p *peer) RequestHashesFromNumber(from uint64, count int) error {
glog.V(logger.Debug).Infof("Peer [%s] fetching hashes (%d) from #%d...\n", p.id, count, from)
return p2p.Send(p.rw, GetBlockHashesFromNumberMsg, getBlockHashesFromNumberData{from, uint64(count)})
} }
func (p *peer) requestBlocks(hashes []common.Hash) error { // RequestBlocks fetches a batch of blocks corresponding to the specified hashes.
func (p *peer) RequestBlocks(hashes []common.Hash) error {
glog.V(logger.Debug).Infof("[%s] fetching %v blocks\n", p.id, len(hashes)) glog.V(logger.Debug).Infof("[%s] fetching %v blocks\n", p.id, len(hashes))
return p2p.Send(p.rw, GetBlocksMsg, hashes) return p2p.Send(p.rw, GetBlocksMsg, hashes)
} }
func (p *peer) handleStatus() error { // Handshake executes the eth protocol handshake, negotiating version number,
// network IDs, difficulties, head and genesis blocks.
func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) error {
// Send out own handshake in a new thread
errc := make(chan error, 1) errc := make(chan error, 1)
go func() { go func() {
errc <- p2p.Send(p.rw, StatusMsg, &statusMsgData{ errc <- p2p.Send(p.rw, StatusMsg, &statusData{
ProtocolVersion: uint32(p.protv), ProtocolVersion: uint32(p.version),
NetworkId: uint32(p.netid), NetworkId: uint32(p.network),
TD: p.ourTd, TD: td,
CurrentBlock: p.ourHash, CurrentBlock: head,
GenesisBlock: p.genesis, GenesisBlock: genesis,
}) })
}() }()
// In the mean time retrieve the remote status message
// read and handle remote status
msg, err := p.rw.ReadMsg() msg, err := p.rw.ReadMsg()
if err != nil { if err != nil {
return err return err
...@@ -175,31 +204,32 @@ func (p *peer) handleStatus() error { ...@@ -175,31 +204,32 @@ func (p *peer) handleStatus() error {
if msg.Size > ProtocolMaxMsgSize { if msg.Size > ProtocolMaxMsgSize {
return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
} }
// Decode the handshake and make sure everything matches
var status statusMsgData var status statusData
if err := msg.Decode(&status); err != nil { if err := msg.Decode(&status); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
if status.GenesisBlock != genesis {
if status.GenesisBlock != p.genesis { return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesis)
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, p.genesis)
} }
if int(status.NetworkId) != p.network {
if int(status.NetworkId) != p.netid { return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, p.network)
return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, p.netid)
} }
if int(status.ProtocolVersion) != p.version {
if int(status.ProtocolVersion) != p.protv { return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version)
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.protv)
} }
// Set the total difficulty of the peer // Configure the remote peer, and sanity check out handshake too
p.td = status.TD p.td, p.head = status.TD, status.CurrentBlock
// set the best hash of the peer
p.head = status.CurrentBlock
return <-errc return <-errc
} }
// String implements fmt.Stringer.
func (p *peer) String() string {
return fmt.Sprintf("Peer %s [%s]", p.id,
fmt.Sprintf("eth/%2d", p.version),
)
}
// peerSet represents the collection of active peers currently participating in // peerSet represents the collection of active peers currently participating in
// the Ethereum sub-protocol. // the Ethereum sub-protocol.
type peerSet struct { type peerSet struct {
...@@ -264,7 +294,7 @@ func (ps *peerSet) PeersWithoutBlock(hash common.Hash) []*peer { ...@@ -264,7 +294,7 @@ func (ps *peerSet) PeersWithoutBlock(hash common.Hash) []*peer {
list := make([]*peer, 0, len(ps.peers)) list := make([]*peer, 0, len(ps.peers))
for _, p := range ps.peers { for _, p := range ps.peers {
if !p.blockHashes.Has(hash) { if !p.knownBlocks.Has(hash) {
list = append(list, p) list = append(list, p)
} }
} }
...@@ -279,7 +309,7 @@ func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer { ...@@ -279,7 +309,7 @@ func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer {
list := make([]*peer, 0, len(ps.peers)) list := make([]*peer, 0, len(ps.peers))
for _, p := range ps.peers { for _, p := range ps.peers {
if !p.txHashes.Has(hash) { if !p.knownTxs.Has(hash) {
list = append(list, p) list = append(list, p)
} }
} }
......
...@@ -7,11 +7,15 @@ import ( ...@@ -7,11 +7,15 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
) )
// Supported versions of the eth protocol (first is primary).
var ProtocolVersions = []uint{61, 60}
// Number of implemented message corresponding to different protocol versions.
var ProtocolLengths = []uint64{9, 8}
const ( const (
ProtocolVersion = 60
NetworkId = 0 NetworkId = 0
ProtocolLength = uint64(8) ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
ProtocolMaxMsgSize = 10 * 1024 * 1024
) )
// eth protocol message codes // eth protocol message codes
...@@ -24,6 +28,7 @@ const ( ...@@ -24,6 +28,7 @@ const (
GetBlocksMsg GetBlocksMsg
BlocksMsg BlocksMsg
NewBlockMsg NewBlockMsg
GetBlockHashesFromNumberMsg
) )
type errCode int type errCode int
...@@ -72,8 +77,31 @@ type chainManager interface { ...@@ -72,8 +77,31 @@ type chainManager interface {
Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash)
} }
// message structs used for RLP serialization // statusData is the network packet for the status message.
type newBlockMsgData struct { type statusData struct {
ProtocolVersion uint32
NetworkId uint32
TD *big.Int
CurrentBlock common.Hash
GenesisBlock common.Hash
}
// getBlockHashesData is the network packet for the hash based block retrieval
// message.
type getBlockHashesData struct {
Hash common.Hash
Amount uint64
}
// getBlockHashesFromNumberData is the network packet for the number based block
// retrieval message.
type getBlockHashesFromNumberData struct {
Number uint64
Amount uint64
}
// newBlockData is the network packet for the block propagation message.
type newBlockData struct {
Block *types.Block Block *types.Block
TD *big.Int TD *big.Int
} }
...@@ -39,15 +39,15 @@ func TestStatusMsgErrors(t *testing.T) { ...@@ -39,15 +39,15 @@ func TestStatusMsgErrors(t *testing.T) {
wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"), wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"),
}, },
{ {
code: StatusMsg, data: statusMsgData{10, NetworkId, td, currentBlock, genesis}, code: StatusMsg, data: statusData{10, NetworkId, td, currentBlock, genesis},
wantError: errResp(ErrProtocolVersionMismatch, "10 (!= 0)"), wantError: errResp(ErrProtocolVersionMismatch, "10 (!= 0)"),
}, },
{ {
code: StatusMsg, data: statusMsgData{ProtocolVersion, 999, td, currentBlock, genesis}, code: StatusMsg, data: statusData{uint32(ProtocolVersions[0]), 999, td, currentBlock, genesis},
wantError: errResp(ErrNetworkIdMismatch, "999 (!= 0)"), wantError: errResp(ErrNetworkIdMismatch, "999 (!= 0)"),
}, },
{ {
code: StatusMsg, data: statusMsgData{ProtocolVersion, NetworkId, td, currentBlock, common.Hash{3}}, code: StatusMsg, data: statusData{uint32(ProtocolVersions[0]), NetworkId, td, currentBlock, common.Hash{3}},
wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000000000000000000000000000000000000000000000000000 (!= %x)", genesis), wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000000000000000000000000000000000000000000000000000 (!= %x)", genesis),
}, },
} }
...@@ -167,7 +167,7 @@ func newProtocolManagerForTesting(txAdded chan<- []*types.Transaction) *Protocol ...@@ -167,7 +167,7 @@ func newProtocolManagerForTesting(txAdded chan<- []*types.Transaction) *Protocol
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
chain, _ = core.NewChainManager(core.GenesisBlock(0, db), db, db, core.FakePow{}, em) chain, _ = core.NewChainManager(core.GenesisBlock(0, db), db, db, core.FakePow{}, em)
txpool = &fakeTxPool{added: txAdded} txpool = &fakeTxPool{added: txAdded}
pm = NewProtocolManager(ProtocolVersion, 0, em, txpool, core.FakePow{}, chain) pm = NewProtocolManager(0, em, txpool, core.FakePow{}, chain)
) )
pm.Start() pm.Start()
return pm return pm
...@@ -188,7 +188,7 @@ func newTestPeer(pm *ProtocolManager) (*testPeer, <-chan error) { ...@@ -188,7 +188,7 @@ func newTestPeer(pm *ProtocolManager) (*testPeer, <-chan error) {
func (p *testPeer) handshake(t *testing.T) { func (p *testPeer) handshake(t *testing.T) {
td, currentBlock, genesis := p.pm.chainman.Status() td, currentBlock, genesis := p.pm.chainman.Status()
msg := &statusMsgData{ msg := &statusData{
ProtocolVersion: uint32(p.pm.protVer), ProtocolVersion: uint32(p.pm.protVer),
NetworkId: uint32(p.pm.netId), NetworkId: uint32(p.pm.netId),
TD: td, TD: td,
......
...@@ -20,14 +20,6 @@ const ( ...@@ -20,14 +20,6 @@ const (
txsyncPackSize = 100 * 1024 txsyncPackSize = 100 * 1024
) )
// blockAnnounce is the hash notification of the availability of a new block in
// the network.
type blockAnnounce struct {
hash common.Hash
peer *peer
time time.Time
}
type txsync struct { type txsync struct {
p *peer p *peer
txs []*types.Transaction txs []*types.Transaction
...@@ -75,7 +67,7 @@ func (pm *ProtocolManager) txsyncLoop() { ...@@ -75,7 +67,7 @@ func (pm *ProtocolManager) txsyncLoop() {
// Send the pack in the background. // Send the pack in the background.
glog.V(logger.Detail).Infof("%v: sending %d transactions (%v)", s.p.Peer, len(pack.txs), size) glog.V(logger.Detail).Infof("%v: sending %d transactions (%v)", s.p.Peer, len(pack.txs), size)
sending = true sending = true
go func() { done <- pack.p.sendTransactions(pack.txs) }() go func() { done <- pack.p.SendTransactions(pack.txs) }()
} }
// pick chooses the next pending sync. // pick chooses the next pending sync.
......
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