github.com/lbryio/lbcd@v0.22.119/blockchain/chainio_test.go (about)

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