Reset chain to previous checkpoint if a checkpoint check fails

This commit is contained in:
Alex 2017-03-16 17:17:15 -06:00 committed by Olaoluwa Osuntokun
parent 28b7bb65ea
commit 6ea7e6035d

View file

@ -28,6 +28,10 @@ const (
// maxRequestedBlocks is the maximum number of requested block // maxRequestedBlocks is the maximum number of requested block
// hashes to store in memory. // hashes to store in memory.
maxRequestedBlocks = wire.MaxInvPerMsg maxRequestedBlocks = wire.MaxInvPerMsg
// maxTimeOffset is the maximum duration a block time is allowed to be
// ahead of the curent time. This is currently 2 hours.
maxTimeOffset = 2 * time.Hour
) )
// zeroHash is the zero value hash (all zeros). It is defined as a convenience. // zeroHash is the zero value hash (all zeros). It is defined as a convenience.
@ -340,11 +344,10 @@ func (b *blockManager) isSyncCandidate(sp *serverPeer) bool {
// findNextHeaderCheckpoint returns the next checkpoint after the passed height. // findNextHeaderCheckpoint returns the next checkpoint after the passed height.
// It returns nil when there is not one either because the height is already // It returns nil when there is not one either because the height is already
// later than the final checkpoint or some other reason such as disabled // later than the final checkpoint or there are none for the current network.
// checkpoints.
func (b *blockManager) findNextHeaderCheckpoint(height int32) *chaincfg.Checkpoint { func (b *blockManager) findNextHeaderCheckpoint(height int32) *chaincfg.Checkpoint {
// There is no next checkpoint if checkpoints are disabled or there are // There is no next checkpoint if there are none for this current
// none for this current network. // network.
checkpoints := b.server.chainParams.Checkpoints checkpoints := b.server.chainParams.Checkpoints
if len(checkpoints) == 0 { if len(checkpoints) == 0 {
return nil return nil
@ -368,6 +371,31 @@ func (b *blockManager) findNextHeaderCheckpoint(height int32) *chaincfg.Checkpoi
return nextCheckpoint return nextCheckpoint
} }
// findPreviousHeaderCheckpoint returns the last checkpoint before the passed
// height. It returns a checkpoint matching the genesis block when the height
// is earlier than the first checkpoint or there are no checkpoints for the
// current network. This is used for resettng state when a malicious peer sends
// us headers that don't lead up to a known checkpoint.
func (b *blockManager) findPreviousHeaderCheckpoint(height int32) *chaincfg.Checkpoint {
// Start with the genesis block - earliest checkpoint to which our
// code will want to reset
prevCheckpoint := &chaincfg.Checkpoint{
Height: 0,
Hash: b.server.chainParams.GenesisHash,
}
// Find the latest checkpoint lower than height or return genesis block
// if there are none.
checkpoints := b.server.chainParams.Checkpoints
for _, ckpt := range checkpoints {
if height <= ckpt.Height {
break
}
prevCheckpoint = &ckpt
}
return prevCheckpoint
}
// resetHeaderState sets the headers-first mode state to values appropriate for // resetHeaderState sets the headers-first mode state to values appropriate for
// syncing from a new peer. // syncing from a new peer.
func (b *blockManager) resetHeaderState(newestHeader *wire.BlockHeader, func (b *blockManager) resetHeaderState(newestHeader *wire.BlockHeader,
@ -612,6 +640,11 @@ func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
return return
} }
// For checking to make sure blocks aren't too far in the
// future as of the time we receive the headers message.
maxTimestamp := b.server.timeSource.AdjustedTime().
Add(maxTimeOffset)
// Process all of the received headers ensuring each one connects to the // Process all of the received headers ensuring each one connects to the
// previous and that checkpoints match. // previous and that checkpoints match.
receivedCheckpoint := false receivedCheckpoint := false
@ -630,8 +663,10 @@ func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
return return
} }
// Ensure the header properly connects to the previous one and // Ensure the header properly connects to the previous one,
// the proof of work is good, and add it to the list of headers. // that the proof of work is good, and that the header's
// timestamp isn't too far in the future, and add it to the
// list of headers.
node := headerNode{header: blockHeader} node := headerNode{header: blockHeader}
prevNode := prevNodeEl.Value.(*headerNode) prevNode := prevNodeEl.Value.(*headerNode)
prevHash := prevNode.header.BlockHash() prevHash := prevNode.header.BlockHash()
@ -656,6 +691,13 @@ func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
hmsg.peer.Disconnect() hmsg.peer.Disconnect()
return return
} }
// Ensure the block time is not too far in the future.
if blockHeader.Timestamp.After(maxTimestamp) {
log.Warnf("block timestamp of %v is too far in"+
" the future", blockHeader.Timestamp)
hmsg.peer.Disconnect()
return
}
node.height = prevNode.height + 1 node.height = prevNode.height + 1
finalHeight = node.height finalHeight = node.height
err = b.server.putBlock(*blockHeader, err = b.server.putBlock(*blockHeader,
@ -696,6 +738,13 @@ func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
"disconnecting", node.height, "disconnecting", node.height,
nodeHash, hmsg.peer.Addr(), nodeHash, hmsg.peer.Addr(),
b.nextCheckpoint.Hash) b.nextCheckpoint.Hash)
prevCheckpoint := b.findPreviousHeaderCheckpoint(node.height)
log.Infof("Rolling back to previous validated "+
"checkpoint at height %d/hash %s",
prevCheckpoint.Height,
prevCheckpoint.Hash)
b.server.putMaxBlockHeight(uint32(
prevCheckpoint.Height))
hmsg.peer.Disconnect() hmsg.peer.Disconnect()
return return
} }
@ -884,20 +933,11 @@ func (b *blockManager) findPrevTestNetDifficulty() (uint32, error) {
/* /*
import ( import (
"container/list"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"sync"
"sync/atomic"
"time"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/database" "github.com/btcsuite/btcd/database"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
) )
// handleBlockMsg handles block messages from all peers. // handleBlockMsg handles block messages from all peers.