github.com/BlockABC/godash@v0.0.0-20191112120524-f4aa3a32c566/blockchain/chainio_test.go (about)

     1  // Copyright (c) 2015-2016 The btcsuite developers
     2  // Copyright (c) 2016 The Dash 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
     7  
     8  import (
     9  	"bytes"
    10  	"errors"
    11  	"math/big"
    12  	"reflect"
    13  	"testing"
    14  
    15  	"github.com/BlockABC/godash/database"
    16  	"github.com/BlockABC/godash/wire"
    17  )
    18  
    19  // TestErrNotInMainChain ensures the functions related to errNotInMainChain work
    20  // as expected.
    21  func TestErrNotInMainChain(t *testing.T) {
    22  	errStr := "no block at height 1 exists"
    23  	err := error(errNotInMainChain(errStr))
    24  
    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)
    29  	}
    30  
    31  	// Ensure error is detected as the correct type.
    32  	if !isNotInMainChainErr(err) {
    33  		t.Fatalf("isNotInMainChainErr did not detect as expected type")
    34  	}
    35  	err = errors.New("something else")
    36  	if isNotInMainChainErr(err) {
    37  		t.Fatalf("isNotInMainChainErr detected incorrect type")
    38  	}
    39  }
    40  
    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.
    45  	if !o.compressed {
    46  		return
    47  	}
    48  
    49  	o.amount = int64(decompressTxOutAmount(uint64(o.amount)))
    50  	o.pkScript = decompressScript(o.pkScript, version)
    51  	o.compressed = false
    52  }
    53  
    54  // TestStxoSerialization ensures serializing and deserializing spent transaction
    55  // output entries works as expected.
    56  func TestStxoSerialization(t *testing.T) {
    57  	t.Parallel()
    58  
    59  	tests := []struct {
    60  		name       string
    61  		stxo       spentTxOut
    62  		txVersion  int32 // When the txout is not fully spent.
    63  		serialized []byte
    64  	}{
    65  		// From block 170 in main blockchain.
    66  		{
    67  			name: "Spends last output of coinbase",
    68  			stxo: spentTxOut{
    69  				amount:     5000000000,
    70  				pkScript:   hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
    71  				isCoinBase: true,
    72  				height:     9,
    73  				version:    1,
    74  			},
    75  			serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"),
    76  		},
    77  		// Adapted from block 100025 in main blockchain.
    78  		{
    79  			name: "Spends last output of non coinbase",
    80  			stxo: spentTxOut{
    81  				amount:     13761000000,
    82  				pkScript:   hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"),
    83  				isCoinBase: false,
    84  				height:     100024,
    85  				version:    1,
    86  			},
    87  			serialized: hexToBytes("8b99700186c64700b2fb57eadf61e106a100a7445a8c3f67898841ec"),
    88  		},
    89  		// Adapted from block 100025 in main blockchain.
    90  		{
    91  			name: "Does not spend last output",
    92  			stxo: spentTxOut{
    93  				amount:   34405000000,
    94  				pkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"),
    95  				version:  1,
    96  			},
    97  			txVersion:  1,
    98  			serialized: hexToBytes("0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"),
    99  		},
   100  	}
   101  
   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))
   110  			continue
   111  		}
   112  
   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)
   120  			continue
   121  		}
   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))
   127  			continue
   128  		}
   129  
   130  		// Ensure the serialized bytes are decoded back to the expected
   131  		// stxo.
   132  		var gotStxo spentTxOut
   133  		gotBytesRead, err := decodeSpentTxOut(test.serialized, &gotStxo,
   134  			test.txVersion)
   135  		if err != nil {
   136  			t.Errorf("decodeSpentTxOut (%s): unexpected error: %v",
   137  				test.name, err)
   138  			continue
   139  		}
   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)
   144  			continue
   145  		}
   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))
   150  			continue
   151  		}
   152  	}
   153  }
   154  
   155  // TestStxoDecodeErrors performs negative tests against decoding spent
   156  // transaction outputs to ensure error paths work as expected.
   157  func TestStxoDecodeErrors(t *testing.T) {
   158  	t.Parallel()
   159  
   160  	tests := []struct {
   161  		name       string
   162  		stxo       spentTxOut
   163  		txVersion  int32 // When the txout is not fully spent.
   164  		serialized []byte
   165  		bytesRead  int // Expected number of bytes read.
   166  		errType    error
   167  	}{
   168  		{
   169  			name:       "nothing serialized",
   170  			stxo:       spentTxOut{},
   171  			serialized: hexToBytes(""),
   172  			errType:    errDeserialize(""),
   173  			bytesRead:  0,
   174  		},
   175  		{
   176  			name:       "no data after header code w/o version",
   177  			stxo:       spentTxOut{},
   178  			serialized: hexToBytes("00"),
   179  			errType:    errDeserialize(""),
   180  			bytesRead:  1,
   181  		},
   182  		{
   183  			name:       "no data after header code with version",
   184  			stxo:       spentTxOut{},
   185  			serialized: hexToBytes("13"),
   186  			errType:    errDeserialize(""),
   187  			bytesRead:  1,
   188  		},
   189  		{
   190  			name:       "no data after version",
   191  			stxo:       spentTxOut{},
   192  			serialized: hexToBytes("1301"),
   193  			errType:    errDeserialize(""),
   194  			bytesRead:  2,
   195  		},
   196  		{
   197  			name:       "no serialized tx version and passed 0",
   198  			stxo:       spentTxOut{},
   199  			serialized: hexToBytes("003205"),
   200  			errType:    AssertError(""),
   201  			bytesRead:  1,
   202  		},
   203  		{
   204  			name:       "incomplete compressed txout",
   205  			stxo:       spentTxOut{},
   206  			txVersion:  1,
   207  			serialized: hexToBytes("0032"),
   208  			errType:    errDeserialize(""),
   209  			bytesRead:  2,
   210  		},
   211  	}
   212  
   213  	for _, test := range tests {
   214  		// Ensure the expected error type is returned.
   215  		gotBytesRead, err := decodeSpentTxOut(test.serialized,
   216  			&test.stxo, test.txVersion)
   217  		if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
   218  			t.Errorf("decodeSpentTxOut (%s): expected error type "+
   219  				"does not match - got %T, want %T", test.name,
   220  				err, test.errType)
   221  			continue
   222  		}
   223  
   224  		// Ensure the expected number of bytes read is returned.
   225  		if gotBytesRead != test.bytesRead {
   226  			t.Errorf("decodeSpentTxOut (%s): unexpected number of "+
   227  				"bytes read - got %d, want %d", test.name,
   228  				gotBytesRead, test.bytesRead)
   229  			continue
   230  		}
   231  	}
   232  }
   233  
   234  // TestSpendJournalSerialization ensures serializing and deserializing spend
   235  // journal entries works as expected.
   236  func TestSpendJournalSerialization(t *testing.T) {
   237  	t.Parallel()
   238  
   239  	tests := []struct {
   240  		name       string
   241  		entry      []spentTxOut
   242  		blockTxns  []*wire.MsgTx
   243  		utxoView   *UtxoViewpoint
   244  		serialized []byte
   245  	}{
   246  		// From block 2 in main blockchain.
   247  		{
   248  			name:       "No spends",
   249  			entry:      nil,
   250  			blockTxns:  nil,
   251  			utxoView:   NewUtxoViewpoint(),
   252  			serialized: nil,
   253  		},
   254  		// From block 170 in main blockchain.
   255  		{
   256  			name: "One tx with one input spends last output of coinbase",
   257  			entry: []spentTxOut{{
   258  				amount:     5000000000,
   259  				pkScript:   hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
   260  				isCoinBase: true,
   261  				height:     9,
   262  				version:    1,
   263  			}},
   264  			blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
   265  				Version: 1,
   266  				TxIn: []*wire.TxIn{{
   267  					PreviousOutPoint: wire.OutPoint{
   268  						Hash:  *newShaHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
   269  						Index: 0,
   270  					},
   271  					SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
   272  					Sequence:        0xffffffff,
   273  				}},
   274  				TxOut: []*wire.TxOut{{
   275  					Value:    1000000000,
   276  					PkScript: hexToBytes("4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac"),
   277  				}, {
   278  					Value:    4000000000,
   279  					PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
   280  				}},
   281  				LockTime: 0,
   282  			}},
   283  			utxoView:   NewUtxoViewpoint(),
   284  			serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"),
   285  		},
   286  		// Adapted from block 100025 in main blockchain.
   287  		{
   288  			name: "Two txns when one spends last output, one doesn't",
   289  			entry: []spentTxOut{{
   290  				amount:   34405000000,
   291  				pkScript: hexToBytes("76a9146edbc6c4d31bae9f1ccc38538a114bf42de65e8688ac"),
   292  				version:  1,
   293  			}, {
   294  				amount:     13761000000,
   295  				pkScript:   hexToBytes("76a914b2fb57eadf61e106a100a7445a8c3f67898841ec88ac"),
   296  				isCoinBase: false,
   297  				height:     100024,
   298  				version:    1,
   299  			}},
   300  			blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
   301  				Version: 1,
   302  				TxIn: []*wire.TxIn{{
   303  					PreviousOutPoint: wire.OutPoint{
   304  						Hash:  *newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"),
   305  						Index: 1,
   306  					},
   307  					SignatureScript: hexToBytes("493046022100c167eead9840da4a033c9a56470d7794a9bb1605b377ebe5688499b39f94be59022100fb6345cab4324f9ea0b9ee9169337534834638d818129778370f7d378ee4a325014104d962cac5390f12ddb7539507065d0def320d68c040f2e73337c3a1aaaab7195cb5c4d02e0959624d534f3c10c3cf3d73ca5065ebd62ae986b04c6d090d32627c"),
   308  					Sequence:        0xffffffff,
   309  				}},
   310  				TxOut: []*wire.TxOut{{
   311  					Value:    5000000,
   312  					PkScript: hexToBytes("76a914f419b8db4ba65f3b6fcc233acb762ca6f51c23d488ac"),
   313  				}, {
   314  					Value:    34400000000,
   315  					PkScript: hexToBytes("76a914cadf4fc336ab3c6a4610b75f31ba0676b7f663d288ac"),
   316  				}},
   317  				LockTime: 0,
   318  			}, {
   319  				Version: 1,
   320  				TxIn: []*wire.TxIn{{
   321  					PreviousOutPoint: wire.OutPoint{
   322  						Hash:  *newShaHashFromStr("92fbe1d4be82f765dfabc9559d4620864b05cc897c4db0e29adac92d294e52b7"),
   323  						Index: 0,
   324  					},
   325  					SignatureScript: hexToBytes("483045022100e256743154c097465cf13e89955e1c9ff2e55c46051b627751dee0144183157e02201d8d4f02cde8496aae66768f94d35ce54465bd4ae8836004992d3216a93a13f00141049d23ce8686fe9b802a7a938e8952174d35dd2c2089d4112001ed8089023ab4f93a3c9fcd5bfeaa9727858bf640dc1b1c05ec3b434bb59837f8640e8810e87742"),
   326  					Sequence:        0xffffffff,
   327  				}},
   328  				TxOut: []*wire.TxOut{{
   329  					Value:    5000000,
   330  					PkScript: hexToBytes("76a914a983ad7c92c38fc0e2025212e9f972204c6e687088ac"),
   331  				}, {
   332  					Value:    13756000000,
   333  					PkScript: hexToBytes("76a914a6ebd69952ab486a7a300bfffdcb395dc7d47c2388ac"),
   334  				}},
   335  				LockTime: 0,
   336  			}},
   337  			utxoView: &UtxoViewpoint{entries: map[wire.ShaHash]*UtxoEntry{
   338  				*newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"): {
   339  					version:     1,
   340  					isCoinBase:  false,
   341  					blockHeight: 100024,
   342  					sparseOutputs: map[uint32]*utxoOutput{
   343  						1: {
   344  							amount:   34405000000,
   345  							pkScript: hexToBytes("76a9142084541c3931677527a7eafe56fd90207c344eb088ac"),
   346  						},
   347  					},
   348  				},
   349  			}},
   350  			serialized: hexToBytes("8b99700186c64700b2fb57eadf61e106a100a7445a8c3f67898841ec0091f20f006edbc6c4d31bae9f1ccc38538a114bf42de65e86"),
   351  		},
   352  		// Hand crafted.
   353  		{
   354  			name: "One tx, two inputs from same tx, neither spend last output",
   355  			entry: []spentTxOut{{
   356  				amount:   165125632,
   357  				pkScript: hexToBytes("51"),
   358  				version:  1,
   359  			}, {
   360  				amount:   154370000,
   361  				pkScript: hexToBytes("51"),
   362  				version:  1,
   363  			}},
   364  			blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
   365  				Version: 1,
   366  				TxIn: []*wire.TxIn{{
   367  					PreviousOutPoint: wire.OutPoint{
   368  						Hash:  *newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"),
   369  						Index: 1,
   370  					},
   371  					SignatureScript: hexToBytes(""),
   372  					Sequence:        0xffffffff,
   373  				}, {
   374  					PreviousOutPoint: wire.OutPoint{
   375  						Hash:  *newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"),
   376  						Index: 2,
   377  					},
   378  					SignatureScript: hexToBytes(""),
   379  					Sequence:        0xffffffff,
   380  				}},
   381  				TxOut: []*wire.TxOut{{
   382  					Value:    165125632,
   383  					PkScript: hexToBytes("51"),
   384  				}, {
   385  					Value:    154370000,
   386  					PkScript: hexToBytes("51"),
   387  				}},
   388  				LockTime: 0,
   389  			}},
   390  			utxoView: &UtxoViewpoint{entries: map[wire.ShaHash]*UtxoEntry{
   391  				*newShaHashFromStr("c0ed017828e59ad5ed3cf70ee7c6fb0f426433047462477dc7a5d470f987a537"): {
   392  					version:     1,
   393  					isCoinBase:  false,
   394  					blockHeight: 100000,
   395  					sparseOutputs: map[uint32]*utxoOutput{
   396  						0: {
   397  							amount:   165712179,
   398  							pkScript: hexToBytes("51"),
   399  						},
   400  					},
   401  				},
   402  			}},
   403  			serialized: hexToBytes("0087bc3707510084c3d19a790751"),
   404  		},
   405  	}
   406  
   407  	for i, test := range tests {
   408  		// Ensure the journal entry serializes to the expected value.
   409  		gotBytes := serializeSpendJournalEntry(test.entry)
   410  		if !bytes.Equal(gotBytes, test.serialized) {
   411  			t.Errorf("serializeSpendJournalEntry #%d (%s): "+
   412  				"mismatched bytes - got %x, want %x", i,
   413  				test.name, gotBytes, test.serialized)
   414  			continue
   415  		}
   416  
   417  		// Deserialize to a spend journal entry.
   418  		gotEntry, err := deserializeSpendJournalEntry(test.serialized,
   419  			test.blockTxns, test.utxoView)
   420  		if err != nil {
   421  			t.Errorf("deserializeSpendJournalEntry #%d (%s) "+
   422  				"unexpected error: %v", i, test.name, err)
   423  			continue
   424  		}
   425  		for stxoIdx := range gotEntry {
   426  			stxo := &gotEntry[stxoIdx]
   427  			stxo.maybeDecompress(test.entry[stxoIdx].version)
   428  		}
   429  
   430  		// Ensure that the deserialized spend journal entry has the
   431  		// correct properties.
   432  		if !reflect.DeepEqual(gotEntry, test.entry) {
   433  			t.Errorf("deserializeSpendJournalEntry #%d (%s) "+
   434  				"mismatched entries - got %v, want %v",
   435  				i, test.name, gotEntry, test.entry)
   436  			continue
   437  		}
   438  	}
   439  }
   440  
   441  // TestSpendJournalErrors performs negative tests against deserializing spend
   442  // journal entries to ensure error paths work as expected.
   443  func TestSpendJournalErrors(t *testing.T) {
   444  	t.Parallel()
   445  
   446  	tests := []struct {
   447  		name       string
   448  		blockTxns  []*wire.MsgTx
   449  		utxoView   *UtxoViewpoint
   450  		serialized []byte
   451  		errType    error
   452  	}{
   453  		// Adapted from block 170 in main blockchain.
   454  		{
   455  			name: "Force assertion due to missing stxos",
   456  			blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
   457  				Version: 1,
   458  				TxIn: []*wire.TxIn{{
   459  					PreviousOutPoint: wire.OutPoint{
   460  						Hash:  *newShaHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
   461  						Index: 0,
   462  					},
   463  					SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
   464  					Sequence:        0xffffffff,
   465  				}},
   466  				LockTime: 0,
   467  			}},
   468  			utxoView:   NewUtxoViewpoint(),
   469  			serialized: hexToBytes(""),
   470  			errType:    AssertError(""),
   471  		},
   472  		{
   473  			name: "Force deserialization error in stxos",
   474  			blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
   475  				Version: 1,
   476  				TxIn: []*wire.TxIn{{
   477  					PreviousOutPoint: wire.OutPoint{
   478  						Hash:  *newShaHashFromStr("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"),
   479  						Index: 0,
   480  					},
   481  					SignatureScript: hexToBytes("47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"),
   482  					Sequence:        0xffffffff,
   483  				}},
   484  				LockTime: 0,
   485  			}},
   486  			utxoView:   NewUtxoViewpoint(),
   487  			serialized: hexToBytes("1301320511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a"),
   488  			errType:    errDeserialize(""),
   489  		},
   490  	}
   491  
   492  	for _, test := range tests {
   493  		// Ensure the expected error type is returned and the returned
   494  		// slice is nil.
   495  		stxos, err := deserializeSpendJournalEntry(test.serialized,
   496  			test.blockTxns, test.utxoView)
   497  		if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
   498  			t.Errorf("deserializeSpendJournalEntry (%s): expected "+
   499  				"error type does not match - got %T, want %T",
   500  				test.name, err, test.errType)
   501  			continue
   502  		}
   503  		if stxos != nil {
   504  			t.Errorf("deserializeSpendJournalEntry (%s): returned "+
   505  				"slice of spent transaction outputs is not nil",
   506  				test.name)
   507  			continue
   508  		}
   509  	}
   510  }
   511  
   512  // TestUtxoSerialization ensures serializing and deserializing unspent
   513  // trasaction output entries works as expected.
   514  func TestUtxoSerialization(t *testing.T) {
   515  	t.Parallel()
   516  
   517  	tests := []struct {
   518  		name       string
   519  		entry      *UtxoEntry
   520  		serialized []byte
   521  	}{
   522  		// From tx in main blockchain:
   523  		// 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098
   524  		{
   525  			name: "Only output 0, coinbase",
   526  			entry: &UtxoEntry{
   527  				version:     1,
   528  				isCoinBase:  true,
   529  				blockHeight: 1,
   530  				sparseOutputs: map[uint32]*utxoOutput{
   531  					0: {
   532  						amount:   5000000000,
   533  						pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
   534  					},
   535  				},
   536  			},
   537  			serialized: hexToBytes("010103320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"),
   538  		},
   539  		// From tx in main blockchain:
   540  		// 8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb
   541  		{
   542  			name: "Only output 1, not coinbase",
   543  			entry: &UtxoEntry{
   544  				version:     1,
   545  				isCoinBase:  false,
   546  				blockHeight: 100001,
   547  				sparseOutputs: map[uint32]*utxoOutput{
   548  					1: {
   549  						amount:   1000000,
   550  						pkScript: hexToBytes("76a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"),
   551  					},
   552  				},
   553  			},
   554  			serialized: hexToBytes("01858c21040700ee8bd501094a7d5ca318da2506de35e1cb025ddc"),
   555  		},
   556  		// Adapted from tx in main blockchain:
   557  		// df3f3f442d9699857f7f49de4ff0b5d0f3448bec31cdc7b5bf6d25f2abd637d5
   558  		{
   559  			name: "Only output 2, coinbase",
   560  			entry: &UtxoEntry{
   561  				version:     1,
   562  				isCoinBase:  true,
   563  				blockHeight: 99004,
   564  				sparseOutputs: map[uint32]*utxoOutput{
   565  					2: {
   566  						amount:   100937281,
   567  						pkScript: hexToBytes("76a914da33f77cee27c2a975ed5124d7e4f7f97513510188ac"),
   568  					},
   569  				},
   570  			},
   571  			serialized: hexToBytes("0185843c010182b095bf4100da33f77cee27c2a975ed5124d7e4f7f975135101"),
   572  		},
   573  		// Adapted from tx in main blockchain:
   574  		// 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
   575  		{
   576  			name: "outputs 0 and 2 not coinbase",
   577  			entry: &UtxoEntry{
   578  				version:     1,
   579  				isCoinBase:  false,
   580  				blockHeight: 113931,
   581  				sparseOutputs: map[uint32]*utxoOutput{
   582  					0: {
   583  						amount:   20000000,
   584  						pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"),
   585  					},
   586  					2: {
   587  						amount:   15000000,
   588  						pkScript: hexToBytes("76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac"),
   589  					},
   590  				},
   591  			},
   592  			serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
   593  		},
   594  		// Adapted from tx in main blockchain:
   595  		// 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
   596  		{
   597  			name: "outputs 0 and 2, not coinbase, 1 marked spent",
   598  			entry: &UtxoEntry{
   599  				version:     1,
   600  				isCoinBase:  false,
   601  				blockHeight: 113931,
   602  				sparseOutputs: map[uint32]*utxoOutput{
   603  					0: {
   604  						amount:   20000000,
   605  						pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"),
   606  					},
   607  					1: { // This won't be serialized.
   608  						spent:    true,
   609  						amount:   1000000,
   610  						pkScript: hexToBytes("76a914e43031c3e46f20bf1ccee9553ce815de5a48467588ac"),
   611  					},
   612  					2: {
   613  						amount:   15000000,
   614  						pkScript: hexToBytes("76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac"),
   615  					},
   616  				},
   617  			},
   618  			serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
   619  		},
   620  		// Adapted from tx in main blockchain:
   621  		// 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
   622  		{
   623  			name: "outputs 0 and 2, not coinbase, output 2 compressed",
   624  			entry: &UtxoEntry{
   625  				version:     1,
   626  				isCoinBase:  false,
   627  				blockHeight: 113931,
   628  				sparseOutputs: map[uint32]*utxoOutput{
   629  					0: {
   630  						amount:   20000000,
   631  						pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"),
   632  					},
   633  					2: {
   634  						// Uncompressed Amount: 15000000
   635  						// Uncompressed PkScript: 76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac
   636  						compressed: true,
   637  						amount:     137,
   638  						pkScript:   hexToBytes("00b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
   639  					},
   640  				},
   641  			},
   642  			serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
   643  		},
   644  		// Adapted from tx in main blockchain:
   645  		// 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
   646  		{
   647  			name: "outputs 0 and 2, not coinbase, output 2 compressed, packed indexes reversed",
   648  			entry: &UtxoEntry{
   649  				version:     1,
   650  				isCoinBase:  false,
   651  				blockHeight: 113931,
   652  				sparseOutputs: map[uint32]*utxoOutput{
   653  					0: {
   654  						amount:   20000000,
   655  						pkScript: hexToBytes("76a914e2ccd6ec7c6e2e581349c77e067385fa8236bf8a88ac"),
   656  					},
   657  					2: {
   658  						// Uncompressed Amount: 15000000
   659  						// Uncompressed PkScript: 76a914b8025be1b3efc63b0ad48e7f9f10e87544528d5888ac
   660  						compressed: true,
   661  						amount:     137,
   662  						pkScript:   hexToBytes("00b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
   663  					},
   664  				},
   665  			},
   666  			serialized: hexToBytes("0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58"),
   667  		},
   668  		// From tx in main blockchain:
   669  		// 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098
   670  		{
   671  			name: "Only output 0, coinbase, fully spent",
   672  			entry: &UtxoEntry{
   673  				version:     1,
   674  				isCoinBase:  true,
   675  				blockHeight: 1,
   676  				sparseOutputs: map[uint32]*utxoOutput{
   677  					0: {
   678  						spent:    true,
   679  						amount:   5000000000,
   680  						pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
   681  					},
   682  				},
   683  			},
   684  			serialized: nil,
   685  		},
   686  		// Adapted from tx in main blockchain:
   687  		// 1b02d1c8cfef60a189017b9a420c682cf4a0028175f2f563209e4ff61c8c3620
   688  		{
   689  			name: "Only output 22, not coinbase",
   690  			entry: &UtxoEntry{
   691  				version:     1,
   692  				isCoinBase:  false,
   693  				blockHeight: 338156,
   694  				sparseOutputs: map[uint32]*utxoOutput{
   695  					22: {
   696  						spent:    false,
   697  						amount:   366875659,
   698  						pkScript: hexToBytes("a9141dd46a006572d820e448e12d2bbb38640bc718e687"),
   699  					},
   700  				},
   701  			},
   702  			serialized: hexToBytes("0193d06c100000108ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6"),
   703  		},
   704  	}
   705  
   706  	for i, test := range tests {
   707  		// Ensure the utxo entry serializes to the expected value.
   708  		gotBytes, err := serializeUtxoEntry(test.entry)
   709  		if err != nil {
   710  			t.Errorf("serializeUtxoEntry #%d (%s) unexpected "+
   711  				"error: %v", i, test.name, err)
   712  			continue
   713  		}
   714  		if !bytes.Equal(gotBytes, test.serialized) {
   715  			t.Errorf("serializeUtxoEntry #%d (%s): mismatched "+
   716  				"bytes - got %x, want %x", i, test.name,
   717  				gotBytes, test.serialized)
   718  			continue
   719  		}
   720  
   721  		// Don't try to deserialize if the test entry was fully spent
   722  		// since it will have a nil serialization.
   723  		if test.entry.IsFullySpent() {
   724  			continue
   725  		}
   726  
   727  		// Deserialize to a utxo entry.
   728  		utxoEntry, err := deserializeUtxoEntry(test.serialized)
   729  		if err != nil {
   730  			t.Errorf("deserializeUtxoEntry #%d (%s) unexpected "+
   731  				"error: %v", i, test.name, err)
   732  			continue
   733  		}
   734  
   735  		// Ensure that the deserialized utxo entry has the same
   736  		// properties for the containing transaction and block height.
   737  		if utxoEntry.Version() != test.entry.Version() {
   738  			t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
   739  				"version: got %d, want %d", i, test.name,
   740  				utxoEntry.Version(), test.entry.Version())
   741  			continue
   742  		}
   743  		if utxoEntry.IsCoinBase() != test.entry.IsCoinBase() {
   744  			t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
   745  				"coinbase flag: got %v, want %v", i, test.name,
   746  				utxoEntry.IsCoinBase(), test.entry.IsCoinBase())
   747  			continue
   748  		}
   749  		if utxoEntry.BlockHeight() != test.entry.BlockHeight() {
   750  			t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
   751  				"block height: got %d, want %d", i, test.name,
   752  				utxoEntry.BlockHeight(),
   753  				test.entry.BlockHeight())
   754  			continue
   755  		}
   756  		if utxoEntry.IsFullySpent() != test.entry.IsFullySpent() {
   757  			t.Errorf("deserializeUtxoEntry #%d (%s) mismatched "+
   758  				"fully spent: got %v, want %v", i, test.name,
   759  				utxoEntry.IsFullySpent(),
   760  				test.entry.IsFullySpent())
   761  			continue
   762  		}
   763  
   764  		// Ensure all of the outputs in the test entry match the
   765  		// spentness of the output in the deserialized entry and the
   766  		// deserialized entry does not contain any additional utxos.
   767  		var numUnspent int
   768  		for outputIndex := range test.entry.sparseOutputs {
   769  			gotSpent := utxoEntry.IsOutputSpent(outputIndex)
   770  			wantSpent := test.entry.IsOutputSpent(outputIndex)
   771  			if !wantSpent {
   772  				numUnspent++
   773  			}
   774  			if gotSpent != wantSpent {
   775  				t.Errorf("deserializeUtxoEntry #%d (%s) output "+
   776  					"#%d: mismatched spent: got %v, want "+
   777  					"%v", i, test.name, outputIndex,
   778  					gotSpent, wantSpent)
   779  				continue
   780  
   781  			}
   782  		}
   783  		if len(utxoEntry.sparseOutputs) != numUnspent {
   784  			t.Errorf("deserializeUtxoEntry #%d (%s): mismatched "+
   785  				"number of unspent outputs: got %d, want %d", i,
   786  				test.name, len(utxoEntry.sparseOutputs),
   787  				numUnspent)
   788  			continue
   789  		}
   790  
   791  		// Ensure all of the amounts and scripts of the utxos in the
   792  		// deserialized entry match the ones in the test entry.
   793  		for outputIndex := range utxoEntry.sparseOutputs {
   794  			gotAmount := utxoEntry.AmountByIndex(outputIndex)
   795  			wantAmount := test.entry.AmountByIndex(outputIndex)
   796  			if gotAmount != wantAmount {
   797  				t.Errorf("deserializeUtxoEntry #%d (%s) "+
   798  					"output #%d: mismatched amounts: got "+
   799  					"%d, want %d", i, test.name,
   800  					outputIndex, gotAmount, wantAmount)
   801  				continue
   802  			}
   803  
   804  			gotPkScript := utxoEntry.PkScriptByIndex(outputIndex)
   805  			wantPkScript := test.entry.PkScriptByIndex(outputIndex)
   806  			if !bytes.Equal(gotPkScript, wantPkScript) {
   807  				t.Errorf("deserializeUtxoEntry #%d (%s) "+
   808  					"output #%d mismatched scripts: got "+
   809  					"%x, want %x", i, test.name,
   810  					outputIndex, gotPkScript, wantPkScript)
   811  				continue
   812  			}
   813  		}
   814  	}
   815  }
   816  
   817  // TestUtxoEntryHeaderCodeErrors performs negative tests against unspent
   818  // transaction output header codes to ensure error paths work as expected.
   819  func TestUtxoEntryHeaderCodeErrors(t *testing.T) {
   820  	t.Parallel()
   821  
   822  	tests := []struct {
   823  		name      string
   824  		entry     *UtxoEntry
   825  		code      uint64
   826  		bytesRead int // Expected number of bytes read.
   827  		errType   error
   828  	}{
   829  		{
   830  			name:      "Force assertion due to fully spent tx",
   831  			entry:     &UtxoEntry{},
   832  			errType:   AssertError(""),
   833  			bytesRead: 0,
   834  		},
   835  	}
   836  
   837  	for _, test := range tests {
   838  		// Ensure the expected error type is returned and the code is 0.
   839  		code, gotBytesRead, err := utxoEntryHeaderCode(test.entry, 0)
   840  		if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
   841  			t.Errorf("utxoEntryHeaderCode (%s): expected error "+
   842  				"type does not match - got %T, want %T",
   843  				test.name, err, test.errType)
   844  			continue
   845  		}
   846  		if code != 0 {
   847  			t.Errorf("utxoEntryHeaderCode (%s): unexpected code "+
   848  				"on error - got %d, want 0", test.name, code)
   849  			continue
   850  		}
   851  
   852  		// Ensure the expected number of bytes read is returned.
   853  		if gotBytesRead != test.bytesRead {
   854  			t.Errorf("utxoEntryHeaderCode (%s): unexpected number "+
   855  				"of bytes read - got %d, want %d", test.name,
   856  				gotBytesRead, test.bytesRead)
   857  			continue
   858  		}
   859  	}
   860  }
   861  
   862  // TestUtxoEntryDeserializeErrors performs negative tests against deserializing
   863  // unspent transaction outputs to ensure error paths work as expected.
   864  func TestUtxoEntryDeserializeErrors(t *testing.T) {
   865  	t.Parallel()
   866  
   867  	tests := []struct {
   868  		name       string
   869  		serialized []byte
   870  		errType    error
   871  	}{
   872  		{
   873  			name:       "no data after version",
   874  			serialized: hexToBytes("01"),
   875  			errType:    errDeserialize(""),
   876  		},
   877  		{
   878  			name:       "no data after block height",
   879  			serialized: hexToBytes("0101"),
   880  			errType:    errDeserialize(""),
   881  		},
   882  		{
   883  			name:       "no data after header code",
   884  			serialized: hexToBytes("010102"),
   885  			errType:    errDeserialize(""),
   886  		},
   887  		{
   888  			name:       "not enough bytes for unspentness bitmap",
   889  			serialized: hexToBytes("01017800"),
   890  			errType:    errDeserialize(""),
   891  		},
   892  		{
   893  			name:       "incomplete compressed txout",
   894  			serialized: hexToBytes("01010232"),
   895  			errType:    errDeserialize(""),
   896  		},
   897  	}
   898  
   899  	for _, test := range tests {
   900  		// Ensure the expected error type is returned and the returned
   901  		// entry is nil.
   902  		entry, err := deserializeUtxoEntry(test.serialized)
   903  		if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
   904  			t.Errorf("deserializeUtxoEntry (%s): expected error "+
   905  				"type does not match - got %T, want %T",
   906  				test.name, err, test.errType)
   907  			continue
   908  		}
   909  		if entry != nil {
   910  			t.Errorf("deserializeUtxoEntry (%s): returned entry "+
   911  				"is not nil", test.name)
   912  			continue
   913  		}
   914  	}
   915  }
   916  
   917  // TestBestChainStateSerialization ensures serializing and deserializing the
   918  // best chain state works as expected.
   919  func TestBestChainStateSerialization(t *testing.T) {
   920  	t.Parallel()
   921  
   922  	workSum := new(big.Int)
   923  	tests := []struct {
   924  		name       string
   925  		state      bestChainState
   926  		serialized []byte
   927  	}{
   928  		{
   929  			name: "genesis",
   930  			state: bestChainState{
   931  				hash:      *newShaHashFromStr("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
   932  				height:    0,
   933  				totalTxns: 1,
   934  				workSum: func() *big.Int {
   935  					workSum.Add(workSum, CalcWork(486604799))
   936  					return new(big.Int).Set(workSum)
   937  				}(), // 0x0100010001
   938  			},
   939  			serialized: hexToBytes("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000000000000100000000000000050000000100010001"),
   940  		},
   941  		{
   942  			name: "block 1",
   943  			state: bestChainState{
   944  				hash:      *newShaHashFromStr("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"),
   945  				height:    1,
   946  				totalTxns: 2,
   947  				workSum: func() *big.Int {
   948  					workSum.Add(workSum, CalcWork(486604799))
   949  					return new(big.Int).Set(workSum)
   950  				}(), // 0x0200020002
   951  			},
   952  			serialized: hexToBytes("4860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000010000000200000000000000050000000200020002"),
   953  		},
   954  	}
   955  
   956  	for i, test := range tests {
   957  		// Ensure the state serializes to the expected value.
   958  		gotBytes := serializeBestChainState(test.state)
   959  		if !bytes.Equal(gotBytes, test.serialized) {
   960  			t.Errorf("serializeBestChainState #%d (%s): mismatched "+
   961  				"bytes - got %x, want %x", i, test.name,
   962  				gotBytes, test.serialized)
   963  			continue
   964  		}
   965  
   966  		// Ensure the serialized bytes are decoded back to the expected
   967  		// state.
   968  		state, err := deserializeBestChainState(test.serialized)
   969  		if err != nil {
   970  			t.Errorf("deserializeBestChainState #%d (%s) "+
   971  				"unexpected error: %v", i, test.name, err)
   972  			continue
   973  		}
   974  		if !reflect.DeepEqual(state, test.state) {
   975  			t.Errorf("deserializeBestChainState #%d (%s) "+
   976  				"mismatched state - got %v, want %v", i,
   977  				test.name, state, test.state)
   978  			continue
   979  
   980  		}
   981  	}
   982  }
   983  
   984  // TestBestChainStateDeserializeErrors performs negative tests against
   985  // deserializing the chain state to ensure error paths work as expected.
   986  func TestBestChainStateDeserializeErrors(t *testing.T) {
   987  	t.Parallel()
   988  
   989  	tests := []struct {
   990  		name       string
   991  		serialized []byte
   992  		errType    error
   993  	}{
   994  		{
   995  			name:       "nothing serialized",
   996  			serialized: hexToBytes(""),
   997  			errType:    database.Error{ErrorCode: database.ErrCorruption},
   998  		},
   999  		{
  1000  			name:       "short data in hash",
  1001  			serialized: hexToBytes("0000"),
  1002  			errType:    database.Error{ErrorCode: database.ErrCorruption},
  1003  		},
  1004  		{
  1005  			name:       "short data in work sum",
  1006  			serialized: hexToBytes("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000001000000000000000500000001000100"),
  1007  			errType:    database.Error{ErrorCode: database.ErrCorruption},
  1008  		},
  1009  	}
  1010  
  1011  	for _, test := range tests {
  1012  		// Ensure the expected error type and code is returned.
  1013  		_, err := deserializeBestChainState(test.serialized)
  1014  		if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
  1015  			t.Errorf("deserializeBestChainState (%s): expected "+
  1016  				"error type does not match - got %T, want %T",
  1017  				test.name, err, test.errType)
  1018  			continue
  1019  		}
  1020  		if derr, ok := err.(database.Error); ok {
  1021  			tderr := test.errType.(database.Error)
  1022  			if derr.ErrorCode != tderr.ErrorCode {
  1023  				t.Errorf("deserializeBestChainState (%s): "+
  1024  					"wrong  error code got: %v, want: %v",
  1025  					test.name, derr.ErrorCode,
  1026  					tderr.ErrorCode)
  1027  				continue
  1028  			}
  1029  		}
  1030  	}
  1031  }