You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
3.2 KiB
134 lines
3.2 KiB
// Copyright (c) 2013-2014 The btcsuite developers |
|
// Use of this source code is governed by an ISC |
|
// license that can be found in the LICENSE file. |
|
|
|
package blockchain_test |
|
|
|
import ( |
|
"compress/bzip2" |
|
"encoding/binary" |
|
"io" |
|
"os" |
|
"path/filepath" |
|
"strings" |
|
"testing" |
|
|
|
"github.com/btcsuite/btcd/blockchain" |
|
"github.com/btcsuite/btcd/wire" |
|
"github.com/btcsuite/btcutil" |
|
) |
|
|
|
// TestReorganization loads a set of test blocks which force a chain |
|
// reorganization to test the block chain handling code. |
|
// The test blocks were originally from a post on the bitcoin talk forums: |
|
// https://bitcointalk.org/index.php?topic=46370.msg577556#msg577556 |
|
func TestReorganization(t *testing.T) { |
|
// Intentionally load the side chain blocks out of order to ensure |
|
// orphans are handled properly along with chain reorganization. |
|
testFiles := []string{ |
|
"blk_0_to_4.dat.bz2", |
|
"blk_4A.dat.bz2", |
|
"blk_5A.dat.bz2", |
|
"blk_3A.dat.bz2", |
|
} |
|
|
|
var blocks []*btcutil.Block |
|
for _, file := range testFiles { |
|
blockTmp, err := loadBlocks(file) |
|
if err != nil { |
|
t.Errorf("Error loading file: %v\n", err) |
|
} |
|
for _, block := range blockTmp { |
|
blocks = append(blocks, block) |
|
} |
|
} |
|
|
|
t.Logf("Number of blocks: %v\n", len(blocks)) |
|
|
|
// Create a new database and chain instance to run tests against. |
|
chain, teardownFunc, err := chainSetup("reorg") |
|
if err != nil { |
|
t.Errorf("Failed to setup chain instance: %v", err) |
|
return |
|
} |
|
defer teardownFunc() |
|
|
|
// Since we're not dealing with the real block chain, disable |
|
// checkpoints and set the coinbase maturity to 1. |
|
chain.DisableCheckpoints(true) |
|
blockchain.TstSetCoinbaseMaturity(1) |
|
|
|
timeSource := blockchain.NewMedianTime() |
|
expectedOrphans := map[int]struct{}{5: struct{}{}, 6: struct{}{}} |
|
for i := 1; i < len(blocks); i++ { |
|
isOrphan, err := chain.ProcessBlock(blocks[i], timeSource, blockchain.BFNone) |
|
if err != nil { |
|
t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) |
|
return |
|
} |
|
if _, ok := expectedOrphans[i]; !ok && isOrphan { |
|
t.Errorf("ProcessBlock incorrectly returned block %v "+ |
|
"is an orphan\n", i) |
|
} |
|
} |
|
|
|
return |
|
} |
|
|
|
// loadBlocks reads files containing bitcoin block data (gzipped but otherwise |
|
// in the format bitcoind writes) from disk and returns them as an array of |
|
// btcutil.Block. This is largely borrowed from the test code in btcdb. |
|
func loadBlocks(filename string) (blocks []*btcutil.Block, err error) { |
|
filename = filepath.Join("testdata/", filename) |
|
|
|
var network = wire.MainNet |
|
var dr io.Reader |
|
var fi io.ReadCloser |
|
|
|
fi, err = os.Open(filename) |
|
if err != nil { |
|
return |
|
} |
|
|
|
if strings.HasSuffix(filename, ".bz2") { |
|
dr = bzip2.NewReader(fi) |
|
} else { |
|
dr = fi |
|
} |
|
defer fi.Close() |
|
|
|
var block *btcutil.Block |
|
|
|
err = nil |
|
for height := int64(1); err == nil; height++ { |
|
var rintbuf uint32 |
|
err = binary.Read(dr, binary.LittleEndian, &rintbuf) |
|
if err == io.EOF { |
|
// hit end of file at expected offset: no warning |
|
height-- |
|
err = nil |
|
break |
|
} |
|
if err != nil { |
|
break |
|
} |
|
if rintbuf != uint32(network) { |
|
break |
|
} |
|
err = binary.Read(dr, binary.LittleEndian, &rintbuf) |
|
blocklen := rintbuf |
|
|
|
rbytes := make([]byte, blocklen) |
|
|
|
// read block |
|
dr.Read(rbytes) |
|
|
|
block, err = btcutil.NewBlockFromBytes(rbytes) |
|
if err != nil { |
|
return |
|
} |
|
blocks = append(blocks, block) |
|
} |
|
|
|
return |
|
}
|
|
|