mirror of
https://github.com/btcsuite/btcd.git
synced 2026-08-13 12:32:51 +02:00
netsync: add checkHeadersList
checkHeadersList takes in a blockhash and returns if it's a checkpointed block and the correct behavior flags for the verification of the block.
This commit is contained in:
parent
eeb2d43a79
commit
b1aef3ad21
3 changed files with 280 additions and 0 deletions
|
|
@ -690,6 +690,47 @@ func (sm *SyncManager) current() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// checkHeadersList checks if the sync manager is in the initial block download
|
||||
// mode and returns if the given block hash is a checkpointed block and the
|
||||
// behavior flags for this block. If the block is still under the checkpoint,
|
||||
// then it's given the fast-add flag.
|
||||
func (sm *SyncManager) checkHeadersList(blockHash *chainhash.Hash) (
|
||||
bool, blockchain.BehaviorFlags) {
|
||||
|
||||
// Always return false and BFNone if we're not in ibd mode.
|
||||
if !sm.headersFirstMode {
|
||||
return false, blockchain.BFNone
|
||||
}
|
||||
|
||||
isCheckpointBlock := false
|
||||
behaviorFlags := blockchain.BFNone
|
||||
|
||||
// If we don't already know this is a valid header, return false and
|
||||
// BFNone.
|
||||
if !sm.chain.IsValidHeader(blockHash) {
|
||||
return false, blockchain.BFNone
|
||||
}
|
||||
|
||||
height, err := sm.chain.HeaderHeightByHash(*blockHash)
|
||||
if err != nil {
|
||||
return false, blockchain.BFNone
|
||||
}
|
||||
|
||||
// Since findNextHeaderCheckpoint returns the next checkpoint after the
|
||||
// passed height, we do a -1 to include the current block.
|
||||
checkpoint := sm.findNextHeaderCheckpoint(height - 1)
|
||||
if checkpoint == nil {
|
||||
return false, blockchain.BFNone
|
||||
}
|
||||
|
||||
behaviorFlags |= blockchain.BFFastAdd
|
||||
if blockHash.IsEqual(checkpoint.Hash) {
|
||||
isCheckpointBlock = true
|
||||
}
|
||||
|
||||
return isCheckpointBlock, behaviorFlags
|
||||
}
|
||||
|
||||
// handleBlockMsg handles block messages from all peers.
|
||||
func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) {
|
||||
peer := bmsg.peer
|
||||
|
|
|
|||
228
netsync/manager_test.go
Normal file
228
netsync/manager_test.go
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
package netsync
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
_ "github.com/btcsuite/btcd/database/ffldb"
|
||||
"github.com/btcsuite/btcd/mempool"
|
||||
"github.com/btcsuite/btcd/peer"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// noopPeerNotifier is a no-op implementation of PeerNotifier for tests.
|
||||
type noopPeerNotifier struct{}
|
||||
|
||||
func (noopPeerNotifier) AnnounceNewTransactions([]*mempool.TxDesc) {}
|
||||
func (noopPeerNotifier) UpdatePeerHeights(*chainhash.Hash, int32, *peer.Peer) {}
|
||||
func (noopPeerNotifier) RelayInventory(*wire.InvVect, interface{}) {}
|
||||
func (noopPeerNotifier) TransactionConfirmed(*btcutil.Tx) {}
|
||||
|
||||
// dbSetup is used to create a new db with the genesis block already inserted.
|
||||
// It returns a teardown function the caller should invoke when done testing to
|
||||
// clean up. The database is stored under t.TempDir() which is automatically
|
||||
// removed when the test finishes.
|
||||
func dbSetup(t *testing.T, params *chaincfg.Params) (database.DB, func(), error) {
|
||||
dbPath := filepath.Join(t.TempDir(), "ffldb")
|
||||
db, err := database.Create("ffldb", dbPath, params.Net)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating db: %v", err)
|
||||
}
|
||||
|
||||
teardown := func() {
|
||||
db.Close()
|
||||
}
|
||||
|
||||
return db, teardown, nil
|
||||
}
|
||||
|
||||
// chainSetup is used to create a new db and chain instance with the genesis
|
||||
// block already inserted. In addition to the new chain instance, it returns
|
||||
// a teardown function the caller should invoke when done testing to clean up.
|
||||
func chainSetup(t *testing.T, params *chaincfg.Params) (
|
||||
*blockchain.BlockChain, func(), error) {
|
||||
|
||||
db, teardown, err := dbSetup(t, params)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Copy the chain params to ensure any modifications the tests do to
|
||||
// the chain parameters do not affect the global instance.
|
||||
paramsCopy := *params
|
||||
|
||||
// Deep-copy deployment starters/enders so that parallel tests don't
|
||||
// race on the shared blockClock field written by SynchronizeClock.
|
||||
for i := range paramsCopy.Deployments {
|
||||
d := ¶msCopy.Deployments[i]
|
||||
if s, ok := d.DeploymentStarter.(*chaincfg.MedianTimeDeploymentStarter); ok {
|
||||
d.DeploymentStarter = chaincfg.NewMedianTimeDeploymentStarter(
|
||||
s.StartTime())
|
||||
}
|
||||
if e, ok := d.DeploymentEnder.(*chaincfg.MedianTimeDeploymentEnder); ok {
|
||||
d.DeploymentEnder = chaincfg.NewMedianTimeDeploymentEnder(
|
||||
e.EndTime())
|
||||
}
|
||||
}
|
||||
|
||||
// Create the main chain instance.
|
||||
chain, err := blockchain.New(&blockchain.Config{
|
||||
DB: db,
|
||||
Checkpoints: paramsCopy.Checkpoints,
|
||||
ChainParams: ¶msCopy,
|
||||
TimeSource: blockchain.NewMedianTime(),
|
||||
SigCache: txscript.NewSigCache(1000),
|
||||
})
|
||||
if err != nil {
|
||||
teardown()
|
||||
err := fmt.Errorf("failed to create chain instance: %v", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
return chain, teardown, nil
|
||||
}
|
||||
|
||||
// loadHeaders loads headers from mainnet from 1 to 11.
|
||||
func loadHeaders(t *testing.T) []*wire.BlockHeader {
|
||||
testFile := "blockheaders-mainnet-1-11.txt"
|
||||
filename := filepath.Join("testdata/", testFile)
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
headers := make([]*wire.BlockHeader, 0, 10)
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
b, err := hex.DecodeString(line)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read block headers from file %v", testFile)
|
||||
}
|
||||
|
||||
r := bytes.NewReader(b)
|
||||
header := new(wire.BlockHeader)
|
||||
header.Deserialize(r)
|
||||
|
||||
headers = append(headers, header)
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func makeMockSyncManager(t *testing.T,
|
||||
params *chaincfg.Params) (*SyncManager, func()) {
|
||||
|
||||
t.Helper()
|
||||
|
||||
chain, tearDown, err := chainSetup(t, params)
|
||||
require.NoError(t, err)
|
||||
|
||||
sm, err := New(&Config{
|
||||
PeerNotifier: noopPeerNotifier{},
|
||||
Chain: chain,
|
||||
ChainParams: params,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return sm, tearDown
|
||||
}
|
||||
|
||||
func TestCheckHeadersList(t *testing.T) {
|
||||
// Set params to mainnet with a checkpoint at block 11.
|
||||
params := chaincfg.MainNetParams
|
||||
checkpointHeight := int32(11)
|
||||
checkpointHash, err := chainhash.NewHashFromStr(
|
||||
"0000000097be56d606cdd9c54b04d4747e957d3608abe69198c661f2add73073")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
params.Checkpoints = []chaincfg.Checkpoint{
|
||||
{
|
||||
Height: checkpointHeight,
|
||||
Hash: checkpointHash,
|
||||
},
|
||||
}
|
||||
|
||||
// Create mock SyncManager.
|
||||
sm, tearDown := makeMockSyncManager(t, ¶ms)
|
||||
defer tearDown()
|
||||
|
||||
// Setup SyncManager with headers processed.
|
||||
headers := loadHeaders(t)
|
||||
for _, header := range headers {
|
||||
isMainChain, err := sm.chain.ProcessBlockHeader(
|
||||
header, blockchain.BFNone, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !isMainChain {
|
||||
t.Fatalf("expected block header %v to be in the main chain",
|
||||
header.BlockHash())
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
hash string
|
||||
isCheckpointBlock bool
|
||||
behaviorFlags blockchain.BehaviorFlags
|
||||
}{
|
||||
{
|
||||
hash: chaincfg.MainNetParams.GenesisHash.String(),
|
||||
isCheckpointBlock: false,
|
||||
behaviorFlags: blockchain.BFFastAdd,
|
||||
},
|
||||
{
|
||||
// Block 10.
|
||||
hash: "000000002c05cc2e78923c34df87fd108b22221ac6076c18f3ade378a4d915e9",
|
||||
isCheckpointBlock: false,
|
||||
behaviorFlags: blockchain.BFFastAdd,
|
||||
},
|
||||
{
|
||||
// Block 11.
|
||||
hash: "0000000097be56d606cdd9c54b04d4747e957d3608abe69198c661f2add73073",
|
||||
isCheckpointBlock: true,
|
||||
behaviorFlags: blockchain.BFFastAdd,
|
||||
},
|
||||
{
|
||||
// Block 12.
|
||||
hash: "0000000027c2488e2510d1acf4369787784fa20ee084c258b58d9fbd43802b5e",
|
||||
isCheckpointBlock: false,
|
||||
behaviorFlags: blockchain.BFNone,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
hash, err := chainhash.NewHashFromStr(test.hash)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Make sure that when the headers-first mode is off, we always get
|
||||
// false and BFNone.
|
||||
sm.headersFirstMode = false
|
||||
isCheckpoint, gotFlags := sm.checkHeadersList(hash)
|
||||
require.Equal(t, false, isCheckpoint)
|
||||
require.Equal(t, blockchain.BFNone, gotFlags)
|
||||
|
||||
// Now check that the test values are correct.
|
||||
sm.headersFirstMode = true
|
||||
isCheckpoint, gotFlags = sm.checkHeadersList(hash)
|
||||
require.Equal(t, test.isCheckpointBlock, isCheckpoint)
|
||||
require.Equal(t, test.behaviorFlags, gotFlags)
|
||||
}
|
||||
}
|
||||
11
netsync/testdata/blockheaders-mainnet-1-11.txt
vendored
Normal file
11
netsync/testdata/blockheaders-mainnet-1-11.txt
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299
|
||||
010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd61
|
||||
01000000bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a0000000044f672226090d85db9a9f2fbfe5f0f9609b387af7be5b7fbb7a1767c831c9e995dbe6649ffff001d05e0ed6d
|
||||
010000004944469562ae1c2c74d9a535e00b6f3e40ffbad4f2fda3895501b582000000007a06ea98cd40ba2e3288262b28638cec5337c1456aaf5eedc8e9e5a20f062bdf8cc16649ffff001d2bfee0a9
|
||||
0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e477
|
||||
01000000fc33f596f822a0a1951ffdbf2a897b095636ad871707bf5d3162729b00000000379dfb96a5ea8c81700ea4ac6b97ae9a9312b2d4301a29580e924ee6761a2520adc46649ffff001d189c4c97
|
||||
010000008d778fdc15a2d3fb76b7122a3b5582bea4f21f5a0c693537e7a03130000000003f674005103b42f984169c7d008370967e91920a6a5d64fd51282f75bc73a68af1c66649ffff001d39a59c86
|
||||
010000004494c8cf4154bdcc0720cd4a59d9c9b285e4b146d45f061d2b6c967100000000e3855ed886605b6d4a99d5fa2ef2e9b0b164e63df3c4136bebf2d0dac0f1f7a667c86649ffff001d1c4b5666
|
||||
01000000c60ddef1b7618ca2348a46e868afc26e3efc68226c78aa47f8488c4000000000c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd37047fca6649ffff001d28404f53
|
||||
010000000508085c47cc849eb80ea905cc7800a3be674ffc57263cf210c59d8d00000000112ba175a1e04b14ba9e7ea5f76ab640affeef5ec98173ac9799a852fa39add320cd6649ffff001d1e2de565
|
||||
01000000e915d9a478e3adf3186c07c61a22228b10fd87df343c92782ecc052c000000006e06373c80de397406dc3d19c90d71d230058d28293614ea58d6a57f8f5d32f8b8ce6649ffff001d173807f8
|
||||
Loading…
Add table
Add a link
Reference in a new issue