|
|
@ -45,6 +45,10 @@ const ( |
|
|
|
estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
|
|
|
|
estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
|
|
|
|
daoChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the DAO handshake challenge
|
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// errIncompatibleConfig is returned if the requested protocols and configs are
|
|
|
|
// errIncompatibleConfig is returned if the requested protocols and configs are
|
|
|
|
// not compatible (low protocol version restrictions and high requirements).
|
|
|
|
// not compatible (low protocol version restrictions and high requirements).
|
|
|
|
var errIncompatibleConfig = errors.New("incompatible configuration") |
|
|
|
var errIncompatibleConfig = errors.New("incompatible configuration") |
|
|
@ -62,9 +66,10 @@ type ProtocolManager struct { |
|
|
|
fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks)
|
|
|
|
fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks)
|
|
|
|
synced uint32 // Flag whether we're considered synchronised (enables transaction processing)
|
|
|
|
synced uint32 // Flag whether we're considered synchronised (enables transaction processing)
|
|
|
|
|
|
|
|
|
|
|
|
txpool txPool |
|
|
|
txpool txPool |
|
|
|
blockchain *core.BlockChain |
|
|
|
blockchain *core.BlockChain |
|
|
|
chaindb ethdb.Database |
|
|
|
chaindb ethdb.Database |
|
|
|
|
|
|
|
chainconfig *core.ChainConfig |
|
|
|
|
|
|
|
|
|
|
|
downloader *downloader.Downloader |
|
|
|
downloader *downloader.Downloader |
|
|
|
fetcher *fetcher.Fetcher |
|
|
|
fetcher *fetcher.Fetcher |
|
|
@ -99,6 +104,7 @@ func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, |
|
|
|
txpool: txpool, |
|
|
|
txpool: txpool, |
|
|
|
blockchain: blockchain, |
|
|
|
blockchain: blockchain, |
|
|
|
chaindb: chaindb, |
|
|
|
chaindb: chaindb, |
|
|
|
|
|
|
|
chainconfig: config, |
|
|
|
peers: newPeerSet(), |
|
|
|
peers: newPeerSet(), |
|
|
|
newPeerCh: make(chan *peer), |
|
|
|
newPeerCh: make(chan *peer), |
|
|
|
noMorePeers: make(chan struct{}), |
|
|
|
noMorePeers: make(chan struct{}), |
|
|
@ -278,6 +284,18 @@ func (pm *ProtocolManager) handle(p *peer) error { |
|
|
|
// after this will be sent via broadcasts.
|
|
|
|
// after this will be sent via broadcasts.
|
|
|
|
pm.syncTransactions(p) |
|
|
|
pm.syncTransactions(p) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If we're DAO hard-fork aware, validate any remote peer with regard to the hard-fork
|
|
|
|
|
|
|
|
if daoBlock := pm.chainconfig.DAOForkBlock; daoBlock != nil { |
|
|
|
|
|
|
|
// Request the peer's DAO fork header for extra-data validation
|
|
|
|
|
|
|
|
if err := p.RequestHeadersByNumber(daoBlock.Uint64(), 1, 0, false); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Start a timer to disconnect if the peer doesn't reply in time
|
|
|
|
|
|
|
|
p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() { |
|
|
|
|
|
|
|
glog.V(logger.Warn).Infof("%v: timed out DAO fork-check, dropping", p) |
|
|
|
|
|
|
|
pm.removePeer(p.id) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
// main loop. handle incoming messages.
|
|
|
|
// main loop. handle incoming messages.
|
|
|
|
for { |
|
|
|
for { |
|
|
|
if err := pm.handleMsg(p); err != nil { |
|
|
|
if err := pm.handleMsg(p); err != nil { |
|
|
@ -479,9 +497,43 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { |
|
|
|
if err := msg.Decode(&headers); err != nil { |
|
|
|
if err := msg.Decode(&headers); err != nil { |
|
|
|
return errResp(ErrDecode, "msg %v: %v", msg, err) |
|
|
|
return errResp(ErrDecode, "msg %v: %v", msg, err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// If no headers were received, but we're expending a DAO fork check, maybe it's that
|
|
|
|
|
|
|
|
if len(headers) == 0 && p.forkDrop != nil { |
|
|
|
|
|
|
|
// Possibly an empty reply to the fork header checks, sanity check TDs
|
|
|
|
|
|
|
|
verifyDAO := true |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If we already have a DAO header, we can check the peer's TD against it. If
|
|
|
|
|
|
|
|
// the peer's ahead of this, it too must have a reply to the DAO check
|
|
|
|
|
|
|
|
if daoHeader := pm.blockchain.GetHeaderByNumber(pm.chainconfig.DAOForkBlock.Uint64()); daoHeader != nil { |
|
|
|
|
|
|
|
if p.Td().Cmp(pm.blockchain.GetTd(daoHeader.Hash(), daoHeader.Number.Uint64())) >= 0 { |
|
|
|
|
|
|
|
verifyDAO = false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// If we're seemingly on the same chain, disable the drop timer
|
|
|
|
|
|
|
|
if verifyDAO { |
|
|
|
|
|
|
|
glog.V(logger.Info).Infof("%v: seems to be on the same side of the DAO fork", p) |
|
|
|
|
|
|
|
p.forkDrop.Stop() |
|
|
|
|
|
|
|
p.forkDrop = nil |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
// Filter out any explicitly requested headers, deliver the rest to the downloader
|
|
|
|
// Filter out any explicitly requested headers, deliver the rest to the downloader
|
|
|
|
filter := len(headers) == 1 |
|
|
|
filter := len(headers) == 1 |
|
|
|
if filter { |
|
|
|
if filter { |
|
|
|
|
|
|
|
// If it's a potential DAO fork check, validate against the rules
|
|
|
|
|
|
|
|
if p.forkDrop != nil && pm.chainconfig.DAOForkBlock.Cmp(headers[0].Number) == 0 { |
|
|
|
|
|
|
|
// Disable the fork drop timer
|
|
|
|
|
|
|
|
p.forkDrop.Stop() |
|
|
|
|
|
|
|
p.forkDrop = nil |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Validate the header and either drop the peer or continue
|
|
|
|
|
|
|
|
if err := core.ValidateHeaderExtraData(pm.chainconfig, headers[0]); err != nil { |
|
|
|
|
|
|
|
glog.V(logger.Info).Infof("%v: verified to be on the other side of the DAO fork, dropping", p) |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
glog.V(logger.Info).Infof("%v: verified to be on the same side of the DAO fork", p) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Irrelevant of the fork checks, send the header to the fetcher just in case
|
|
|
|
headers = pm.fetcher.FilterHeaders(headers, time.Now()) |
|
|
|
headers = pm.fetcher.FilterHeaders(headers, time.Now()) |
|
|
|
} |
|
|
|
} |
|
|
|
if len(headers) > 0 || !filter { |
|
|
|
if len(headers) > 0 || !filter { |
|
|
|