chainio_test.go raw
1 package blockchain
2
3 import (
4 "bytes"
5 "errors"
6 "reflect"
7 "testing"
8
9 "github.com/p9c/p9/pkg/database"
10 "github.com/p9c/p9/pkg/wire"
11 )
12
13 // TestErrNotInMainChain ensures the functions related to errNotInMainChain work as expected.
14 func TestErrNotInMainChain(t *testing.T) {
15 errStr := "no block at height 1 exists"
16 e := error(errNotInMainChain(errStr))
17 // Ensure the stringized output for the error is as expected.
18 if e != nil && e.Error() != errStr {
19 t.Fatalf("errNotInMainChain retuned unexpected error string - "+
20 "got %q, want %q", e.Error(), errStr,
21 )
22 }
23 // Ensure error is detected as the correct type.
24 if !isNotInMainChainErr(e) {
25 t.Fatalf("isNotInMainChainErr did not detect as expected type")
26 }
27 e = errors.New("something else")
28 if isNotInMainChainErr(e) {
29 t.Fatalf("isNotInMainChainErr detected incorrect type")
30 }
31 }
32
33 // TestStxoSerialization ensures serializing and deserializing spent transaction output entries works as expected.
34 func TestStxoSerialization(t *testing.T) {
35 t.Parallel()
36 tests := []struct {
37 name string
38 stxo SpentTxOut
39 serialized []byte
40 }{
41 // From block 170 in main blockchain.
42 {
43 name: "Spends last output of coinbase",
44 stxo: SpentTxOut{
45 Amount: 5000000000,
46 PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
47 IsCoinBase: true,
48 Height: 9,
49 },
50 serialized: hexToBytes("1300320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"),
51 },
52 // Adapted from block 100025 in main blockchain.
53 {
54 name: "Spends last output of non coinbase",
55 stxo: SpentTxOut{
56 Amount: 13761000000,
57 PkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"),
58 IsCoinBase: false,
59 Height: 100024,
60 },
61 serialized: hexToBytes("8b99700086c64700b2fb57eadf61e106a100a7445a8c3f67898841ec"),
62 },
63 // Adapted from block 100025 in main blockchain.
64 {
65 name: "Does not spend last output, legacy format",
66 stxo: SpentTxOut{
67 Amount: 34405000000,
68 PkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"),
69 },
70 serialized: hexToBytes("0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"),
71 },
72 }
73 for _, test := range tests {
74 // Ensure the function to calculate the serialized size without actually serializing it is calculated properly.
75 gotSize := spentTxOutSerializeSize(&test.stxo)
76 if gotSize != len(test.serialized) {
77 t.Errorf("SpentTxOutSerializeSize (%s): did not get "+
78 "expected size - got %d, want %d", test.name,
79 gotSize, len(test.serialized),
80 )
81 continue
82 }
83 // Ensure the stxo serializes to the expected value.
84 gotSerialized := make([]byte, gotSize)
85 gotBytesWritten := putSpentTxOut(gotSerialized, &test.stxo)
86 if !bytes.Equal(gotSerialized, test.serialized) {
87 t.Errorf("putSpentTxOut (%s): did not get expected "+
88 "bytes - got %x, want %x", test.name,
89 gotSerialized, test.serialized,
90 )
91 continue
92 }
93 if gotBytesWritten != len(test.serialized) {
94 t.Errorf("putSpentTxOut (%s): did not get expected "+
95 "number of bytes written - got %d, want %d",
96 test.name, gotBytesWritten,
97 len(test.serialized),
98 )
99 continue
100 }
101 // Ensure the serialized bytes are decoded back to the expected stxo.
102 var gotStxo SpentTxOut
103 gotBytesRead, e := decodeSpentTxOut(test.serialized, &gotStxo)
104 if e != nil {
105 t.Errorf("decodeSpentTxOut (%s): unexpected error: %v",
106 test.name, e,
107 )
108 continue
109 }
110 if !reflect.DeepEqual(gotStxo, test.stxo) {
111 t.Errorf("decodeSpentTxOut (%s) mismatched entries - "+
112 "got %v, want %v", test.name, gotStxo, test.stxo,
113 )
114 continue
115 }
116 if gotBytesRead != len(test.serialized) {
117 t.Errorf("decodeSpentTxOut (%s): did not get expected "+
118 "number of bytes read - got %d, want %d",
119 test.name, gotBytesRead, len(test.serialized),
120 )
121 continue
122 }
123 }
124 }
125
126 // TestStxoDecodeErrors performs negative tests against decoding spent transaction outputs to ensure error paths work as
127 // expected.
128 func TestStxoDecodeErrors(t *testing.T) {
129 t.Parallel()
130 tests := []struct {
131 name string
132 stxo SpentTxOut
133 serialized []byte
134 bytesRead int
135 errType error
136 }{
137 {
138 name: "nothing serialized",
139 stxo: SpentTxOut{},
140 serialized: hexToBytes(""),
141 errType: errDeserialize(""),
142 bytesRead: 0,
143 },
144 {
145 name: "no data after header code w/o reserved",
146 stxo: SpentTxOut{},
147 serialized: hexToBytes("00"),
148 errType: errDeserialize(""),
149 bytesRead: 1,
150 },
151 {
152 name: "no data after header code with reserved",
153 stxo: SpentTxOut{},
154 serialized: hexToBytes("13"),
155 errType: errDeserialize(""),
156 bytesRead: 1,
157 },
158 {
159 name: "no data after reserved",
160 stxo: SpentTxOut{},
161 serialized: hexToBytes("1300"),
162 errType: errDeserialize(""),
163 bytesRead: 2,
164 },
165 {
166 name: "incomplete compressed txout",
167 stxo: SpentTxOut{},
168 serialized: hexToBytes("1332"),
169 errType: errDeserialize(""),
170 bytesRead: 2,
171 },
172 }
173 for _, test := range tests {
174 // Ensure the expected error type is returned.
175 gotBytesRead, e := decodeSpentTxOut(test.serialized,
176 &test.stxo,
177 )
178 if reflect.TypeOf(e) != reflect.TypeOf(test.errType) {
179 t.Errorf("decodeSpentTxOut (%s): expected error type "+
180 "does not match - got %T, want %T", test.name,
181 e, test.errType,
182 )
183 continue
184 }
185 // Ensure the expected number of bytes read is returned.
186 if gotBytesRead != test.bytesRead {
187 t.Errorf("decodeSpentTxOut (%s): unexpected number of "+
188 "bytes read - got %d, want %d", test.name,
189 gotBytesRead, test.bytesRead,
190 )
191 continue
192 }
193 }
194 }
195
196 // TestSpendJournalSerialization ensures serializing and deserializing spend journal entries works as expected.
197 func TestSpendJournalSerialization(t *testing.T) {
198 t.Parallel()
199 tests := []struct {
200 name string
201 entry []SpentTxOut
202 blockTxns []*wire.MsgTx
203 serialized []byte
204 }{
205 // From block 2 in main blockchain.
206 {
207 name: "No spends",
208 entry: nil,
209 blockTxns: nil,
210 serialized: nil,
211 },
212 // From block 170 in main blockchain.
213 {
214 name: "One tx with one input spends last output of coinbase",
215 entry: []SpentTxOut{{
216 Amount: 5000000000,
217 PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
218 IsCoinBase: true,
219 Height: 9,
220 },
221 },
222 blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
223 Version: 1,
224 TxIn: []*wire.TxIn{{
225 PreviousOutPoint: wire.OutPoint{
226 Hash: *newHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
227 Index: 0,
228 },
229 SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
230 Sequence: 0xffffffff,
231 },
232 },
233 TxOut: []*wire.TxOut{{
234 Value: 1000000000,
235 PkScript: hexToBytes("4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac"),
236 },
237 {
238 Value: 4000000000,
239 PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
240 },
241 },
242 LockTime: 0,
243 },
244 },
245 serialized: hexToBytes("1300320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"),
246 },
247 // Adapted from block 100025 in main blockchain.
248 {
249 name: "Two txns when one spends last output, one doesn't",
250 entry: []SpentTxOut{{
251 Amount: 34405000000,
252 PkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"),
253 IsCoinBase: false,
254 Height: 100024,
255 },
256 {
257 Amount: 13761000000,
258 PkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"),
259 IsCoinBase: false,
260 Height: 100024,
261 },
262 },
263 blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
264 Version: 1,
265 TxIn: []*wire.TxIn{{
266 PreviousOutPoint: wire.OutPoint{
267 Hash: *newHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"),
268 Index: 1,
269 },
270 SignatureScript: hexToBytes("493046022100c167eead9840da4a033c9a56470d7794a9bb1605b377ebe5688499b39f94be59022100fb6345cab4324f9ea0b9ee9169337534834638d818129778370f7d378ee4a325014104d962cac5390f12ddb7539507065d0def320d68c040f2e73337c3a1aaaab7195cb5c4d02e0959624d534f3c10c3cf3d73ca5065ebd62ae986b04c6d090d32627c"),
271 Sequence: 0xffffffff,
272 },
273 },
274 TxOut: []*wire.TxOut{{
275 Value: 5000000,
276 PkScript: hexToBytes("76a914f419b8db4ba65f3b6fcc233acb762ca6f51c23d488ac"),
277 },
278 {
279 Value: 34400000000,
280 PkScript: hexToBytes("76a914cadf4fc336ab3c6a4610b75f31ba0676b7f663d288ac"),
281 },
282 },
283 LockTime: 0,
284 },
285 {
286 Version: 1,
287 TxIn: []*wire.TxIn{{
288 PreviousOutPoint: wire.OutPoint{
289 Hash: *newHashFromStr("92fbe1d4be82f765dfabc9559d4620864b05cc897c4db0e29adac92d294e52b7"),
290 Index: 0,
291 },
292 SignatureScript: hexToBytes("483045022100e256743154c097465cf13e89955e1c9ff2e55c46051b627751dee0144183157e02201d8d4f02cde8496aae66768f94d35ce54465bd4ae8836004992d3216a93a13f00141049d23ce8686fe9b802a7a938e8952174d35dd2c2089d4112001ed8089023ab4f93a3c9fcd5bfeaa9727858bf640dc1b1c05ec3b434bb59837f8640e8810e87742"),
293 Sequence: 0xffffffff,
294 },
295 },
296 TxOut: []*wire.TxOut{{
297 Value: 5000000,
298 PkScript: hexToBytes("76a914a983ad7c92c38fc0e2025212e9f972204c6e687088ac"),
299 },
300 {
301 Value: 13756000000,
302 PkScript: hexToBytes("76a914a6ebd69952ab486a7a300bfffdcb395dc7d47c2388ac"),
303 },
304 },
305 LockTime: 0,
306 },
307 },
308 serialized: hexToBytes("8b99700086c64700b2fb57eadf61e106a100a7445a8c3f67898841ec8b99700091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"),
309 },
310 }
311 for i, test := range tests {
312 // Ensure the journal entry serializes to the expected value.
313 gotBytes := serializeSpendJournalEntry(test.entry)
314 if !bytes.Equal(gotBytes, test.serialized) {
315 t.Errorf("serializeSpendJournalEntry #%d (%s): "+
316 "mismatched bytes - got %x, want %x", i,
317 test.name, gotBytes, test.serialized,
318 )
319 continue
320 }
321 // Deserialize to a spend journal entry.
322 gotEntry, e := deserializeSpendJournalEntry(test.serialized,
323 test.blockTxns,
324 )
325 if e != nil {
326 t.Errorf("deserializeSpendJournalEntry #%d (%s) "+
327 "unexpected error: %v", i, test.name, e,
328 )
329 continue
330 }
331 // Ensure that the deserialized spend journal entry has the
332 // correct properties.
333 if !reflect.DeepEqual(gotEntry, test.entry) {
334 t.Errorf("deserializeSpendJournalEntry #%d (%s) "+
335 "mismatched entries - got %v, want %v",
336 i, test.name, gotEntry, test.entry,
337 )
338 continue
339 }
340 }
341 }
342
343 // TestSpendJournalErrors performs negative tests against deserializing spend journal entries to ensure error paths work
344 // as expected.
345 func TestSpendJournalErrors(t *testing.T) {
346 t.Parallel()
347 tests := []struct {
348 name string
349 blockTxns []*wire.MsgTx
350 serialized []byte
351 errType error
352 }{
353 // Adapted from block 170 in main blockchain.
354 {
355 name: "Force assertion due to missing stxos",
356 blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
357 Version: 1,
358 TxIn: []*wire.TxIn{{
359 PreviousOutPoint: wire.OutPoint{
360 Hash: *newHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
361 Index: 0,
362 },
363 SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
364 Sequence: 0xffffffff,
365 },
366 },
367 LockTime: 0,
368 },
369 },
370 serialized: hexToBytes(""),
371 errType: AssertError(""),
372 },
373 {
374 name: "Force deserialization error in stxos",
375 blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
376 Version: 1,
377 TxIn: []*wire.TxIn{{
378 PreviousOutPoint: wire.OutPoint{
379 Hash: *newHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
380 Index: 0,
381 },
382 SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
383 Sequence: 0xffffffff,
384 },
385 },
386 LockTime: 0,
387 },
388 },
389 serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a"),
390 errType: errDeserialize(""),
391 },
392 }
393 for _, test := range tests {
394 // Ensure the expected error type is returned and the returned slice is nil.
395 stxos, e := deserializeSpendJournalEntry(test.serialized,
396 test.blockTxns,
397 )
398 if reflect.TypeOf(e) != reflect.TypeOf(test.errType) {
399 t.Errorf("deserializeSpendJournalEntry (%s): expected "+
400 "error type does not match - got %T, want %T",
401 test.name, e, test.errType,
402 )
403 continue
404 }
405 if stxos != nil {
406 t.Errorf("deserializeSpendJournalEntry (%s): returned "+
407 "slice of spent transaction outputs is not nil",
408 test.name,
409 )
410 continue
411 }
412 }
413 }
414
415 // TestUtxoSerialization ensures serializing and deserializing unspent trasaction output entries works as expected.
416 func TestUtxoSerialization(t *testing.T) {
417 t.Parallel()
418 tests := []struct {
419 name string
420 entry *UtxoEntry
421 serialized []byte
422 }{
423 // From tx in main blockchain: 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098:0
424 {
425 name: "height 1, coinbase",
426 entry: &UtxoEntry{
427 amount: 5000000000,
428 pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
429 blockHeight: 1,
430 packedFlags: tfCoinBase,
431 },
432 serialized: hexToBytes("03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"),
433 },
434 // From tx in main blockchain: 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098:0
435 {
436 name: "height 1, coinbase, spent",
437 entry: &UtxoEntry{
438 amount: 5000000000,
439 pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
440 blockHeight: 1,
441 packedFlags: tfCoinBase | tfSpent,
442 },
443 serialized: nil,
444 },
445 // From tx in main blockchain: 8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb:1
446 {
447 name: "height 100001, not coinbase",
448 entry: &UtxoEntry{
449 amount: 1000000,
450 pkScript: hexToBytes("76a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"),
451 blockHeight: 100001,
452 packedFlags: 0,
453 },
454 serialized: hexToBytes("8b99420700ee8bd501094a7d5ca318da2506de35e1cb025ddc"),
455 },
456 // From tx in main blockchain: 8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb:1
457 {
458 name: "height 100001, not coinbase, spent",
459 entry: &UtxoEntry{
460 amount: 1000000,
461 pkScript: hexToBytes("76a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"),
462 blockHeight: 100001,
463 packedFlags: tfSpent,
464 },
465 serialized: nil,
466 },
467 }
468 for i, test := range tests {
469 // Ensure the utxo entry serializes to the expected value.
470 gotBytes, e := serializeUtxoEntry(test.entry)
471 if e != nil {
472 t.Errorf("serializeUtxoEntry #%d (%s) unexpected "+
473 "error: %v", i, test.name, e,
474 )
475 continue
476 }
477 if !bytes.Equal(gotBytes, test.serialized) {
478 t.Errorf("serializeUtxoEntry #%d (%s): mismatched "+
479 "bytes - got %x, want %x", i, test.name,
480 gotBytes, test.serialized,
481 )
482 continue
483 }
484 // Don't try to deserialize if the test entry was spent since it will have a nil serialization.
485 if test.entry.IsSpent() {
486 continue
487 }
488 // Deserialize to a utxo entry.
489 utxoEntry, e := deserializeUtxoEntry(test.serialized)
490 if e != nil {
491 t.Errorf("deserializeUtxoEntry #%d (%s) unexpected "+
492 "error: %v", i, test.name, e,
493 )
494 continue
495 }
496 // The deserialized entry must not be marked spent since unspent entries are not serialized.
497 if utxoEntry.IsSpent() {
498 t.Errorf("deserializeUtxoEntry #%d (%s) output should "+
499 "not be marked spent", i, test.name,
500 )
501 continue
502 }
503 // Ensure the deserialized entry has the same properties as the ones in the test entry.
504 if utxoEntry.Amount() != test.entry.Amount() {
505 t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
506 "amounts: got %d, want %d", i, test.name,
507 utxoEntry.Amount(), test.entry.Amount(),
508 )
509 continue
510 }
511 if !bytes.Equal(utxoEntry.PkScript(), test.entry.PkScript()) {
512 t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
513 "scripts: got %x, want %x", i, test.name,
514 utxoEntry.PkScript(), test.entry.PkScript(),
515 )
516 continue
517 }
518 if utxoEntry.BlockHeight() != test.entry.BlockHeight() {
519 t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
520 "block height: got %d, want %d", i, test.name,
521 utxoEntry.BlockHeight(), test.entry.BlockHeight(),
522 )
523 continue
524 }
525 if utxoEntry.IsCoinBase() != test.entry.IsCoinBase() {
526 t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
527 "coinbase flag: got %v, want %v", i, test.name,
528 utxoEntry.IsCoinBase(), test.entry.IsCoinBase(),
529 )
530 continue
531 }
532 }
533 }
534
535 // TestUtxoEntryHeaderCodeErrors performs negative tests against unspent transaction output header codes to ensure error
536 // paths work as expected.
537 func TestUtxoEntryHeaderCodeErrors(t *testing.T) {
538 t.Parallel()
539 tests := []struct {
540 name string
541 entry *UtxoEntry
542 // code uint64
543 errType error
544 }{
545 {
546 name: "Force assertion due to spent output",
547 entry: &UtxoEntry{packedFlags: tfSpent},
548 errType: AssertError(""),
549 },
550 }
551 for _, test := range tests {
552 // Ensure the expected error type is returned and the code is 0.
553 code, e := utxoEntryHeaderCode(test.entry)
554 if reflect.TypeOf(e) != reflect.TypeOf(test.errType) {
555 t.Errorf("utxoEntryHeaderCode (%s): expected error "+
556 "type does not match - got %T, want %T",
557 test.name, e, test.errType,
558 )
559 continue
560 }
561 if code != 0 {
562 t.Errorf("utxoEntryHeaderCode (%s): unexpected code "+
563 "on error - got %d, want 0", test.name, code,
564 )
565 continue
566 }
567 }
568 }
569
570 // TestUtxoEntryDeserializeErrors performs negative tests against deserializing unspent transaction outputs to ensure
571 // error paths work as expected.
572 func TestUtxoEntryDeserializeErrors(t *testing.T) {
573 t.Parallel()
574 tests := []struct {
575 name string
576 serialized []byte
577 errType error
578 }{
579 {
580 name: "no data after header code",
581 serialized: hexToBytes("02"),
582 errType: errDeserialize(""),
583 },
584 {
585 name: "incomplete compressed txout",
586 serialized: hexToBytes("0232"),
587 errType: errDeserialize(""),
588 },
589 }
590 for _, test := range tests {
591 // Ensure the expected error type is returned and the returned entry is nil.
592 entry, e := deserializeUtxoEntry(test.serialized)
593 if reflect.TypeOf(e) != reflect.TypeOf(test.errType) {
594 t.Errorf("deserializeUtxoEntry (%s): expected error "+
595 "type does not match - got %T, want %T",
596 test.name, e, test.errType,
597 )
598 continue
599 }
600 if entry != nil {
601 t.Errorf("deserializeUtxoEntry (%s): returned entry "+
602 "is not nil", test.name,
603 )
604 continue
605 }
606 }
607 }
608
609 // // TestBestChainStateSerialization ensures serializing and deserializing the best chain state works as expected.
610 // func TestBestChainStateSerialization(// t *testing.T) {
611 // t.Parallel()
612 // workSum := new(big.Int)
613 // tests := []struct {
614 // name string
615 // state bestChainState
616 // serialized []byte
617 // }{
618 // {
619 // name: "genesis",
620 // state: bestChainState{
621 // hash: *newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
622 // height: 0,
623 // totalTxns: 1,
624 // workSum: func() *big.Int {
625 // workSum.Add(workSum, CalcWork(486604799, 0, 2))
626 // return new(big.Int).Set(workSum)
627 // }(),
628 // // 0x0100010001
629 // },
630 // serialized: hexToBytes("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000100000000000000050000000100010001"),
631 // },
632 // {
633 // name: "block 1",
634 // state: bestChainState{
635 // hash: *newHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"),
636 // height: 1,
637 // totalTxns: 2,
638 // workSum: func() *big.Int {
639 // workSum.Add(workSum, CalcWork(486604799, 1, 2))
640 // return new(big.Int).Set(workSum)
641 // }(),
642 // // 0x0200020002
643 // },
644 // serialized: hexToBytes("4860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000010000000200000000000000050000000200020002"),
645 // },
646 // }
647 // for i, test := range tests {
648 // // Ensure the state serializes to the expected value.
649 // gotBytes := serializeBestChainState(test.state)
650 // if !bytes.Equal(gotBytes, test.serialized) {
651 // t.Errorf("serializeBestChainState #%d (%s): mismatched "+
652 // "bytes - got %x, want %x", i, test.name,
653 // gotBytes, test.serialized)
654 // continue
655 // }
656 // // Ensure the serialized bytes are decoded back to the expected state.
657 // state, e := deserializeBestChainState(test.serialized)
658 // if e != nil {
659 // t.Errorf("deserializeBestChainState #%d (%s) "+
660 // "unexpected error: %v", i, test.name, e)
661 // continue
662 // }
663 // if !reflect.DeepEqual(state, test.state) {
664 // t.Errorf("deserializeBestChainState #%d (%s) "+
665 // "mismatched state - got %v, want %v", i,
666 // test.name, state, test.state)
667 // continue
668 // }
669 // }
670 // }
671
672 // TestBestChainStateDeserializeErrors performs negative tests against deserializing the chain state to ensure error
673 // paths work as expected.
674 func TestBestChainStateDeserializeErrors(t *testing.T) {
675 t.Parallel()
676 tests := []struct {
677 name string
678 serialized []byte
679 errType error
680 }{
681 {
682 name: "nothing serialized",
683 serialized: hexToBytes(""),
684 errType: database.DBError{ErrorCode: database.ErrCorruption},
685 },
686 {
687 name: "short data in hash",
688 serialized: hexToBytes("0000"),
689 errType: database.DBError{ErrorCode: database.ErrCorruption},
690 },
691 {
692 name: "short data in work sum",
693 serialized: hexToBytes("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000001000000000000000500000001000100"),
694 errType: database.DBError{ErrorCode: database.ErrCorruption},
695 },
696 }
697 for _, test := range tests {
698 // Ensure the expected error type and code is returned.
699 _, e := deserializeBestChainState(test.serialized)
700 if reflect.TypeOf(e) != reflect.TypeOf(test.errType) {
701 t.Errorf("deserializeBestChainState (%s): expected "+
702 "error type does not match - got %T, want %T",
703 test.name, e, test.errType,
704 )
705 continue
706 }
707 if derr, ok := e.(database.DBError); ok {
708 tderr := test.errType.(database.DBError)
709 if derr.ErrorCode != tderr.ErrorCode {
710 t.Errorf("deserializeBestChainState (%s): "+
711 "wrong error code got: %v, want: %v",
712 test.name, derr.ErrorCode,
713 tderr.ErrorCode,
714 )
715 continue
716 }
717 }
718 }
719 }
720