@@ -28,7 +28,6 @@ import (
28
28
29
29
"github.com/ethereum/go-ethereum/common"
30
30
"github.com/ethereum/go-ethereum/consensus"
31
- "github.com/ethereum/go-ethereum/consensus/misc"
32
31
"github.com/ethereum/go-ethereum/core"
33
32
"github.com/ethereum/go-ethereum/core/types"
34
33
"github.com/ethereum/go-ethereum/eth/downloader"
@@ -55,7 +54,7 @@ const (
55
54
)
56
55
57
56
var (
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
59
58
)
60
59
61
60
// errIncompatibleConfig is returned if the requested protocols and configs are
@@ -72,6 +71,9 @@ type ProtocolManager struct {
72
71
fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks)
73
72
acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing)
74
73
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
+
75
77
txpool txPool
76
78
blockchain * core.BlockChain
77
79
chainconfig * params.ChainConfig
@@ -126,6 +128,11 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
126
128
if mode == downloader .FastSync {
127
129
manager .fastSync = uint32 (1 )
128
130
}
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
+ }
129
136
// Initiate a sub-protocol for every implemented version we can handle
130
137
manager .SubProtocols = make ([]p2p.Protocol , 0 , len (ProtocolVersions ))
131
138
for i , version := range ProtocolVersions {
@@ -165,7 +172,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
165
172
return nil , errIncompatibleConfig
166
173
}
167
174
// 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 )
169
176
170
177
validator := func (header * types.Header ) error {
171
178
return engine .VerifyHeader (blockchain , header , true )
@@ -291,22 +298,22 @@ func (pm *ProtocolManager) handle(p *peer) error {
291
298
// after this will be sent via broadcasts.
292
299
pm .syncTransactions (p )
293
300
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 {
298
305
return err
299
306
}
300
307
// 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 () )
303
310
pm .removePeer (p .id )
304
311
})
305
312
// Make sure it's cleaned up if the peer dies off
306
313
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
310
317
}
311
318
}()
312
319
}
@@ -438,41 +445,33 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
438
445
if err := msg .Decode (& headers ); err != nil {
439
446
return errResp (ErrDecode , "msg %v: %v" , msg , err )
440
447
}
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" )
459
460
}
460
461
}
461
462
// Filter out any explicitly requested headers, deliver the rest to the downloader
462
463
filter := len (headers ) == 1
463
464
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
469
470
470
471
// 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" )
474
474
}
475
- p .Log ().Debug ("Verified to be on the same side of the DAO fork" )
476
475
return nil
477
476
}
478
477
// Otherwise if it's a whitelisted block, validate against the set
0 commit comments