1 // Copyright (c) 2013-2017 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
17 "github.com/btcsuite/btcd/chaincfg"
18 "github.com/btcsuite/btcd/chaincfg/chainhash"
19 "github.com/btcsuite/btcd/database"
20 _ "github.com/btcsuite/btcd/database/ffldb"
21 "github.com/btcsuite/btcd/txscript"
22 "github.com/btcsuite/btcd/wire"
23 "github.com/btcsuite/btcutil"
27 // testDbType is the database backend type to use for the tests.
30 // testDbRoot is the root directory used to create all test databases.
31 testDbRoot = "testdbs"
33 // blockDataNet is the expected network in the test block data.
34 blockDataNet = wire.MainNet
37 // filesExists returns whether or not the named file or directory exists.
38 func fileExists(name string) bool {
39 if _, err := os.Stat(name); err != nil {
40 if os.IsNotExist(err) {
47 // isSupportedDbType returns whether or not the passed database type is
48 // currently supported.
49 func isSupportedDbType(dbType string) bool {
50 supportedDrivers := database.SupportedDrivers()
51 for _, driver := range supportedDrivers {
60 // loadBlocks reads files containing bitcoin block data (gzipped but otherwise
61 // in the format bitcoind writes) from disk and returns them as an array of
62 // btcutil.Block. This is largely borrowed from the test code in btcdb.
63 func loadBlocks(filename string) (blocks []*btcutil.Block, err error) {
64 filename = filepath.Join("testdata/", filename)
66 var network = wire.MainNet
70 fi, err = os.Open(filename)
75 if strings.HasSuffix(filename, ".bz2") {
76 dr = bzip2.NewReader(fi)
82 var block *btcutil.Block
85 for height := int64(1); err == nil; height++ {
87 err = binary.Read(dr, binary.LittleEndian, &rintbuf)
89 // hit end of file at expected offset: no warning
97 if rintbuf != uint32(network) {
100 err = binary.Read(dr, binary.LittleEndian, &rintbuf)
103 rbytes := make([]byte, blocklen)
108 block, err = btcutil.NewBlockFromBytes(rbytes)
112 blocks = append(blocks, block)
118 // chainSetup is used to create a new db and chain instance with the genesis
119 // block already inserted. In addition to the new chain instance, it returns
120 // a teardown function the caller should invoke when done testing to clean up.
121 func chainSetup(dbName string, params *chaincfg.Params) (*BlockChain, func(), error) {
122 if !isSupportedDbType(testDbType) {
123 return nil, nil, fmt.Errorf("unsupported db type %v", testDbType)
126 // Handle memory database specially since it doesn't need the disk
127 // specific handling.
130 if testDbType == "memdb" {
131 ndb, err := database.Create(testDbType)
133 return nil, nil, fmt.Errorf("error creating db: %v", err)
137 // Setup a teardown function for cleaning up. This function is
138 // returned to the caller to be invoked when it is done testing.
143 // Create the root directory for test databases.
144 if !fileExists(testDbRoot) {
145 if err := os.MkdirAll(testDbRoot, 0700); err != nil {
146 err := fmt.Errorf("unable to create test db "+
152 // Create a new database to store the accepted blocks into.
153 dbPath := filepath.Join(testDbRoot, dbName)
154 _ = os.RemoveAll(dbPath)
155 ndb, err := database.Create(testDbType, dbPath, blockDataNet)
157 return nil, nil, fmt.Errorf("error creating db: %v", err)
161 // Setup a teardown function for cleaning up. This function is
162 // returned to the caller to be invoked when it is done testing.
166 os.RemoveAll(testDbRoot)
170 // Copy the chain params to ensure any modifications the tests do to
171 // the chain parameters do not affect the global instance.
172 paramsCopy := *params
174 // Create the main chain instance.
175 chain, err := New(&Config{
177 ChainParams: ¶msCopy,
179 TimeSource: NewMedianTime(),
180 SigCache: txscript.NewSigCache(1000),
184 err := fmt.Errorf("failed to create chain instance: %v", err)
187 return chain, teardown, nil
190 // loadUtxoView returns a utxo view loaded from a file.
191 func loadUtxoView(filename string) (*UtxoViewpoint, error) {
192 // The utxostore file format is:
193 // <tx hash><serialized utxo len><serialized utxo>
195 // The serialized utxo len is a little endian uint32 and the serialized
196 // utxo uses the format described in chainio.go.
198 filename = filepath.Join("testdata", filename)
199 fi, err := os.Open(filename)
204 // Choose read based on whether the file is compressed or not.
206 if strings.HasSuffix(filename, ".bz2") {
207 r = bzip2.NewReader(fi)
213 view := NewUtxoViewpoint()
215 // Hash of the utxo entry.
216 var hash chainhash.Hash
217 _, err := io.ReadAtLeast(r, hash[:], len(hash[:]))
219 // Expected EOF at the right offset.
226 // Num of serialize utxo entry bytes.
228 err = binary.Read(r, binary.LittleEndian, &numBytes)
233 // Serialized utxo entry.
234 serialized := make([]byte, numBytes)
235 _, err = io.ReadAtLeast(r, serialized, int(numBytes))
240 // Deserialize it and add it to the view.
241 utxoEntry, err := deserializeUtxoEntry(serialized)
245 view.Entries()[hash] = utxoEntry
251 // TstSetCoinbaseMaturity makes the ability to set the coinbase maturity
252 // available when running tests.
253 func (b *BlockChain) TstSetCoinbaseMaturity(maturity uint16) {
254 b.chainParams.CoinbaseMaturity = maturity
257 // newFakeChain returns a chain that is usable for syntetic tests. It is
258 // important to note that this chain has no database associated with it, so
259 // it is not usable with all functions and the tests must take care when making
261 func newFakeChain(params *chaincfg.Params) *BlockChain {
262 // Create a genesis block node and block index index populated with it
263 // for use when creating the fake chain below.
264 node := newBlockNode(¶ms.GenesisBlock.Header, 0)
265 index := newBlockIndex(nil, params)
268 targetTimespan := int64(params.TargetTimespan / time.Second)
269 targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second)
270 adjustmentFactor := params.RetargetAdjustmentFactor
273 timeSource: NewMedianTime(),
274 minRetargetTimespan: targetTimespan / adjustmentFactor,
275 maxRetargetTimespan: targetTimespan * adjustmentFactor,
276 blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
278 bestChain: newChainView(node),
279 warningCaches: newThresholdCaches(vbNumBits),
280 deploymentCaches: newThresholdCaches(chaincfg.DefinedDeployments),
284 // newFakeNode creates a block node connected to the passed parent with the
285 // provided fields populated and fake values for the other fields.
286 func newFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp time.Time) *blockNode {
287 // Make up a header and create a block node from it.
288 header := &wire.BlockHeader{
289 Version: blockVersion,
290 PrevBlock: parent.hash,
292 Timestamp: timestamp,
294 node := newBlockNode(header, parent.height+1)
296 node.workSum.Add(parent.workSum, node.workSum)