OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / vendor / github.com / btcsuite / btcd / blockchain / common_test.go
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.
4
5 package blockchain
6
7 import (
8         "compress/bzip2"
9         "encoding/binary"
10         "fmt"
11         "io"
12         "os"
13         "path/filepath"
14         "strings"
15         "time"
16
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"
24 )
25
26 const (
27         // testDbType is the database backend type to use for the tests.
28         testDbType = "ffldb"
29
30         // testDbRoot is the root directory used to create all test databases.
31         testDbRoot = "testdbs"
32
33         // blockDataNet is the expected network in the test block data.
34         blockDataNet = wire.MainNet
35 )
36
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) {
41                         return false
42                 }
43         }
44         return true
45 }
46
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 {
52                 if dbType == driver {
53                         return true
54                 }
55         }
56
57         return false
58 }
59
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)
65
66         var network = wire.MainNet
67         var dr io.Reader
68         var fi io.ReadCloser
69
70         fi, err = os.Open(filename)
71         if err != nil {
72                 return
73         }
74
75         if strings.HasSuffix(filename, ".bz2") {
76                 dr = bzip2.NewReader(fi)
77         } else {
78                 dr = fi
79         }
80         defer fi.Close()
81
82         var block *btcutil.Block
83
84         err = nil
85         for height := int64(1); err == nil; height++ {
86                 var rintbuf uint32
87                 err = binary.Read(dr, binary.LittleEndian, &rintbuf)
88                 if err == io.EOF {
89                         // hit end of file at expected offset: no warning
90                         height--
91                         err = nil
92                         break
93                 }
94                 if err != nil {
95                         break
96                 }
97                 if rintbuf != uint32(network) {
98                         break
99                 }
100                 err = binary.Read(dr, binary.LittleEndian, &rintbuf)
101                 blocklen := rintbuf
102
103                 rbytes := make([]byte, blocklen)
104
105                 // read block
106                 dr.Read(rbytes)
107
108                 block, err = btcutil.NewBlockFromBytes(rbytes)
109                 if err != nil {
110                         return
111                 }
112                 blocks = append(blocks, block)
113         }
114
115         return
116 }
117
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)
124         }
125
126         // Handle memory database specially since it doesn't need the disk
127         // specific handling.
128         var db database.DB
129         var teardown func()
130         if testDbType == "memdb" {
131                 ndb, err := database.Create(testDbType)
132                 if err != nil {
133                         return nil, nil, fmt.Errorf("error creating db: %v", err)
134                 }
135                 db = ndb
136
137                 // Setup a teardown function for cleaning up.  This function is
138                 // returned to the caller to be invoked when it is done testing.
139                 teardown = func() {
140                         db.Close()
141                 }
142         } else {
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 "+
147                                         "root: %v", err)
148                                 return nil, nil, err
149                         }
150                 }
151
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)
156                 if err != nil {
157                         return nil, nil, fmt.Errorf("error creating db: %v", err)
158                 }
159                 db = ndb
160
161                 // Setup a teardown function for cleaning up.  This function is
162                 // returned to the caller to be invoked when it is done testing.
163                 teardown = func() {
164                         db.Close()
165                         os.RemoveAll(dbPath)
166                         os.RemoveAll(testDbRoot)
167                 }
168         }
169
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
173
174         // Create the main chain instance.
175         chain, err := New(&Config{
176                 DB:          db,
177                 ChainParams: &paramsCopy,
178                 Checkpoints: nil,
179                 TimeSource:  NewMedianTime(),
180                 SigCache:    txscript.NewSigCache(1000),
181         })
182         if err != nil {
183                 teardown()
184                 err := fmt.Errorf("failed to create chain instance: %v", err)
185                 return nil, nil, err
186         }
187         return chain, teardown, nil
188 }
189
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>
194         //
195         // The serialized utxo len is a little endian uint32 and the serialized
196         // utxo uses the format described in chainio.go.
197
198         filename = filepath.Join("testdata", filename)
199         fi, err := os.Open(filename)
200         if err != nil {
201                 return nil, err
202         }
203
204         // Choose read based on whether the file is compressed or not.
205         var r io.Reader
206         if strings.HasSuffix(filename, ".bz2") {
207                 r = bzip2.NewReader(fi)
208         } else {
209                 r = fi
210         }
211         defer fi.Close()
212
213         view := NewUtxoViewpoint()
214         for {
215                 // Hash of the utxo entry.
216                 var hash chainhash.Hash
217                 _, err := io.ReadAtLeast(r, hash[:], len(hash[:]))
218                 if err != nil {
219                         // Expected EOF at the right offset.
220                         if err == io.EOF {
221                                 break
222                         }
223                         return nil, err
224                 }
225
226                 // Num of serialize utxo entry bytes.
227                 var numBytes uint32
228                 err = binary.Read(r, binary.LittleEndian, &numBytes)
229                 if err != nil {
230                         return nil, err
231                 }
232
233                 // Serialized utxo entry.
234                 serialized := make([]byte, numBytes)
235                 _, err = io.ReadAtLeast(r, serialized, int(numBytes))
236                 if err != nil {
237                         return nil, err
238                 }
239
240                 // Deserialize it and add it to the view.
241                 utxoEntry, err := deserializeUtxoEntry(serialized)
242                 if err != nil {
243                         return nil, err
244                 }
245                 view.Entries()[hash] = utxoEntry
246         }
247
248         return view, nil
249 }
250
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
255 }
256
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
260 // use of it.
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(&params.GenesisBlock.Header, 0)
265         index := newBlockIndex(nil, params)
266         index.AddNode(node)
267
268         targetTimespan := int64(params.TargetTimespan / time.Second)
269         targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second)
270         adjustmentFactor := params.RetargetAdjustmentFactor
271         return &BlockChain{
272                 chainParams:         params,
273                 timeSource:          NewMedianTime(),
274                 minRetargetTimespan: targetTimespan / adjustmentFactor,
275                 maxRetargetTimespan: targetTimespan * adjustmentFactor,
276                 blocksPerRetarget:   int32(targetTimespan / targetTimePerBlock),
277                 index:               index,
278                 bestChain:           newChainView(node),
279                 warningCaches:       newThresholdCaches(vbNumBits),
280                 deploymentCaches:    newThresholdCaches(chaincfg.DefinedDeployments),
281         }
282 }
283
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,
291                 Bits:      bits,
292                 Timestamp: timestamp,
293         }
294         node := newBlockNode(header, parent.height+1)
295         node.parent = parent
296         node.workSum.Add(parent.workSum, node.workSum)
297         return node
298 }