decred.org/dcrwallet/v3@v3.1.0/wallet/udb/txquery_test.go (about)

     1  // Copyright (c) 2015 The btcsuite developers
     2  // Copyright (c) 2015-2016 The Decred developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  //go:build ignore
     7  
     8  package udb
     9  
    10  import (
    11  	"bytes"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/decred/dcrd/chaincfg/chainhash"
    18  	"github.com/decred/dcrd/dcrutil/v4"
    19  	"github.com/decred/dcrd/wire"
    20  )
    21  
    22  type queryState struct {
    23  	// slice items are ordered by height, mempool comes last.
    24  	blocks    [][]TxDetails
    25  	txDetails map[chainhash.Hash][]TxDetails
    26  }
    27  
    28  func newQueryState() *queryState {
    29  	return &queryState{
    30  		txDetails: make(map[chainhash.Hash][]TxDetails),
    31  	}
    32  }
    33  
    34  func (q *queryState) deepCopy() *queryState {
    35  	cpy := newQueryState()
    36  	for _, blockDetails := range q.blocks {
    37  		var cpyDetails []TxDetails
    38  		for _, detail := range blockDetails {
    39  			cpyDetails = append(cpyDetails, *deepCopyTxDetails(&detail))
    40  		}
    41  		cpy.blocks = append(cpy.blocks, cpyDetails)
    42  	}
    43  	cpy.txDetails = make(map[chainhash.Hash][]TxDetails)
    44  	for txHash, details := range q.txDetails {
    45  		detailsSlice := make([]TxDetails, len(details))
    46  		for i, detail := range details {
    47  			detailsSlice[i] = *deepCopyTxDetails(&detail)
    48  		}
    49  		cpy.txDetails[txHash] = detailsSlice
    50  	}
    51  	return cpy
    52  }
    53  
    54  func deepCopyTxDetails(d *TxDetails) *TxDetails {
    55  	cpy := *d
    56  	cpy.MsgTx = *d.MsgTx.Copy()
    57  	if cpy.SerializedTx != nil {
    58  		cpy.SerializedTx = make([]byte, len(cpy.SerializedTx))
    59  		copy(cpy.SerializedTx, d.SerializedTx)
    60  	}
    61  	cpy.Credits = make([]CreditRecord, len(d.Credits))
    62  	copy(cpy.Credits, d.Credits)
    63  	cpy.Debits = make([]DebitRecord, len(d.Debits))
    64  	copy(cpy.Debits, d.Debits)
    65  	return &cpy
    66  }
    67  
    68  func (q *queryState) compare(t *testing.T, s *Store, changeDesc string) {
    69  	defer func() {
    70  		if t.Failed() {
    71  			t.Fatalf("Store state queries failed after '%s'", changeDesc)
    72  		}
    73  	}()
    74  
    75  	fwdBlocks := q.blocks
    76  	revBlocks := make([][]TxDetails, len(q.blocks))
    77  	copy(revBlocks, q.blocks)
    78  	for i := 0; i < len(revBlocks)/2; i++ {
    79  		revBlocks[i], revBlocks[len(revBlocks)-1-i] = revBlocks[len(revBlocks)-1-i], revBlocks[i]
    80  	}
    81  	checkBlock := func(blocks [][]TxDetails) func([]TxDetails) (bool, error) {
    82  		return func(got []TxDetails) (bool, error) {
    83  			if len(fwdBlocks) == 0 {
    84  				return false, fmt.Errorf("entered range when no more details expected")
    85  			}
    86  			exp := blocks[0]
    87  			if len(got) != len(exp) {
    88  				return false, fmt.Errorf("got len(details)=%d in transaction range, expected %d", len(got), len(exp))
    89  			}
    90  			for i := range got {
    91  				equalTxDetails(t, &got[i], &exp[i])
    92  			}
    93  			if t.Failed() {
    94  				return false, fmt.Errorf("Failed comparing range of transaction details")
    95  			}
    96  			blocks = blocks[1:]
    97  			return false, nil
    98  		}
    99  	}
   100  	err := s.RangeTransactions(0, -1, checkBlock(fwdBlocks))
   101  	if err != nil {
   102  		t.Fatalf("Failed in RangeTransactions (forwards iteration): %v", err)
   103  	}
   104  	err = s.RangeTransactions(-1, 0, checkBlock(revBlocks))
   105  	if err != nil {
   106  		t.Fatalf("Failed in RangeTransactions (reverse iteration): %v", err)
   107  	}
   108  
   109  	for txHash, details := range q.txDetails {
   110  		for _, detail := range details {
   111  			blk := &detail.Block.Block
   112  			if blk.Height == -1 {
   113  				blk = nil
   114  			}
   115  			d, err := s.UniqueTxDetails(&txHash, blk)
   116  			if err != nil {
   117  				t.Fatal(err)
   118  			}
   119  			if d == nil {
   120  				t.Errorf("Found no matching transaction at height %d", detail.Block.Height)
   121  				continue
   122  			}
   123  			equalTxDetails(t, d, &detail)
   124  		}
   125  		if t.Failed() {
   126  			t.Fatalf("Failed querying unique details regarding transaction %v", txHash)
   127  		}
   128  
   129  		// For the most recent tx with this hash, check that
   130  		// TxDetails (not looking up a tx at any particular
   131  		// height) matches the last.
   132  		detail := &details[len(details)-1]
   133  		d, err := s.TxDetails(&txHash)
   134  		if err != nil {
   135  			t.Fatal(err)
   136  		}
   137  		equalTxDetails(t, d, detail)
   138  		if t.Failed() {
   139  			t.Fatalf("Failed querying latest details regarding transaction %v", txHash)
   140  		}
   141  	}
   142  }
   143  
   144  func equalTxDetails(t *testing.T, got, exp *TxDetails) {
   145  	// Need to avoid using reflect.DeepEqual against slices, since it
   146  	// returns false for nil vs non-nil zero length slices.
   147  	equalTxs(t, &got.MsgTx, &exp.MsgTx)
   148  	if got.Hash != exp.Hash {
   149  		t.Errorf("Found mismatched hashes")
   150  		t.Errorf("Got: %v", got.Hash)
   151  		t.Errorf("Expected: %v", exp.Hash)
   152  	}
   153  	if got.Received != exp.Received {
   154  		t.Errorf("Found mismatched receive time")
   155  		t.Errorf("Got: %v", got.Received)
   156  		t.Errorf("Expected: %v", exp.Received)
   157  	}
   158  	if !bytes.Equal(got.SerializedTx, exp.SerializedTx) {
   159  		t.Errorf("Found mismatched serialized txs")
   160  		t.Errorf("Got: %x", got.SerializedTx)
   161  		t.Errorf("Expected: %x", exp.SerializedTx)
   162  	}
   163  	if got.Block != exp.Block {
   164  		t.Errorf("Found mismatched block meta")
   165  		t.Errorf("Got: %v", got.Block)
   166  		t.Errorf("Expected: %v", exp.Block)
   167  	}
   168  	if len(got.Credits) != len(exp.Credits) {
   169  		t.Errorf("Credit slice lengths differ: Got %d Expected %d", len(got.Credits), len(exp.Credits))
   170  	} else {
   171  		for i := range got.Credits {
   172  			if got.Credits[i] != exp.Credits[i] {
   173  				t.Errorf("Found mismatched Credit[%d]", i)
   174  				t.Errorf("Got: %v", got.Credits[i])
   175  				t.Errorf("Expected: %v", exp.Credits[i])
   176  			}
   177  		}
   178  	}
   179  	if len(got.Debits) != len(exp.Debits) {
   180  		t.Errorf("Debit slice lengths differ: Got %d Expected %d", len(got.Debits), len(exp.Debits))
   181  	} else {
   182  		for i := range got.Debits {
   183  			if got.Debits[i] != exp.Debits[i] {
   184  				t.Errorf("Found mismatched Debit[%d]", i)
   185  				t.Errorf("Got: %v", got.Debits[i])
   186  				t.Errorf("Expected: %v", exp.Debits[i])
   187  			}
   188  		}
   189  	}
   190  }
   191  
   192  func equalTxs(t *testing.T, got, exp *wire.MsgTx) {
   193  	var bufGot, bufExp bytes.Buffer
   194  
   195  	bufGot.Grow(got.SerializeSize())
   196  	err := got.Serialize(&bufGot)
   197  	if err != nil {
   198  		t.Fatal(err)
   199  	}
   200  	bufExp.Grow(exp.SerializeSize())
   201  	err = exp.Serialize(&bufExp)
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  	if !bytes.Equal(bufGot.Bytes(), bufExp.Bytes()) {
   206  		t.Errorf("Found unexpected wire.MsgTx:")
   207  		t.Errorf("Got: %x", bufGot.Bytes())
   208  		t.Errorf("Expected: %x", bufExp.Bytes())
   209  	}
   210  }
   211  
   212  // Returns time.Now() with seconds resolution, this is what Store saves.
   213  func timeNow() time.Time {
   214  	return time.Unix(time.Now().Unix(), 0)
   215  }
   216  
   217  // Returns a copy of a TxRecord without the serialized tx.
   218  func stripSerializedTx(rec *TxRecord) *TxRecord {
   219  	ret := *rec
   220  	ret.SerializedTx = nil
   221  	return &ret
   222  }
   223  
   224  func makeBlockMeta(height int32) BlockMeta {
   225  	if height == -1 {
   226  		return BlockMeta{Block: Block{Height: -1}}
   227  	}
   228  
   229  	b := BlockMeta{
   230  		Block: Block{Height: height},
   231  		Time:  timeNow(),
   232  	}
   233  	// Give it a fake block hash created from the height and time.
   234  	binary.LittleEndian.PutUint32(b.Hash[0:4], uint32(height))
   235  	binary.LittleEndian.PutUint64(b.Hash[4:12], uint64(b.Time.Unix()))
   236  	return b
   237  }
   238  
   239  func TestStoreQueries(t *testing.T) {
   240  	t.Parallel()
   241  
   242  	type queryTest struct {
   243  		desc    string
   244  		updates func() // Unwinds from t.Fatal if the update errors.
   245  		state   *queryState
   246  	}
   247  	var tests []queryTest
   248  
   249  	// Create the store and test initial state.
   250  	s, teardown, err := testStore()
   251  	defer teardown()
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	lastState := newQueryState()
   256  	tests = append(tests, queryTest{
   257  		desc:    "initial store",
   258  		updates: func() {},
   259  		state:   lastState,
   260  	})
   261  
   262  	// simplify error handling
   263  	insertTx := func(rec *TxRecord, block *BlockMeta) {
   264  		err := s.InsertTx(rec, block)
   265  		if err != nil {
   266  			t.Fatal(err)
   267  		}
   268  	}
   269  	defaultAccount := uint32(0)
   270  	addCredit := func(s *Store, rec *TxRecord, block *BlockMeta, index uint32, change bool) {
   271  		err := s.AddCredit(rec, block, index, change, defaultAccount)
   272  		if err != nil {
   273  			t.Fatal(err)
   274  		}
   275  	}
   276  	newTxRecordFromMsgTx := func(tx *wire.MsgTx, received time.Time) *TxRecord {
   277  		rec, err := NewTxRecordFromMsgTx(tx, received)
   278  		if err != nil {
   279  			t.Fatal(err)
   280  		}
   281  		return rec
   282  	}
   283  	rollback := func(height int32) {
   284  		err := s.Rollback(height)
   285  		if err != nil {
   286  			t.Fatal(err)
   287  		}
   288  	}
   289  
   290  	// Insert an unmined transaction.  Mark no credits yet.
   291  	txA := spendOutput(&chainhash.Hash{}, 0, 0, 100e8)
   292  	recA := newTxRecordFromMsgTx(txA, timeNow())
   293  	newState := lastState.deepCopy()
   294  	newState.blocks = [][]TxDetails{
   295  		{
   296  			{
   297  				TxRecord: *stripSerializedTx(recA),
   298  				Block:    BlockMeta{Block: Block{Height: -1}},
   299  			},
   300  		},
   301  	}
   302  	newState.txDetails[recA.Hash] = []TxDetails{
   303  		newState.blocks[0][0],
   304  	}
   305  	lastState = newState
   306  	tests = append(tests, queryTest{
   307  		desc:    "insert tx A unmined",
   308  		updates: func() { insertTx(recA, nil) },
   309  		state:   newState,
   310  	})
   311  
   312  	// Add txA:0 as a change credit.
   313  	newState = lastState.deepCopy()
   314  	newState.blocks[0][0].Credits = []CreditRecord{
   315  		{
   316  			Index:  0,
   317  			Amount: dcrutil.Amount(recA.MsgTx.TxOut[0].Value),
   318  			Spent:  false,
   319  			Change: true,
   320  		},
   321  	}
   322  	newState.txDetails[recA.Hash][0].Credits = newState.blocks[0][0].Credits
   323  	lastState = newState
   324  	tests = append(tests, queryTest{
   325  		desc:    "mark unconfirmed txA:0 as credit",
   326  		updates: func() { addCredit(s, recA, nil, 0, true) },
   327  		state:   newState,
   328  	})
   329  
   330  	// Insert another unmined transaction which spends txA:0, splitting the
   331  	// amount into outputs of 40 and 60 DCR.
   332  	txB := spendOutput(&recA.Hash, 0, 0, 40e8, 60e8)
   333  	recB := newTxRecordFromMsgTx(txB, timeNow())
   334  	newState = lastState.deepCopy()
   335  	newState.blocks[0][0].Credits[0].Spent = true
   336  	newState.blocks[0] = append(newState.blocks[0], TxDetails{
   337  		TxRecord: *stripSerializedTx(recB),
   338  		Block:    BlockMeta{Block: Block{Height: -1}},
   339  		Debits: []DebitRecord{
   340  			{
   341  				Amount: dcrutil.Amount(recA.MsgTx.TxOut[0].Value),
   342  				Index:  0, // recB.MsgTx.TxIn index
   343  			},
   344  		},
   345  	})
   346  	newState.txDetails[recA.Hash][0].Credits[0].Spent = true
   347  	newState.txDetails[recB.Hash] = []TxDetails{newState.blocks[0][1]}
   348  	lastState = newState
   349  	tests = append(tests, queryTest{
   350  		desc:    "insert tx B unmined",
   351  		updates: func() { insertTx(recB, nil) },
   352  		state:   newState,
   353  	})
   354  	newState = lastState.deepCopy()
   355  	newState.blocks[0][1].Credits = []CreditRecord{
   356  		{
   357  			Index:  0,
   358  			Amount: dcrutil.Amount(recB.MsgTx.TxOut[0].Value),
   359  			Spent:  false,
   360  			Change: false,
   361  		},
   362  	}
   363  	newState.txDetails[recB.Hash][0].Credits = newState.blocks[0][1].Credits
   364  	lastState = newState
   365  	tests = append(tests, queryTest{
   366  		desc:    "mark txB:0 as non-change credit",
   367  		updates: func() { addCredit(s, recB, nil, 0, false) },
   368  		state:   newState,
   369  	})
   370  
   371  	// Mine tx A at block 100.  Leave tx B unmined.
   372  	b100 := makeBlockMeta(100)
   373  	newState = lastState.deepCopy()
   374  	newState.blocks[0] = newState.blocks[0][:1]
   375  	newState.blocks[0][0].Block = b100
   376  	newState.blocks = append(newState.blocks, lastState.blocks[0][1:])
   377  	newState.txDetails[recA.Hash][0].Block = b100
   378  	lastState = newState
   379  	tests = append(tests, queryTest{
   380  		desc:    "mine tx A",
   381  		updates: func() { insertTx(recA, &b100) },
   382  		state:   newState,
   383  	})
   384  
   385  	// Mine tx B at block 101.
   386  	b101 := makeBlockMeta(101)
   387  	newState = lastState.deepCopy()
   388  	newState.blocks[1][0].Block = b101
   389  	newState.txDetails[recB.Hash][0].Block = b101
   390  	lastState = newState
   391  	tests = append(tests, queryTest{
   392  		desc:    "mine tx B",
   393  		updates: func() { insertTx(recB, &b101) },
   394  		state:   newState,
   395  	})
   396  
   397  	for _, tst := range tests {
   398  		tst.updates()
   399  		tst.state.compare(t, s, tst.desc)
   400  	}
   401  
   402  	// Run some additional query tests with the current store's state:
   403  	//   - Verify that querying for a transaction not in the store returns
   404  	//     nil without failure.
   405  	//   - Verify that querying for a unique transaction at the wrong block
   406  	//     returns nil without failure.
   407  	//   - Verify that breaking early on RangeTransactions stops further
   408  	//     iteration.
   409  
   410  	missingTx := spendOutput(&recB.Hash, 0, 0, 40e8)
   411  	missingRec := newTxRecordFromMsgTx(missingTx, timeNow())
   412  	missingBlock := makeBlockMeta(102)
   413  	missingDetails, err := s.TxDetails(&missingRec.Hash)
   414  	if err != nil {
   415  		t.Fatal(err)
   416  	}
   417  	if missingDetails != nil {
   418  		t.Errorf("Expected no details, found details for tx %v", missingDetails.Hash)
   419  	}
   420  	missingUniqueTests := []struct {
   421  		hash  *chainhash.Hash
   422  		block *Block
   423  	}{
   424  		{&missingRec.Hash, &b100.Block},
   425  		{&missingRec.Hash, &missingBlock.Block},
   426  		{&missingRec.Hash, nil},
   427  		{&recB.Hash, &b100.Block},
   428  		{&recB.Hash, &missingBlock.Block},
   429  		{&recB.Hash, nil},
   430  	}
   431  	for _, tst := range missingUniqueTests {
   432  		missingDetails, err = s.UniqueTxDetails(tst.hash, tst.block)
   433  		if err != nil {
   434  			t.Fatal(err)
   435  		}
   436  		if missingDetails != nil {
   437  			t.Errorf("Expected no details, found details for tx %v", missingDetails.Hash)
   438  		}
   439  	}
   440  
   441  	iterations := 0
   442  	err = s.RangeTransactions(0, -1, func([]TxDetails) (bool, error) {
   443  		iterations++
   444  		return true, nil
   445  	})
   446  	if err != nil {
   447  		t.Error("RangeTransactions (forwards) failed:", err)
   448  	}
   449  	if iterations != 1 {
   450  		t.Errorf("RangeTransactions (forwards) ran func %d times", iterations)
   451  	}
   452  	iterations = 0
   453  	err = s.RangeTransactions(-1, 0, func([]TxDetails) (bool, error) {
   454  		iterations++
   455  		return true, nil
   456  	})
   457  	if err != nil {
   458  		t.Error("RangeTransactions (reverse) failed:", err)
   459  	}
   460  
   461  	if iterations != 1 {
   462  		t.Errorf("RangeTransactions (reverse) ran func %d times", iterations)
   463  	}
   464  	// Make sure it also breaks early after one iteration through unmined transactions.
   465  	rollback(b101.Height)
   466  	iterations = 0
   467  	err = s.RangeTransactions(-1, 0, func([]TxDetails) (bool, error) {
   468  		iterations++
   469  		return true, nil
   470  	})
   471  	if err != nil {
   472  		t.Error("RangeTransactions (reverse) failed:", err)
   473  	}
   474  	if iterations != 1 {
   475  		t.Errorf("RangeTransactions (reverse) ran func %d times", iterations)
   476  	}
   477  
   478  	// None of the above tests have tested RangeTransactions with multiple
   479  	// txs per block, so do that now.  Start by moving tx B to block 100
   480  	// (same block as tx A), and then rollback from block 100 onwards so
   481  	// both are unmined.
   482  	newState = lastState.deepCopy()
   483  	newState.blocks[0] = append(newState.blocks[0], newState.blocks[1]...)
   484  	newState.blocks[0][1].Block = b100
   485  	newState.blocks = newState.blocks[:1]
   486  	newState.txDetails[recB.Hash][0].Block = b100
   487  	lastState = newState
   488  	tests = append(tests[:0:0], queryTest{
   489  		desc:    "move tx B to block 100",
   490  		updates: func() { insertTx(recB, &b100) },
   491  		state:   newState,
   492  	})
   493  	newState = lastState.deepCopy()
   494  	newState.blocks[0][0].Block = makeBlockMeta(-1)
   495  	newState.blocks[0][1].Block = makeBlockMeta(-1)
   496  	newState.txDetails[recA.Hash][0].Block = makeBlockMeta(-1)
   497  	newState.txDetails[recB.Hash][0].Block = makeBlockMeta(-1)
   498  	lastState = newState
   499  	tests = append(tests, queryTest{
   500  		desc:    "rollback block 100",
   501  		updates: func() { rollback(b100.Height) },
   502  		state:   newState,
   503  	})
   504  
   505  	// None of the above tests have tested transactions with colliding
   506  	// hashes, so mine tx A in block 100, and then insert tx A again
   507  	// unmined.  Also mine tx A in block 101 (this moves it from unmined).
   508  	// This is a valid test because the store does not perform signature
   509  	// validation or keep a full utxo set, and duplicated transaction hashes
   510  	// from different blocks are allowed so long as all previous outputs are
   511  	// spent.
   512  	newState = lastState.deepCopy()
   513  	newState.blocks = append(newState.blocks, newState.blocks[0][1:])
   514  	newState.blocks[0] = newState.blocks[0][:1:1]
   515  	newState.blocks[0][0].Block = b100
   516  	newState.blocks[1] = []TxDetails{
   517  		{
   518  			TxRecord: *stripSerializedTx(recA),
   519  			Block:    makeBlockMeta(-1),
   520  		},
   521  		newState.blocks[1][0],
   522  	}
   523  	newState.txDetails[recA.Hash][0].Block = b100
   524  	newState.txDetails[recA.Hash] = append(newState.txDetails[recA.Hash], newState.blocks[1][0])
   525  	lastState = newState
   526  	tests = append(tests, queryTest{
   527  		desc:    "insert duplicate tx A",
   528  		updates: func() { insertTx(recA, &b100); insertTx(recA, nil) },
   529  		state:   newState,
   530  	})
   531  	newState = lastState.deepCopy()
   532  	newState.blocks = [][]TxDetails{
   533  		newState.blocks[0],
   534  		{newState.blocks[1][0]},
   535  		{newState.blocks[1][1]},
   536  	}
   537  	newState.blocks[1][0].Block = b101
   538  	newState.txDetails[recA.Hash][1].Block = b101
   539  	lastState = newState
   540  	tests = append(tests, queryTest{
   541  		desc:    "mine duplicate tx A",
   542  		updates: func() { insertTx(recA, &b101) },
   543  		state:   newState,
   544  	})
   545  
   546  	for _, tst := range tests {
   547  		tst.updates()
   548  		tst.state.compare(t, s, tst.desc)
   549  	}
   550  }
   551  
   552  func TestPreviousPkScripts(t *testing.T) {
   553  	t.Parallel()
   554  
   555  	s, teardown, err := testStore()
   556  	defer teardown()
   557  	if err != nil {
   558  		t.Fatal(err)
   559  	}
   560  
   561  	// Invalid scripts but sufficient for testing.
   562  	var (
   563  		scriptA0 = []byte("tx A output 0")
   564  		scriptA1 = []byte("tx A output 1")
   565  		scriptB0 = []byte("tx B output 0")
   566  		scriptB1 = []byte("tx B output 1")
   567  		scriptC0 = []byte("tx C output 0")
   568  		scriptC1 = []byte("tx C output 1")
   569  	)
   570  
   571  	// Create a transaction spending two prevous outputs and generating two
   572  	// new outputs the passed pkScipts.  Spends outputs 0 and 1 from prevHash.
   573  	buildTx := func(prevHash *chainhash.Hash, script0, script1 []byte) *wire.MsgTx {
   574  		return &wire.MsgTx{
   575  			TxIn: []*wire.TxIn{
   576  				{PreviousOutPoint: wire.OutPoint{
   577  					Hash:  *prevHash,
   578  					Index: 0,
   579  					Tree:  dcrutil.TxTreeRegular,
   580  				}},
   581  				{PreviousOutPoint: wire.OutPoint{
   582  					Hash:  *prevHash,
   583  					Index: 1,
   584  					Tree:  dcrutil.TxTreeRegular,
   585  				}},
   586  			},
   587  			TxOut: []*wire.TxOut{
   588  				{Value: 1e8, PkScript: script0},
   589  				{Value: 1e8, PkScript: script1},
   590  			},
   591  		}
   592  	}
   593  
   594  	newTxRecordFromMsgTx := func(tx *wire.MsgTx) *TxRecord {
   595  		rec, err := NewTxRecordFromMsgTx(tx, timeNow())
   596  		if err != nil {
   597  			t.Fatal(err)
   598  		}
   599  		return rec
   600  	}
   601  
   602  	// Create transactions with the fake output scripts.
   603  	var (
   604  		txA  = buildTx(&chainhash.Hash{}, scriptA0, scriptA1)
   605  		recA = newTxRecordFromMsgTx(txA)
   606  		txB  = buildTx(&recA.Hash, scriptB0, scriptB1)
   607  		recB = newTxRecordFromMsgTx(txB)
   608  		txC  = buildTx(&recB.Hash, scriptC0, scriptC1)
   609  		recC = newTxRecordFromMsgTx(txC)
   610  		txD  = buildTx(&recC.Hash, nil, nil)
   611  		recD = newTxRecordFromMsgTx(txD)
   612  	)
   613  
   614  	insertTx := func(rec *TxRecord, block *BlockMeta) {
   615  		err := s.InsertTx(rec, block)
   616  		if err != nil {
   617  			t.Fatal(err)
   618  		}
   619  	}
   620  	defaultAccount := uint32(0)
   621  	addCredit := func(rec *TxRecord, block *BlockMeta, index uint32) {
   622  		err := s.AddCredit(rec, block, index, false, defaultAccount)
   623  		if err != nil {
   624  			t.Fatal(err)
   625  		}
   626  	}
   627  
   628  	type scriptTest struct {
   629  		rec     *TxRecord
   630  		block   *Block
   631  		scripts [][]byte
   632  	}
   633  	runTest := func(tst *scriptTest) {
   634  		scripts, err := s.PreviousPkScripts(tst.rec, tst.block)
   635  		if err != nil {
   636  			t.Fatal(err)
   637  		}
   638  		height := int32(-1)
   639  		if tst.block != nil {
   640  			height = tst.block.Height
   641  		}
   642  		if len(scripts) != len(tst.scripts) {
   643  			t.Errorf("Transaction %v height %d: got len(scripts)=%d, expected %d",
   644  				tst.rec.Hash, height, len(scripts), len(tst.scripts))
   645  			return
   646  		}
   647  		for i := range scripts {
   648  			if !bytes.Equal(scripts[i], tst.scripts[i]) {
   649  				// Format scripts with %s since they are (should be) ascii.
   650  				t.Errorf("Transaction %v height %d script %d: got '%s' expected '%s'",
   651  					tst.rec.Hash, height, i, scripts[i], tst.scripts[i])
   652  			}
   653  		}
   654  	}
   655  
   656  	// Insert transactions A-C unmined, but mark no credits yet.  Until
   657  	// these are marked as credits, PreviousPkScripts should not return
   658  	// them.
   659  	insertTx(recA, nil)
   660  	insertTx(recB, nil)
   661  	insertTx(recC, nil)
   662  
   663  	b100 := makeBlockMeta(100)
   664  	b101 := makeBlockMeta(101)
   665  
   666  	tests := []scriptTest{
   667  		{recA, nil, nil},
   668  		{recA, &b100.Block, nil},
   669  		{recB, nil, nil},
   670  		{recB, &b100.Block, nil},
   671  		{recC, nil, nil},
   672  		{recC, &b100.Block, nil},
   673  	}
   674  	for _, tst := range tests {
   675  		runTest(&tst)
   676  	}
   677  	if t.Failed() {
   678  		t.Fatal("Failed after unmined tx inserts")
   679  	}
   680  
   681  	// Mark credits.  Tx C output 1 not marked as a credit: tx D will spend
   682  	// both later but when C is mined, output 1's script should not be
   683  	// returned.
   684  	addCredit(recA, nil, 0)
   685  	addCredit(recA, nil, 1)
   686  	addCredit(recB, nil, 0)
   687  	addCredit(recB, nil, 1)
   688  	addCredit(recC, nil, 0)
   689  	tests = []scriptTest{
   690  		{recA, nil, nil},
   691  		{recA, &b100.Block, nil},
   692  		{recB, nil, [][]byte{scriptA0, scriptA1}},
   693  		{recB, &b100.Block, nil},
   694  		{recC, nil, [][]byte{scriptB0, scriptB1}},
   695  		{recC, &b100.Block, nil},
   696  	}
   697  	for _, tst := range tests {
   698  		runTest(&tst)
   699  	}
   700  	if t.Failed() {
   701  		t.Fatal("Failed after marking unmined credits")
   702  	}
   703  
   704  	// Mine tx A in block 100.  Test results should be identical.
   705  	insertTx(recA, &b100)
   706  	for _, tst := range tests {
   707  		runTest(&tst)
   708  	}
   709  	if t.Failed() {
   710  		t.Fatal("Failed after mining tx A")
   711  	}
   712  
   713  	// Mine tx B in block 101.
   714  	insertTx(recB, &b101)
   715  	tests = []scriptTest{
   716  		{recA, nil, nil},
   717  		{recA, &b100.Block, nil},
   718  		{recB, nil, nil},
   719  		{recB, &b101.Block, [][]byte{scriptA0, scriptA1}},
   720  		{recC, nil, [][]byte{scriptB0, scriptB1}},
   721  		{recC, &b101.Block, nil},
   722  	}
   723  	for _, tst := range tests {
   724  		runTest(&tst)
   725  	}
   726  	if t.Failed() {
   727  		t.Fatal("Failed after mining tx B")
   728  	}
   729  
   730  	// Mine tx C in block 101 (same block as tx B) to test debits from the
   731  	// same block.
   732  	insertTx(recC, &b101)
   733  	tests = []scriptTest{
   734  		{recA, nil, nil},
   735  		{recA, &b100.Block, nil},
   736  		{recB, nil, nil},
   737  		{recB, &b101.Block, [][]byte{scriptA0, scriptA1}},
   738  		{recC, nil, nil},
   739  		{recC, &b101.Block, [][]byte{scriptB0, scriptB1}},
   740  	}
   741  	for _, tst := range tests {
   742  		runTest(&tst)
   743  	}
   744  	if t.Failed() {
   745  		t.Fatal("Failed after mining tx C")
   746  	}
   747  
   748  	// Insert tx D, which spends C:0 and C:1.  However, only C:0 is marked
   749  	// as a credit, and only that output script should be returned.
   750  	insertTx(recD, nil)
   751  	tests = append(tests, scriptTest{recD, nil, [][]byte{scriptC0}})
   752  	tests = append(tests, scriptTest{recD, &b101.Block, nil})
   753  	for _, tst := range tests {
   754  		runTest(&tst)
   755  	}
   756  	if t.Failed() {
   757  		t.Fatal("Failed after inserting tx D")
   758  	}
   759  }