1 // Copyright (c) 2015-2016 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
14 "github.com/btcsuite/btcd/chaincfg/chainhash"
15 "github.com/btcsuite/btcd/database"
16 "github.com/btcsuite/btcd/wire"
19 // TestErrNotInMainChain ensures the functions related to errNotInMainChain work
21 func TestErrNotInMainChain(t *testing.T) {
22 errStr := "no block at height 1 exists"
23 err := error(errNotInMainChain(errStr))
25 // Ensure the stringized output for the error is as expected.
26 if err.Error() != errStr {
27 t.Fatalf("errNotInMainChain retuned unexpected error string - "+
28 "got %q, want %q", err.Error(), errStr)
31 // Ensure error is detected as the correct type.
32 if !isNotInMainChainErr(err) {
33 t.Fatalf("isNotInMainChainErr did not detect as expected type")
35 err = errors.New("something else")
36 if isNotInMainChainErr(err) {
37 t.Fatalf("isNotInMainChainErr detected incorrect type")
41 // maybeDecompress decompresses the amount and public key script fields of the
42 // stxo and marks it decompressed if needed.
43 func (o *spentTxOut) maybeDecompress(version int32) {
44 // Nothing to do if it's not compressed.
49 o.amount = int64(decompressTxOutAmount(uint64(o.amount)))
50 o.pkScript = decompressScript(o.pkScript, version)
54 // TestStxoSerialization ensures serializing and deserializing spent transaction
55 // output entries works as expected.
56 func TestStxoSerialization(t *testing.T) {
62 txVersion int32 // When the txout is not fully spent.
65 // From block 170 in main blockchain.
67 name: "Spends last output of coinbase",
70 pkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
75 serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"),
77 // Adapted from block 100025 in main blockchain.
79 name: "Spends last output of non coinbase",
82 pkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"),
87 serialized: hexToBytes("8b99700186c64700b2fb57eadf61e106a100a7445a8c3f67898841ec"),
89 // Adapted from block 100025 in main blockchain.
91 name: "Does not spend last output",
94 pkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"),
98 serialized: hexToBytes("0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"),
102 for _, test := range tests {
103 // Ensure the function to calculate the serialized size without
104 // actually serializing it is calculated properly.
105 gotSize := spentTxOutSerializeSize(&test.stxo)
106 if gotSize != len(test.serialized) {
107 t.Errorf("spentTxOutSerializeSize (%s): did not get "+
108 "expected size - got %d, want %d", test.name,
109 gotSize, len(test.serialized))
113 // Ensure the stxo serializes to the expected value.
114 gotSerialized := make([]byte, gotSize)
115 gotBytesWritten := putSpentTxOut(gotSerialized, &test.stxo)
116 if !bytes.Equal(gotSerialized, test.serialized) {
117 t.Errorf("putSpentTxOut (%s): did not get expected "+
118 "bytes - got %x, want %x", test.name,
119 gotSerialized, test.serialized)
122 if gotBytesWritten != len(test.serialized) {
123 t.Errorf("putSpentTxOut (%s): did not get expected "+
124 "number of bytes written - got %d, want %d",
125 test.name, gotBytesWritten,
126 len(test.serialized))
130 // Ensure the serialized bytes are decoded back to the expected
132 var gotStxo spentTxOut
133 gotBytesRead, err := decodeSpentTxOut(test.serialized, &gotStxo,
136 t.Errorf("decodeSpentTxOut (%s): unexpected error: %v",
140 gotStxo.maybeDecompress(test.stxo.version)
141 if !reflect.DeepEqual(gotStxo, test.stxo) {
142 t.Errorf("decodeSpentTxOut (%s) mismatched entries - "+
143 "got %v, want %v", test.name, gotStxo, test.stxo)
146 if gotBytesRead != len(test.serialized) {
147 t.Errorf("decodeSpentTxOut (%s): did not get expected "+
148 "number of bytes read - got %d, want %d",
149 test.name, gotBytesRead, len(test.serialized))
155 // TestStxoDecodeErrors performs negative tests against decoding spent
156 // transaction outputs to ensure error paths work as expected.
157 func TestStxoDecodeErrors(t *testing.T) {
163 txVersion int32 // When the txout is not fully spent.
165 bytesRead int // Expected number of bytes read.
169 name: "nothing serialized",
171 serialized: hexToBytes(""),
172 errType: errDeserialize(""),
176 name: "no data after header code w/o version",
178 serialized: hexToBytes("00"),
179 errType: errDeserialize(""),
183 name: "no data after header code with version",
185 serialized: hexToBytes("13"),
186 errType: errDeserialize(""),
190 name: "no data after version",
192 serialized: hexToBytes("1301"),
193 errType: errDeserialize(""),
197 name: "no serialized tx version and passed -1",
200 serialized: hexToBytes("003205"),
201 errType: AssertError(""),
205 name: "incomplete compressed txout",
208 serialized: hexToBytes("0032"),
209 errType: errDeserialize(""),
214 for _, test := range tests {
215 // Ensure the expected error type is returned.
216 gotBytesRead, err := decodeSpentTxOut(test.serialized,
217 &test.stxo, test.txVersion)
218 if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
219 t.Errorf("decodeSpentTxOut (%s): expected error type "+
220 "does not match - got %T, want %T", test.name,
225 // Ensure the expected number of bytes read is returned.
226 if gotBytesRead != test.bytesRead {
227 t.Errorf("decodeSpentTxOut (%s): unexpected number of "+
228 "bytes read - got %d, want %d", test.name,
229 gotBytesRead, test.bytesRead)
235 // TestSpendJournalSerialization ensures serializing and deserializing spend
236 // journal entries works as expected.
237 func TestSpendJournalSerialization(t *testing.T) {
243 blockTxns []*wire.MsgTx
244 utxoView *UtxoViewpoint
247 // From block 2 in main blockchain.
252 utxoView: NewUtxoViewpoint(),
255 // From block 170 in main blockchain.
257 name: "One tx with one input spends last output of coinbase",
258 entry: []spentTxOut{{
260 pkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
265 blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
268 PreviousOutPoint: wire.OutPoint{
269 Hash: *newHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
272 SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
273 Sequence: 0xffffffff,
275 TxOut: []*wire.TxOut{{
277 PkScript: hexToBytes("4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac"),
280 PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
284 utxoView: NewUtxoViewpoint(),
285 serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"),
287 // Adapted from block 100025 in main blockchain.
289 name: "Two txns when one spends last output, one doesn't",
290 entry: []spentTxOut{{
292 pkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"),
296 pkScript: hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"),
301 blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
304 PreviousOutPoint: wire.OutPoint{
305 Hash: *newHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"),
308 SignatureScript: hexToBytes("493046022100c167eead9840da4a033c9a56470d7794a9bb1605b377ebe5688499b39f94be59022100fb6345cab4324f9ea0b9ee9169337534834638d818129778370f7d378ee4a325014104d962cac5390f12ddb7539507065d0def320d68c040f2e73337c3a1aaaab7195cb5c4d02e0959624d534f3c10c3cf3d73ca5065ebd62ae986b04c6d090d32627c"),
309 Sequence: 0xffffffff,
311 TxOut: []*wire.TxOut{{
313 PkScript: hexToBytes("76a914f419b8db4ba65f3b6fcc233acb762ca6f51c23d488ac"),
316 PkScript: hexToBytes("76a914cadf4fc336ab3c6a4610b75f31ba0676b7f663d288ac"),
322 PreviousOutPoint: wire.OutPoint{
323 Hash: *newHashFromStr("92fbe1d4be82f765dfabc9559d4620864b05cc897c4db0e29adac92d294e52b7"),
326 SignatureScript: hexToBytes("483045022100e256743154c097465cf13e89955e1c9ff2e55c46051b627751dee0144183157e02201d8d4f02cde8496aae66768f94d35ce54465bd4ae8836004992d3216a93a13f00141049d23ce8686fe9b802a7a938e8952174d35dd2c2089d4112001ed8089023ab4f93a3c9fcd5bfeaa9727858bf640dc1b1c05ec3b434bb59837f8640e8810e87742"),
327 Sequence: 0xffffffff,
329 TxOut: []*wire.TxOut{{
331 PkScript: hexToBytes("76a914a983ad7c92c38fc0e2025212e9f972204c6e687088ac"),
334 PkScript: hexToBytes("76a914a6ebd69952ab486a7a300bfffdcb395dc7d47c2388ac"),
338 utxoView: &UtxoViewpoint{entries: map[chainhash.Hash]*UtxoEntry{
339 *newHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"): {
343 sparseOutputs: map[uint32]*utxoOutput{
346 pkScript: hexToBytes("76a9142084541c3931677527a7eafe56fd90207c344eb088ac"),
351 serialized: hexToBytes("8b99700186c64700b2fb57eadf61e106a100a7445a8c3f67898841ec0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"),
355 name: "One tx, two inputs from same tx, neither spend last output",
356 entry: []spentTxOut{{
358 pkScript: hexToBytes("51"),
362 pkScript: hexToBytes("51"),
365 blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
368 PreviousOutPoint: wire.OutPoint{
369 Hash: *newHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"),
372 SignatureScript: hexToBytes(""),
373 Sequence: 0xffffffff,
375 PreviousOutPoint: wire.OutPoint{
376 Hash: *newHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"),
379 SignatureScript: hexToBytes(""),
380 Sequence: 0xffffffff,
382 TxOut: []*wire.TxOut{{
384 PkScript: hexToBytes("51"),
387 PkScript: hexToBytes("51"),
391 utxoView: &UtxoViewpoint{entries: map[chainhash.Hash]*UtxoEntry{
392 *newHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"): {
396 sparseOutputs: map[uint32]*utxoOutput{
399 pkScript: hexToBytes("51"),
404 serialized: hexToBytes("0087bc3707510084c3d19a790751"),
408 for i, test := range tests {
409 // Ensure the journal entry serializes to the expected value.
410 gotBytes := serializeSpendJournalEntry(test.entry)
411 if !bytes.Equal(gotBytes, test.serialized) {
412 t.Errorf("serializeSpendJournalEntry #%d (%s): "+
413 "mismatched bytes - got %x, want %x", i,
414 test.name, gotBytes, test.serialized)
418 // Deserialize to a spend journal entry.
419 gotEntry, err := deserializeSpendJournalEntry(test.serialized,
420 test.blockTxns, test.utxoView)
422 t.Errorf("deserializeSpendJournalEntry #%d (%s) "+
423 "unexpected error: %v", i, test.name, err)
426 for stxoIdx := range gotEntry {
427 stxo := &gotEntry[stxoIdx]
428 stxo.maybeDecompress(test.entry[stxoIdx].version)
431 // Ensure that the deserialized spend journal entry has the
432 // correct properties.
433 if !reflect.DeepEqual(gotEntry, test.entry) {
434 t.Errorf("deserializeSpendJournalEntry #%d (%s) "+
435 "mismatched entries - got %v, want %v",
436 i, test.name, gotEntry, test.entry)
442 // TestSpendJournalErrors performs negative tests against deserializing spend
443 // journal entries to ensure error paths work as expected.
444 func TestSpendJournalErrors(t *testing.T) {
449 blockTxns []*wire.MsgTx
450 utxoView *UtxoViewpoint
454 // Adapted from block 170 in main blockchain.
456 name: "Force assertion due to missing stxos",
457 blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
460 PreviousOutPoint: wire.OutPoint{
461 Hash: *newHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
464 SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
465 Sequence: 0xffffffff,
469 utxoView: NewUtxoViewpoint(),
470 serialized: hexToBytes(""),
471 errType: AssertError(""),
474 name: "Force deserialization error in stxos",
475 blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
478 PreviousOutPoint: wire.OutPoint{
479 Hash: *newHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
482 SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
483 Sequence: 0xffffffff,
487 utxoView: NewUtxoViewpoint(),
488 serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a"),
489 errType: errDeserialize(""),
493 for _, test := range tests {
494 // Ensure the expected error type is returned and the returned
496 stxos, err := deserializeSpendJournalEntry(test.serialized,
497 test.blockTxns, test.utxoView)
498 if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
499 t.Errorf("deserializeSpendJournalEntry (%s): expected "+
500 "error type does not match - got %T, want %T",
501 test.name, err, test.errType)
505 t.Errorf("deserializeSpendJournalEntry (%s): returned "+
506 "slice of spent transaction outputs is not nil",
513 // TestUtxoSerialization ensures serializing and deserializing unspent
514 // trasaction output entries works as expected.
515 func TestUtxoSerialization(t *testing.T) {
523 // From tx in main blockchain:
524 // 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098
526 name: "Only output 0, coinbase",
531 sparseOutputs: map[uint32]*utxoOutput{
534 pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
538 serialized: hexToBytes("010103320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"),
540 // From tx in main blockchain:
541 // 8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb
543 name: "Only output 1, not coinbase",
548 sparseOutputs: map[uint32]*utxoOutput{
551 pkScript: hexToBytes("76a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"),
555 serialized: hexToBytes("01858c21040700ee8bd501094a7d5ca318da2506de35e1cb025ddc"),
557 // Adapted from tx in main blockchain:
558 // df3f3f442d9699857f7f49de4ff0b5d0f3448bec31cdc7b5bf6d25f2abd637d5
560 name: "Only output 2, coinbase",
565 sparseOutputs: map[uint32]*utxoOutput{
568 pkScript: hexToBytes("76a914da33f77cee27c2a975ed5124d7e4f7f97513510188ac"),
572 serialized: hexToBytes("0185843c010182b095bf4100da33f77cee27c2a975ed5124d7e4f7f975135101"),
574 // Adapted from tx in main blockchain:
575 // 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
577 name: "outputs 0 and 2 not coinbase",
582 sparseOutputs: map[uint32]*utxoOutput{
585 pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"),
589 pkScript: hexToBytes("76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac"),
593 serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
595 // Adapted from tx in main blockchain:
596 // 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
598 name: "outputs 0 and 2, not coinbase, 1 marked spent",
603 sparseOutputs: map[uint32]*utxoOutput{
606 pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"),
608 1: { // This won't be serialized.
611 pkScript: hexToBytes("76a914e43031c3e46f20bf1ccee9553ce815de5a48467588ac"),
615 pkScript: hexToBytes("76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac"),
619 serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
621 // Adapted from tx in main blockchain:
622 // 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
624 name: "outputs 0 and 2, not coinbase, output 2 compressed",
629 sparseOutputs: map[uint32]*utxoOutput{
632 pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"),
635 // Uncompressed Amount: 15000000
636 // Uncompressed PkScript: 76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac
639 pkScript: hexToBytes("00b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
643 serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
645 // Adapted from tx in main blockchain:
646 // 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
648 name: "outputs 0 and 2, not coinbase, output 2 compressed, packed indexes reversed",
653 sparseOutputs: map[uint32]*utxoOutput{
656 pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"),
659 // Uncompressed Amount: 15000000
660 // Uncompressed PkScript: 76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac
663 pkScript: hexToBytes("00b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
667 serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
669 // From tx in main blockchain:
670 // 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098
672 name: "Only output 0, coinbase, fully spent",
677 sparseOutputs: map[uint32]*utxoOutput{
681 pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
687 // Adapted from tx in main blockchain:
688 // 1b02d1c8cfef60a189017b9a420c682cf4a0028175f2f563209e4ff61c8c3620
690 name: "Only output 22, not coinbase",
695 sparseOutputs: map[uint32]*utxoOutput{
699 pkScript: hexToBytes("a9141dd46a006572d820e448e12d2bbb38640bc718e687"),
703 serialized: hexToBytes("0193d06c100000108ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6"),
707 for i, test := range tests {
708 // Ensure the utxo entry serializes to the expected value.
709 gotBytes, err := serializeUtxoEntry(test.entry)
711 t.Errorf("serializeUtxoEntry #%d (%s) unexpected "+
712 "error: %v", i, test.name, err)
715 if !bytes.Equal(gotBytes, test.serialized) {
716 t.Errorf("serializeUtxoEntry #%d (%s): mismatched "+
717 "bytes - got %x, want %x", i, test.name,
718 gotBytes, test.serialized)
722 // Don't try to deserialize if the test entry was fully spent
723 // since it will have a nil serialization.
724 if test.entry.IsFullySpent() {
728 // Deserialize to a utxo entry.
729 utxoEntry, err := deserializeUtxoEntry(test.serialized)
731 t.Errorf("deserializeUtxoEntry #%d (%s) unexpected "+
732 "error: %v", i, test.name, err)
736 // Ensure that the deserialized utxo entry has the same
737 // properties for the containing transaction and block height.
738 if utxoEntry.Version() != test.entry.Version() {
739 t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
740 "version: got %d, want %d", i, test.name,
741 utxoEntry.Version(), test.entry.Version())
744 if utxoEntry.IsCoinBase() != test.entry.IsCoinBase() {
745 t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
746 "coinbase flag: got %v, want %v", i, test.name,
747 utxoEntry.IsCoinBase(), test.entry.IsCoinBase())
750 if utxoEntry.BlockHeight() != test.entry.BlockHeight() {
751 t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
752 "block height: got %d, want %d", i, test.name,
753 utxoEntry.BlockHeight(),
754 test.entry.BlockHeight())
757 if utxoEntry.IsFullySpent() != test.entry.IsFullySpent() {
758 t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
759 "fully spent: got %v, want %v", i, test.name,
760 utxoEntry.IsFullySpent(),
761 test.entry.IsFullySpent())
765 // Ensure all of the outputs in the test entry match the
766 // spentness of the output in the deserialized entry and the
767 // deserialized entry does not contain any additional utxos.
769 for outputIndex := range test.entry.sparseOutputs {
770 gotSpent := utxoEntry.IsOutputSpent(outputIndex)
771 wantSpent := test.entry.IsOutputSpent(outputIndex)
775 if gotSpent != wantSpent {
776 t.Errorf("deserializeUtxoEntry #%d (%s) output "+
777 "#%d: mismatched spent: got %v, want "+
778 "%v", i, test.name, outputIndex,
784 if len(utxoEntry.sparseOutputs) != numUnspent {
785 t.Errorf("deserializeUtxoEntry #%d (%s): mismatched "+
786 "number of unspent outputs: got %d, want %d", i,
787 test.name, len(utxoEntry.sparseOutputs),
792 // Ensure all of the amounts and scripts of the utxos in the
793 // deserialized entry match the ones in the test entry.
794 for outputIndex := range utxoEntry.sparseOutputs {
795 gotAmount := utxoEntry.AmountByIndex(outputIndex)
796 wantAmount := test.entry.AmountByIndex(outputIndex)
797 if gotAmount != wantAmount {
798 t.Errorf("deserializeUtxoEntry #%d (%s) "+
799 "output #%d: mismatched amounts: got "+
800 "%d, want %d", i, test.name,
801 outputIndex, gotAmount, wantAmount)
805 gotPkScript := utxoEntry.PkScriptByIndex(outputIndex)
806 wantPkScript := test.entry.PkScriptByIndex(outputIndex)
807 if !bytes.Equal(gotPkScript, wantPkScript) {
808 t.Errorf("deserializeUtxoEntry #%d (%s) "+
809 "output #%d mismatched scripts: got "+
810 "%x, want %x", i, test.name,
811 outputIndex, gotPkScript, wantPkScript)
818 // TestUtxoEntryHeaderCodeErrors performs negative tests against unspent
819 // transaction output header codes to ensure error paths work as expected.
820 func TestUtxoEntryHeaderCodeErrors(t *testing.T) {
827 bytesRead int // Expected number of bytes read.
831 name: "Force assertion due to fully spent tx",
833 errType: AssertError(""),
838 for _, test := range tests {
839 // Ensure the expected error type is returned and the code is 0.
840 code, gotBytesRead, err := utxoEntryHeaderCode(test.entry, 0)
841 if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
842 t.Errorf("utxoEntryHeaderCode (%s): expected error "+
843 "type does not match - got %T, want %T",
844 test.name, err, test.errType)
848 t.Errorf("utxoEntryHeaderCode (%s): unexpected code "+
849 "on error - got %d, want 0", test.name, code)
853 // Ensure the expected number of bytes read is returned.
854 if gotBytesRead != test.bytesRead {
855 t.Errorf("utxoEntryHeaderCode (%s): unexpected number "+
856 "of bytes read - got %d, want %d", test.name,
857 gotBytesRead, test.bytesRead)
863 // TestUtxoEntryDeserializeErrors performs negative tests against deserializing
864 // unspent transaction outputs to ensure error paths work as expected.
865 func TestUtxoEntryDeserializeErrors(t *testing.T) {
874 name: "no data after version",
875 serialized: hexToBytes("01"),
876 errType: errDeserialize(""),
879 name: "no data after block height",
880 serialized: hexToBytes("0101"),
881 errType: errDeserialize(""),
884 name: "no data after header code",
885 serialized: hexToBytes("010102"),
886 errType: errDeserialize(""),
889 name: "not enough bytes for unspentness bitmap",
890 serialized: hexToBytes("01017800"),
891 errType: errDeserialize(""),
894 name: "incomplete compressed txout",
895 serialized: hexToBytes("01010232"),
896 errType: errDeserialize(""),
900 for _, test := range tests {
901 // Ensure the expected error type is returned and the returned
903 entry, err := deserializeUtxoEntry(test.serialized)
904 if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
905 t.Errorf("deserializeUtxoEntry (%s): expected error "+
906 "type does not match - got %T, want %T",
907 test.name, err, test.errType)
911 t.Errorf("deserializeUtxoEntry (%s): returned entry "+
912 "is not nil", test.name)
918 // TestBestChainStateSerialization ensures serializing and deserializing the
919 // best chain state works as expected.
920 func TestBestChainStateSerialization(t *testing.T) {
923 workSum := new(big.Int)
931 state: bestChainState{
932 hash: *newHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
935 workSum: func() *big.Int {
936 workSum.Add(workSum, CalcWork(486604799))
937 return new(big.Int).Set(workSum)
940 serialized: hexToBytes("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000100000000000000050000000100010001"),
944 state: bestChainState{
945 hash: *newHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"),
948 workSum: func() *big.Int {
949 workSum.Add(workSum, CalcWork(486604799))
950 return new(big.Int).Set(workSum)
953 serialized: hexToBytes("4860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000010000000200000000000000050000000200020002"),
957 for i, test := range tests {
958 // Ensure the state serializes to the expected value.
959 gotBytes := serializeBestChainState(test.state)
960 if !bytes.Equal(gotBytes, test.serialized) {
961 t.Errorf("serializeBestChainState #%d (%s): mismatched "+
962 "bytes - got %x, want %x", i, test.name,
963 gotBytes, test.serialized)
967 // Ensure the serialized bytes are decoded back to the expected
969 state, err := deserializeBestChainState(test.serialized)
971 t.Errorf("deserializeBestChainState #%d (%s) "+
972 "unexpected error: %v", i, test.name, err)
975 if !reflect.DeepEqual(state, test.state) {
976 t.Errorf("deserializeBestChainState #%d (%s) "+
977 "mismatched state - got %v, want %v", i,
978 test.name, state, test.state)
985 // TestBestChainStateDeserializeErrors performs negative tests against
986 // deserializing the chain state to ensure error paths work as expected.
987 func TestBestChainStateDeserializeErrors(t *testing.T) {
996 name: "nothing serialized",
997 serialized: hexToBytes(""),
998 errType: database.Error{ErrorCode: database.ErrCorruption},
1001 name: "short data in hash",
1002 serialized: hexToBytes("0000"),
1003 errType: database.Error{ErrorCode: database.ErrCorruption},
1006 name: "short data in work sum",
1007 serialized: hexToBytes("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000001000000000000000500000001000100"),
1008 errType: database.Error{ErrorCode: database.ErrCorruption},
1012 for _, test := range tests {
1013 // Ensure the expected error type and code is returned.
1014 _, err := deserializeBestChainState(test.serialized)
1015 if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
1016 t.Errorf("deserializeBestChainState (%s): expected "+
1017 "error type does not match - got %T, want %T",
1018 test.name, err, test.errType)
1021 if derr, ok := err.(database.Error); ok {
1022 tderr := test.errType.(database.Error)
1023 if derr.ErrorCode != tderr.ErrorCode {
1024 t.Errorf("deserializeBestChainState (%s): "+
1025 "wrong error code got: %v, want: %v",
1026 test.name, derr.ErrorCode,