@@ -28,7 +28,6 @@ import (
2828
2929 "github.com/ethereum/go-ethereum/common"
3030 "github.com/ethereum/go-ethereum/consensus"
31- "github.com/ethereum/go-ethereum/consensus/misc"
3231 "github.com/ethereum/go-ethereum/core"
3332 "github.com/ethereum/go-ethereum/core/types"
3433 "github.com/ethereum/go-ethereum/eth/downloader"
@@ -55,7 +54,7 @@ const (
5554)
5655
5756var (
58- daoChallengeTimeout = 15 * time .Second // Time allowance for a node to reply to the DAO handshake challenge
57+ syncChallengeTimeout = 15 * time .Second // Time allowance for a node to reply to the sync progress challenge
5958)
6059
6160// errIncompatibleConfig is returned if the requested protocols and configs are
@@ -72,6 +71,9 @@ type ProtocolManager struct {
7271 fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks)
7372 acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing)
7473
74+ checkpointNumber uint64 // Block number for the sync progress validator to cross reference
75+ checkpointHash common.Hash // Block hash for the sync progress validator to cross reference
76+
7577 txpool txPool
7678 blockchain * core.BlockChain
7779 chainconfig * params.ChainConfig
@@ -126,6 +128,11 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
126128 if mode == downloader .FastSync {
127129 manager .fastSync = uint32 (1 )
128130 }
131+ // If we have trusted checkpoints, enforce them on the chain
132+ if checkpoint , ok := params .TrustedCheckpoints [blockchain .Genesis ().Hash ()]; ok {
133+ manager .checkpointNumber = (checkpoint .SectionIndex + 1 )* params .CHTFrequencyClient - 1
134+ manager .checkpointHash = checkpoint .SectionHead
135+ }
129136 // Initiate a sub-protocol for every implemented version we can handle
130137 manager .SubProtocols = make ([]p2p.Protocol , 0 , len (ProtocolVersions ))
131138 for i , version := range ProtocolVersions {
@@ -165,7 +172,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
165172 return nil , errIncompatibleConfig
166173 }
167174 // Construct the different synchronisation mechanisms
168- manager .downloader = downloader .New (mode , chaindb , manager .eventMux , blockchain , nil , manager .removePeer )
175+ manager .downloader = downloader .New (mode , manager . checkpointNumber , chaindb , manager .eventMux , blockchain , nil , manager .removePeer )
169176
170177 validator := func (header * types.Header ) error {
171178 return engine .VerifyHeader (blockchain , header , true )
@@ -291,22 +298,22 @@ func (pm *ProtocolManager) handle(p *peer) error {
291298 // after this will be sent via broadcasts.
292299 pm .syncTransactions (p )
293300
294- // If we're DAO hard-fork aware, validate any remote peer with regard to the hard-fork
295- if daoBlock := pm .chainconfig . DAOForkBlock ; daoBlock != nil {
296- // Request the peer's DAO fork header for extra-data validation
297- if err := p .RequestHeadersByNumber (daoBlock . Uint64 () , 1 , 0 , false ); err != nil {
301+ // If we have a trusted CHT, reject all peers below that (avoid fast sync eclipse)
302+ if pm .checkpointHash != (common. Hash {}) {
303+ // Request the peer's checkpoint header for chain height/weight validation
304+ if err := p .RequestHeadersByNumber (pm . checkpointNumber , 1 , 0 , false ); err != nil {
298305 return err
299306 }
300307 // Start a timer to disconnect if the peer doesn't reply in time
301- p .forkDrop = time .AfterFunc (daoChallengeTimeout , func () {
302- p .Log ().Debug ( "Timed out DAO fork-check , dropping" )
308+ p .syncDrop = time .AfterFunc (syncChallengeTimeout , func () {
309+ p .Log ().Warn ( "Checkpoint challenge timed out , dropping", "addr" , p . RemoteAddr (), "type" , p . Name () )
303310 pm .removePeer (p .id )
304311 })
305312 // Make sure it's cleaned up if the peer dies off
306313 defer func () {
307- if p .forkDrop != nil {
308- p .forkDrop .Stop ()
309- p .forkDrop = nil
314+ if p .syncDrop != nil {
315+ p .syncDrop .Stop ()
316+ p .syncDrop = nil
310317 }
311318 }()
312319 }
@@ -438,41 +445,33 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
438445 if err := msg .Decode (& headers ); err != nil {
439446 return errResp (ErrDecode , "msg %v: %v" , msg , err )
440447 }
441- // If no headers were received, but we're expending a DAO fork check, maybe it's that
442- if len (headers ) == 0 && p .forkDrop != nil {
443- // Possibly an empty reply to the fork header checks, sanity check TDs
444- verifyDAO := true
445-
446- // If we already have a DAO header, we can check the peer's TD against it. If
447- // the peer's ahead of this, it too must have a reply to the DAO check
448- if daoHeader := pm .blockchain .GetHeaderByNumber (pm .chainconfig .DAOForkBlock .Uint64 ()); daoHeader != nil {
449- if _ , td := p .Head (); td .Cmp (pm .blockchain .GetTd (daoHeader .Hash (), daoHeader .Number .Uint64 ())) >= 0 {
450- verifyDAO = false
451- }
452- }
453- // If we're seemingly on the same chain, disable the drop timer
454- if verifyDAO {
455- p .Log ().Debug ("Seems to be on the same side of the DAO fork" )
456- p .forkDrop .Stop ()
457- p .forkDrop = nil
458- return nil
448+ // If no headers were received, but we're expencting a checkpoint header, consider it that
449+ if len (headers ) == 0 && p .syncDrop != nil {
450+ // Stop the timer either way, decide later to drop or not
451+ p .syncDrop .Stop ()
452+ p .syncDrop = nil
453+
454+ // If we're doing a fast sync, we must enforce the checkpoint block to avoid
455+ // eclipse attacks. Unsynced nodes are welcome to connect after we're done
456+ // joining the network
457+ if atomic .LoadUint32 (& pm .fastSync ) == 1 {
458+ p .Log ().Warn ("Dropping unsynced node during fast sync" , "addr" , p .RemoteAddr (), "type" , p .Name ())
459+ return errors .New ("unsynced node cannot serve fast sync" )
459460 }
460461 }
461462 // Filter out any explicitly requested headers, deliver the rest to the downloader
462463 filter := len (headers ) == 1
463464 if filter {
464- // If it's a potential DAO fork check, validate against the rules
465- if p .forkDrop != nil && pm . chainconfig . DAOForkBlock . Cmp ( headers [0 ].Number ) == 0 {
466- // Disable the fork drop timer
467- p .forkDrop .Stop ()
468- p .forkDrop = nil
465+ // If it's a potential sync progress check, validate the content and advertised chain weight
466+ if p .syncDrop != nil && headers [0 ].Number . Uint64 ( ) == pm . checkpointNumber {
467+ // Disable the sync drop timer
468+ p .syncDrop .Stop ()
469+ p .syncDrop = nil
469470
470471 // Validate the header and either drop the peer or continue
471- if err := misc .VerifyDAOHeaderExtraData (pm .chainconfig , headers [0 ]); err != nil {
472- p .Log ().Debug ("Verified to be on the other side of the DAO fork, dropping" )
473- return err
472+ if headers [0 ].Hash () != pm .checkpointHash {
473+ return errors .New ("checkpoint hash mismatch" )
474474 }
475- p .Log ().Debug ("Verified to be on the same side of the DAO fork" )
476475 return nil
477476 }
478477 // Otherwise if it's a whitelisted block, validate against the set
0 commit comments