OSDN Git Service

Merge pull request #41 from Bytom/dev
[bytom/vapor.git] / vendor / github.com / btcsuite / btcd / blockchain / fullblocks_test.go
1 // Copyright (c) 2016 The Decred developers
2 // Copyright (c) 2016-2017 The btcsuite developers
3 // Use of this source code is governed by an ISC
4 // license that can be found in the LICENSE file.
5
6 package blockchain_test
7
8 import (
9         "bytes"
10         "fmt"
11         "os"
12         "path/filepath"
13         "testing"
14
15         "github.com/btcsuite/btcd/blockchain"
16         "github.com/btcsuite/btcd/blockchain/fullblocktests"
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 // chainSetup is used to create a new db and chain instance with the genesis
61 // block already inserted.  In addition to the new chain instance, it returns
62 // a teardown function the caller should invoke when done testing to clean up.
63 func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, func(), error) {
64         if !isSupportedDbType(testDbType) {
65                 return nil, nil, fmt.Errorf("unsupported db type %v", testDbType)
66         }
67
68         // Handle memory database specially since it doesn't need the disk
69         // specific handling.
70         var db database.DB
71         var teardown func()
72         if testDbType == "memdb" {
73                 ndb, err := database.Create(testDbType)
74                 if err != nil {
75                         return nil, nil, fmt.Errorf("error creating db: %v", err)
76                 }
77                 db = ndb
78
79                 // Setup a teardown function for cleaning up.  This function is
80                 // returned to the caller to be invoked when it is done testing.
81                 teardown = func() {
82                         db.Close()
83                 }
84         } else {
85                 // Create the root directory for test databases.
86                 if !fileExists(testDbRoot) {
87                         if err := os.MkdirAll(testDbRoot, 0700); err != nil {
88                                 err := fmt.Errorf("unable to create test db "+
89                                         "root: %v", err)
90                                 return nil, nil, err
91                         }
92                 }
93
94                 // Create a new database to store the accepted blocks into.
95                 dbPath := filepath.Join(testDbRoot, dbName)
96                 _ = os.RemoveAll(dbPath)
97                 ndb, err := database.Create(testDbType, dbPath, blockDataNet)
98                 if err != nil {
99                         return nil, nil, fmt.Errorf("error creating db: %v", err)
100                 }
101                 db = ndb
102
103                 // Setup a teardown function for cleaning up.  This function is
104                 // returned to the caller to be invoked when it is done testing.
105                 teardown = func() {
106                         db.Close()
107                         os.RemoveAll(dbPath)
108                         os.RemoveAll(testDbRoot)
109                 }
110         }
111
112         // Copy the chain params to ensure any modifications the tests do to
113         // the chain parameters do not affect the global instance.
114         paramsCopy := *params
115
116         // Create the main chain instance.
117         chain, err := blockchain.New(&blockchain.Config{
118                 DB:          db,
119                 ChainParams: &paramsCopy,
120                 Checkpoints: nil,
121                 TimeSource:  blockchain.NewMedianTime(),
122                 SigCache:    txscript.NewSigCache(1000),
123         })
124         if err != nil {
125                 teardown()
126                 err := fmt.Errorf("failed to create chain instance: %v", err)
127                 return nil, nil, err
128         }
129         return chain, teardown, nil
130 }
131
132 // TestFullBlocks ensures all tests generated by the fullblocktests package
133 // have the expected result when processed via ProcessBlock.
134 func TestFullBlocks(t *testing.T) {
135         tests, err := fullblocktests.Generate(false)
136         if err != nil {
137                 t.Fatalf("failed to generate tests: %v", err)
138         }
139
140         // Create a new database and chain instance to run tests against.
141         chain, teardownFunc, err := chainSetup("fullblocktest",
142                 &chaincfg.RegressionNetParams)
143         if err != nil {
144                 t.Errorf("Failed to setup chain instance: %v", err)
145                 return
146         }
147         defer teardownFunc()
148
149         // testAcceptedBlock attempts to process the block in the provided test
150         // instance and ensures that it was accepted according to the flags
151         // specified in the test.
152         testAcceptedBlock := func(item fullblocktests.AcceptedBlock) {
153                 blockHeight := item.Height
154                 block := btcutil.NewBlock(item.Block)
155                 block.SetHeight(blockHeight)
156                 t.Logf("Testing block %s (hash %s, height %d)",
157                         item.Name, block.Hash(), blockHeight)
158
159                 isMainChain, isOrphan, err := chain.ProcessBlock(block,
160                         blockchain.BFNone)
161                 if err != nil {
162                         t.Fatalf("block %q (hash %s, height %d) should "+
163                                 "have been accepted: %v", item.Name,
164                                 block.Hash(), blockHeight, err)
165                 }
166
167                 // Ensure the main chain and orphan flags match the values
168                 // specified in the test.
169                 if isMainChain != item.IsMainChain {
170                         t.Fatalf("block %q (hash %s, height %d) unexpected main "+
171                                 "chain flag -- got %v, want %v", item.Name,
172                                 block.Hash(), blockHeight, isMainChain,
173                                 item.IsMainChain)
174                 }
175                 if isOrphan != item.IsOrphan {
176                         t.Fatalf("block %q (hash %s, height %d) unexpected "+
177                                 "orphan flag -- got %v, want %v", item.Name,
178                                 block.Hash(), blockHeight, isOrphan,
179                                 item.IsOrphan)
180                 }
181         }
182
183         // testRejectedBlock attempts to process the block in the provided test
184         // instance and ensures that it was rejected with the reject code
185         // specified in the test.
186         testRejectedBlock := func(item fullblocktests.RejectedBlock) {
187                 blockHeight := item.Height
188                 block := btcutil.NewBlock(item.Block)
189                 block.SetHeight(blockHeight)
190                 t.Logf("Testing block %s (hash %s, height %d)",
191                         item.Name, block.Hash(), blockHeight)
192
193                 _, _, err := chain.ProcessBlock(block, blockchain.BFNone)
194                 if err == nil {
195                         t.Fatalf("block %q (hash %s, height %d) should not "+
196                                 "have been accepted", item.Name, block.Hash(),
197                                 blockHeight)
198                 }
199
200                 // Ensure the error code is of the expected type and the reject
201                 // code matches the value specified in the test instance.
202                 rerr, ok := err.(blockchain.RuleError)
203                 if !ok {
204                         t.Fatalf("block %q (hash %s, height %d) returned "+
205                                 "unexpected error type -- got %T, want "+
206                                 "blockchain.RuleError", item.Name, block.Hash(),
207                                 blockHeight, err)
208                 }
209                 if rerr.ErrorCode != item.RejectCode {
210                         t.Fatalf("block %q (hash %s, height %d) does not have "+
211                                 "expected reject code -- got %v, want %v",
212                                 item.Name, block.Hash(), blockHeight,
213                                 rerr.ErrorCode, item.RejectCode)
214                 }
215         }
216
217         // testRejectedNonCanonicalBlock attempts to decode the block in the
218         // provided test instance and ensures that it failed to decode with a
219         // message error.
220         testRejectedNonCanonicalBlock := func(item fullblocktests.RejectedNonCanonicalBlock) {
221                 headerLen := len(item.RawBlock)
222                 if headerLen > 80 {
223                         headerLen = 80
224                 }
225                 blockHash := chainhash.DoubleHashH(item.RawBlock[0:headerLen])
226                 blockHeight := item.Height
227                 t.Logf("Testing block %s (hash %s, height %d)", item.Name,
228                         blockHash, blockHeight)
229
230                 // Ensure there is an error due to deserializing the block.
231                 var msgBlock wire.MsgBlock
232                 err := msgBlock.BtcDecode(bytes.NewReader(item.RawBlock), 0, wire.BaseEncoding)
233                 if _, ok := err.(*wire.MessageError); !ok {
234                         t.Fatalf("block %q (hash %s, height %d) should have "+
235                                 "failed to decode", item.Name, blockHash,
236                                 blockHeight)
237                 }
238         }
239
240         // testOrphanOrRejectedBlock attempts to process the block in the
241         // provided test instance and ensures that it was either accepted as an
242         // orphan or rejected with a rule violation.
243         testOrphanOrRejectedBlock := func(item fullblocktests.OrphanOrRejectedBlock) {
244                 blockHeight := item.Height
245                 block := btcutil.NewBlock(item.Block)
246                 block.SetHeight(blockHeight)
247                 t.Logf("Testing block %s (hash %s, height %d)",
248                         item.Name, block.Hash(), blockHeight)
249
250                 _, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone)
251                 if err != nil {
252                         // Ensure the error code is of the expected type.
253                         if _, ok := err.(blockchain.RuleError); !ok {
254                                 t.Fatalf("block %q (hash %s, height %d) "+
255                                         "returned unexpected error type -- "+
256                                         "got %T, want blockchain.RuleError",
257                                         item.Name, block.Hash(), blockHeight,
258                                         err)
259                         }
260                 }
261
262                 if !isOrphan {
263                         t.Fatalf("block %q (hash %s, height %d) was accepted, "+
264                                 "but is not considered an orphan", item.Name,
265                                 block.Hash(), blockHeight)
266                 }
267         }
268
269         // testExpectedTip ensures the current tip of the blockchain is the
270         // block specified in the provided test instance.
271         testExpectedTip := func(item fullblocktests.ExpectedTip) {
272                 blockHeight := item.Height
273                 block := btcutil.NewBlock(item.Block)
274                 block.SetHeight(blockHeight)
275                 t.Logf("Testing tip for block %s (hash %s, height %d)",
276                         item.Name, block.Hash(), blockHeight)
277
278                 // Ensure hash and height match.
279                 best := chain.BestSnapshot()
280                 if best.Hash != item.Block.BlockHash() ||
281                         best.Height != blockHeight {
282
283                         t.Fatalf("block %q (hash %s, height %d) should be "+
284                                 "the current tip -- got (hash %s, height %d)",
285                                 item.Name, block.Hash(), blockHeight, best.Hash,
286                                 best.Height)
287                 }
288         }
289
290         for testNum, test := range tests {
291                 for itemNum, item := range test {
292                         switch item := item.(type) {
293                         case fullblocktests.AcceptedBlock:
294                                 testAcceptedBlock(item)
295                         case fullblocktests.RejectedBlock:
296                                 testRejectedBlock(item)
297                         case fullblocktests.RejectedNonCanonicalBlock:
298                                 testRejectedNonCanonicalBlock(item)
299                         case fullblocktests.OrphanOrRejectedBlock:
300                                 testOrphanOrRejectedBlock(item)
301                         case fullblocktests.ExpectedTip:
302                                 testExpectedTip(item)
303                         default:
304                                 t.Fatalf("test #%d, item #%d is not one of "+
305                                         "the supported test instance types -- "+
306                                         "got type: %T", testNum, itemNum, item)
307                         }
308                 }
309         }
310 }