diff --git a/eth/handler.go b/eth/handler.go index 29f224910..7b753ebb4 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -181,13 +181,30 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne return blockchain.CurrentBlock().NumberU64() } inserter := func(blocks types.Blocks) (int, error) { - // If fast sync is running, deny importing weird blocks - if atomic.LoadUint32(&manager.fastSync) == 1 { - log.Warn("Discarded bad propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) + // If sync hasn't reached the checkpoint yet, deny importing weird blocks. + // + // Ideally we would also compare the head block's timestamp and similarly reject + // the propagated block if the head is too old. Unfortunately there is a corner + // case when starting new networks, where the genesis might be ancient (0 unix) + // which would prevent full nodes from accepting it. + if manager.blockchain.CurrentBlock().NumberU64() < manager.checkpointNumber { + log.Warn("Unsynced yet, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) return 0, nil } - atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import - return manager.blockchain.InsertChain(blocks) + // If fast sync is running, deny importing weird blocks. This is a problematic + // clause when starting up a new network, because fast-syncing miners might not + // accept each others' blocks until a restart. Unfortunately we haven't figured + // out a way yet where nodes can decide unilaterally whether the network is new + // or not. This should be fixed if we figure out a solution. + if atomic.LoadUint32(&manager.fastSync) == 1 { + log.Warn("Fast syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) + return 0, nil + } + n, err := manager.blockchain.InsertChain(blocks) + if err == nil { + atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import + } + return n, err } manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer) diff --git a/eth/sync.go b/eth/sync.go index 3e23c5378..e303ef8d4 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -202,8 +202,17 @@ func (pm *ProtocolManager) synchronise(peer *peer) { log.Info("Fast sync complete, auto disabling") atomic.StoreUint32(&pm.fastSync, 0) } - atomic.StoreUint32(&pm.acceptTxs, 1) // Mark initial sync done - if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 { + // If we've successfully finished a sync cycle and passed any required checkpoint, + // enable accepting transactions from the network. + head := pm.blockchain.CurrentBlock() + if head.NumberU64() >= pm.checkpointNumber { + // Checkpoint passed, sanity check the timestamp to have a fallback mechanism + // for non-checkpointed (number = 0) private networks. + if head.Time() >= uint64(time.Now().AddDate(0, -1, 0).Unix()) { + atomic.StoreUint32(&pm.acceptTxs, 1) + } + } + if head.NumberU64() > 0 { // We've completed a sync cycle, notify all peers of new state. This path is // essential in star-topology networks where a gateway node needs to notify // all its out-of-date peers of the availability of a new block. This failure