2014-01-09 06:52:54 +01:00
|
|
|
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
2013-07-18 16:49:28 +02:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package btcchain_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"compress/bzip2"
|
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
2014-07-02 18:04:59 +02:00
|
|
|
|
2015-01-15 17:23:47 +01:00
|
|
|
"github.com/btcsuite/btcutil"
|
2015-01-16 20:57:29 +01:00
|
|
|
"github.com/btcsuite/btcwire"
|
2014-07-02 18:04:59 +02:00
|
|
|
"github.com/conformal/btcchain"
|
2013-07-18 16:49:28 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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.
|
2013-10-10 01:29:53 +02:00
|
|
|
testFiles := []string{
|
2013-07-18 16:49:28 +02:00
|
|
|
"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))
|
|
|
|
|
2013-10-10 01:29:53 +02:00
|
|
|
// Create a new database and chain instance to run tests against.
|
|
|
|
chain, teardownFunc, err := chainSetup("reorg")
|
2013-07-18 16:49:28 +02:00
|
|
|
if err != nil {
|
2013-10-10 01:29:53 +02:00
|
|
|
t.Errorf("Failed to setup chain instance: %v", err)
|
|
|
|
return
|
2013-10-06 20:51:10 +02:00
|
|
|
}
|
2013-10-10 01:29:53 +02:00
|
|
|
defer teardownFunc()
|
2013-10-06 20:51:10 +02:00
|
|
|
|
2013-07-18 16:49:28 +02:00
|
|
|
// Since we're not dealing with the real block chain, disable
|
|
|
|
// checkpoints and set the coinbase maturity to 1.
|
2013-10-10 01:29:53 +02:00
|
|
|
chain.DisableCheckpoints(true)
|
2013-07-18 16:49:28 +02:00
|
|
|
btcchain.TstSetCoinbaseMaturity(1)
|
|
|
|
|
2014-10-03 22:29:25 +02:00
|
|
|
timeSource := btcchain.NewMedianTime()
|
2014-07-02 17:56:22 +02:00
|
|
|
expectedOrphans := map[int]struct{}{5: struct{}{}, 6: struct{}{}}
|
2013-07-18 16:49:28 +02:00
|
|
|
for i := 1; i < len(blocks); i++ {
|
2014-10-03 22:29:25 +02:00
|
|
|
isOrphan, err := chain.ProcessBlock(blocks[i], timeSource, btcchain.BFNone)
|
2013-07-18 16:49:28 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("ProcessBlock fail on block %v: %v\n", i, err)
|
|
|
|
return
|
|
|
|
}
|
2014-07-02 17:56:22 +02:00
|
|
|
if _, ok := expectedOrphans[i]; !ok && isOrphan {
|
2014-06-25 22:47:24 +02:00
|
|
|
t.Errorf("ProcessBlock incorrectly returned block %v "+
|
|
|
|
"is an orphan\n", i)
|
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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 = btcwire.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)
|
|
|
|
|
2013-08-05 22:20:35 +02:00
|
|
|
block, err = btcutil.NewBlockFromBytes(rbytes)
|
2013-07-18 16:49:28 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
blocks = append(blocks, block)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|