tx_test.go raw

   1  package wtxmgr
   2  
   3  import (
   4  	"bytes"
   5  	"encoding/hex"
   6  	"github.com/p9c/p9/pkg/amt"
   7  	"io/ioutil"
   8  	"os"
   9  	"path/filepath"
  10  	"testing"
  11  	"time"
  12  	
  13  	"github.com/p9c/p9/pkg/chaincfg"
  14  	"github.com/p9c/p9/pkg/chainhash"
  15  	"github.com/p9c/p9/pkg/util"
  16  	"github.com/p9c/p9/pkg/walletdb"
  17  	_ "github.com/p9c/p9/pkg/walletdb/bdb"
  18  	"github.com/p9c/p9/pkg/wire"
  19  )
  20  
  21  // Received transaction output for mainnet outpoint
  22  // 61d3696de4c888730cbe06b0ad8ecb6d72d6108e893895aa9bc067bd7eba3fad:0
  23  var (
  24  	TstRecvSerializedTx, _          = hex.DecodeString("010000000114d9ff358894c486b4ae11c2a8cf7851b1df64c53d2e511278eff17c22fb7373000000008c493046022100995447baec31ee9f6d4ec0e05cb2a44f6b817a99d5f6de167d1c75354a946410022100c9ffc23b64d770b0e01e7ff4d25fbc2f1ca8091053078a247905c39fce3760b601410458b8e267add3c1e374cf40f1de02b59213a82e1d84c2b94096e22e2f09387009c96debe1d0bcb2356ffdcf65d2a83d4b34e72c62eccd8490dbf2110167783b2bffffffff0280969800000000001976a914479ed307831d0ac19ebc5f63de7d5f1a430ddb9d88ac38bfaa00000000001976a914dadf9e3484f28b385ddeaa6c575c0c0d18e9788a88ac00000000")
  25  	TstRecvTx, _                    = util.NewTxFromBytes(TstRecvSerializedTx)
  26  	TstRecvTxSpendingTxBlockHash, _ = chainhash.NewHashFromStr("00000000000000017188b968a371bab95aa43522665353b646e41865abae02a4")
  27  	// TstRecvAmt                      = int64(10000000)
  28  	TstRecvTxBlockDetails = &BlockMeta{
  29  		Block: Block{Hash: *TstRecvTxSpendingTxBlockHash, Height: 276425},
  30  		Time:  time.Unix(1387737310, 0),
  31  	}
  32  	TstRecvCurrentHeight = int32(284498) // mainnet blockchain height at time of writing
  33  	// TstRecvTxOutConfirms       = 8074          // hardcoded number of confirmations given the above block height
  34  	TstSpendingSerializedTx, _ = hex.DecodeString("0100000003ad3fba7ebd67c09baa9538898e10d6726dcb8eadb006be0c7388c8e46d69d361000000006b4830450220702c4fbde5532575fed44f8d6e8c3432a2a9bd8cff2f966c3a79b2245a7c88db02210095d6505a57e350720cb52b89a9b56243c15ddfcea0596aedc1ba55d9fb7d5aa0012103cccb5c48a699d3efcca6dae277fee6b82e0229ed754b742659c3acdfed2651f9ffffffffdbd36173f5610e34de5c00ed092174603761595d90190f790e79cda3e5b45bc2010000006b483045022000fa20735e5875e64d05bed43d81b867f3bd8745008d3ff4331ef1617eac7c44022100ad82261fc57faac67fc482a37b6bf18158da0971e300abf5fe2f9fd39e107f58012102d4e1caf3e022757512c204bf09ff56a9981df483aba3c74bb60d3612077c9206ffffffff65536c9d964b6f89b8ef17e83c6666641bc495cb27bab60052f76cd4556ccd0d040000006a473044022068e3886e0299ffa69a1c3ee40f8b6700f5f6d463a9cf9dbf22c055a131fc4abc02202b58957fe19ff1be7a84c458d08016c53fbddec7184ac5e633f2b282ae3420ae012103b4e411b81d32a69fb81178a8ea1abaa12f613336923ee920ffbb1b313af1f4d2ffffffff02ab233200000000001976a91418808b2fbd8d2c6d022aed5cd61f0ce6c0a4cbb688ac4741f011000000001976a914f081088a300c80ce36b717a9914ab5ec8a7d283988ac00000000")
  35  	TstSpendingTx, _           = util.NewTxFromBytes(TstSpendingSerializedTx)
  36  	TstSpendingTxBlockHeight   = int32(279143)
  37  	TstSignedTxBlockHash, _    = chainhash.NewHashFromStr("00000000000000017188b968a371bab95aa43522665353b646e41865abae02a4")
  38  	TstSignedTxBlockDetails    = &BlockMeta{
  39  		Block: Block{Hash: *TstSignedTxBlockHash, Height: TstSpendingTxBlockHeight},
  40  		Time:  time.Unix(1389114091, 0),
  41  	}
  42  )
  43  
  44  func testDB() (walletdb.DB, func(), error) {
  45  	tmpDir, e := ioutil.TempDir("", "wtxmgr_test")
  46  	if e != nil {
  47  		return nil, func() {
  48  		}, e
  49  	}
  50  	db, e := walletdb.Create("bdb", filepath.Join(tmpDir, "db"))
  51  	return db, func() {
  52  		if e = os.RemoveAll(tmpDir); E.Chk(e) {
  53  		}
  54  	}, e
  55  }
  56  
  57  var namespaceKey = []byte("txstore")
  58  
  59  func testStore() (*Store, walletdb.DB, func(), error) {
  60  	var e error
  61  	var tmpDir string
  62  	tmpDir, e = ioutil.TempDir("", "wtxmgr_test")
  63  	if e != nil {
  64  		return nil, nil, func() {
  65  		}, e
  66  	}
  67  	var db walletdb.DB
  68  	db, e = walletdb.Create("bdb", filepath.Join(tmpDir, "db"))
  69  	if e != nil {
  70  		if e = os.RemoveAll(tmpDir); E.Chk(e) {
  71  		}
  72  		return nil, nil, nil, e
  73  	}
  74  	teardown := func() {
  75  		if e = db.Close(); E.Chk(e) {
  76  		}
  77  		if e = os.RemoveAll(tmpDir); E.Chk(e) {
  78  		}
  79  	}
  80  	var s *Store
  81  	e = walletdb.Update(db, func(tx walletdb.ReadWriteTx) (e error) {
  82  		ns, e := tx.CreateTopLevelBucket(namespaceKey)
  83  		if e != nil {
  84  			return e
  85  		}
  86  		e = Create(ns)
  87  		if e != nil {
  88  			return e
  89  		}
  90  		s, e = Open(ns, &chaincfg.TestNet3Params)
  91  		return e
  92  	},
  93  	)
  94  	return s, db, teardown, e
  95  }
  96  func serializeTx(tx *util.Tx) []byte {
  97  	var buf bytes.Buffer
  98  	e := tx.MsgTx().Serialize(&buf)
  99  	if e != nil {
 100  		panic(e)
 101  	}
 102  	return buf.Bytes()
 103  }
 104  func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
 105  	t.Parallel()
 106  	// Create a double spend of the received blockchain transaction.
 107  	dupRecvTx, _ := util.NewTxFromBytes(TstRecvSerializedTx)
 108  	// Switch txout amount to 1 DUO. Transaction store doesn't validate txs, so this is fine for testing a double spend
 109  	// removal.
 110  	TstDupRecvAmount := int64(1e8)
 111  	newDupMsgTx := dupRecvTx.MsgTx()
 112  	newDupMsgTx.TxOut[0].Value = TstDupRecvAmount
 113  	TstDoubleSpendTx := util.NewTx(newDupMsgTx)
 114  	TstDoubleSpendSerializedTx := serializeTx(TstDoubleSpendTx)
 115  	// Create a "signed" (with invalid sigs) tx that spends output 0 of the double spend.
 116  	spendingTx := wire.NewMsgTx(wire.TxVersion)
 117  	spendingTxIn := wire.NewTxIn(wire.NewOutPoint(TstDoubleSpendTx.Hash(), 0), []byte{0, 1, 2, 3, 4}, nil)
 118  	spendingTx.AddTxIn(spendingTxIn)
 119  	spendingTxOut1 := wire.NewTxOut(1e7, []byte{5, 6, 7, 8, 9})
 120  	spendingTxOut2 := wire.NewTxOut(9e7, []byte{10, 11, 12, 13, 14})
 121  	spendingTx.AddTxOut(spendingTxOut1)
 122  	spendingTx.AddTxOut(spendingTxOut2)
 123  	TstSpendingTx = util.NewTx(spendingTx)
 124  	TstSpendingSerializedTx = serializeTx(TstSpendingTx)
 125  	var _ = TstSpendingTx
 126  	tests := []struct {
 127  		name     string
 128  		f        func(*Store, walletdb.ReadWriteBucket) (*Store, error)
 129  		bal, unc amt.Amount
 130  		unspents map[wire.OutPoint]struct{}
 131  		unmined  map[chainhash.Hash]struct{}
 132  	}{
 133  		{
 134  			name: "new store",
 135  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 136  				return s, nil
 137  			},
 138  			bal:      0,
 139  			unc:      0,
 140  			unspents: map[wire.OutPoint]struct{}{},
 141  			unmined:  map[chainhash.Hash]struct{}{},
 142  		},
 143  		{
 144  			name: "txout insert",
 145  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 146  				rec, e := NewTxRecord(TstRecvSerializedTx, time.Now())
 147  				if e != nil {
 148  					return nil, e
 149  				}
 150  				e = s.InsertTx(ns, rec, nil)
 151  				if e != nil {
 152  					return nil, e
 153  				}
 154  				e = s.AddCredit(ns, rec, nil, 0, false)
 155  				return s, e
 156  			},
 157  			bal: 0,
 158  			unc: amt.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
 159  			unspents: map[wire.OutPoint]struct{}{
 160  				{
 161  					Hash:  *TstRecvTx.Hash(),
 162  					Index: 0,
 163  				}: {},
 164  			},
 165  			unmined: map[chainhash.Hash]struct{}{
 166  				*TstRecvTx.Hash(): {},
 167  			},
 168  		},
 169  		{
 170  			name: "insert duplicate unconfirmed",
 171  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 172  				rec, e := NewTxRecord(TstRecvSerializedTx, time.Now())
 173  				if e != nil {
 174  					return nil, e
 175  				}
 176  				e = s.InsertTx(ns, rec, nil)
 177  				if e != nil {
 178  					return nil, e
 179  				}
 180  				e = s.AddCredit(ns, rec, nil, 0, false)
 181  				return s, e
 182  			},
 183  			bal: 0,
 184  			unc: amt.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
 185  			unspents: map[wire.OutPoint]struct{}{
 186  				{
 187  					Hash:  *TstRecvTx.Hash(),
 188  					Index: 0,
 189  				}: {},
 190  			},
 191  			unmined: map[chainhash.Hash]struct{}{
 192  				*TstRecvTx.Hash(): {},
 193  			},
 194  		},
 195  		{
 196  			name: "confirmed txout insert",
 197  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 198  				rec, e := NewTxRecord(TstRecvSerializedTx, time.Now())
 199  				if e != nil {
 200  					return nil, e
 201  				}
 202  				e = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
 203  				if e != nil {
 204  					return nil, e
 205  				}
 206  				e = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
 207  				return s, e
 208  			},
 209  			bal: amt.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
 210  			unc: 0,
 211  			unspents: map[wire.OutPoint]struct{}{
 212  				{
 213  					Hash:  *TstRecvTx.Hash(),
 214  					Index: 0,
 215  				}: {},
 216  			},
 217  			unmined: map[chainhash.Hash]struct{}{},
 218  		},
 219  		{
 220  			name: "insert duplicate confirmed",
 221  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 222  				rec, e := NewTxRecord(TstRecvSerializedTx, time.Now())
 223  				if e != nil {
 224  					return nil, e
 225  				}
 226  				e = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
 227  				if e != nil {
 228  					return nil, e
 229  				}
 230  				e = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
 231  				return s, e
 232  			},
 233  			bal: amt.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
 234  			unc: 0,
 235  			unspents: map[wire.OutPoint]struct{}{
 236  				{
 237  					Hash:  *TstRecvTx.Hash(),
 238  					Index: 0,
 239  				}: {},
 240  			},
 241  			unmined: map[chainhash.Hash]struct{}{},
 242  		},
 243  		{
 244  			name: "rollback confirmed credit",
 245  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 246  				e := s.Rollback(ns, TstRecvTxBlockDetails.Height)
 247  				return s, e
 248  			},
 249  			bal: 0,
 250  			unc: amt.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
 251  			unspents: map[wire.OutPoint]struct{}{
 252  				{
 253  					Hash:  *TstRecvTx.Hash(),
 254  					Index: 0,
 255  				}: {},
 256  			},
 257  			unmined: map[chainhash.Hash]struct{}{
 258  				*TstRecvTx.Hash(): {},
 259  			},
 260  		},
 261  		{
 262  			name: "insert confirmed double spend",
 263  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 264  				rec, e := NewTxRecord(TstDoubleSpendSerializedTx, time.Now())
 265  				if e != nil {
 266  					return nil, e
 267  				}
 268  				e = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
 269  				if e != nil {
 270  					return nil, e
 271  				}
 272  				e = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
 273  				return s, e
 274  			},
 275  			bal: amt.Amount(TstDoubleSpendTx.MsgTx().TxOut[0].Value),
 276  			unc: 0,
 277  			unspents: map[wire.OutPoint]struct{}{
 278  				{
 279  					Hash:  *TstDoubleSpendTx.Hash(),
 280  					Index: 0,
 281  				}: {},
 282  			},
 283  			unmined: map[chainhash.Hash]struct{}{},
 284  		},
 285  		{
 286  			name: "insert unconfirmed debit",
 287  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 288  				rec, e := NewTxRecord(TstSpendingSerializedTx, time.Now())
 289  				if e != nil {
 290  					return nil, e
 291  				}
 292  				e = s.InsertTx(ns, rec, nil)
 293  				return s, e
 294  			},
 295  			bal:      0,
 296  			unc:      0,
 297  			unspents: map[wire.OutPoint]struct{}{},
 298  			unmined: map[chainhash.Hash]struct{}{
 299  				*TstSpendingTx.Hash(): {},
 300  			},
 301  		},
 302  		{
 303  			name: "insert unconfirmed debit again",
 304  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 305  				rec, e := NewTxRecord(TstDoubleSpendSerializedTx, time.Now())
 306  				if e != nil {
 307  					return nil, e
 308  				}
 309  				e = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
 310  				return s, e
 311  			},
 312  			bal:      0,
 313  			unc:      0,
 314  			unspents: map[wire.OutPoint]struct{}{},
 315  			unmined: map[chainhash.Hash]struct{}{
 316  				*TstSpendingTx.Hash(): {},
 317  			},
 318  		},
 319  		{
 320  			name: "insert change (index 0)",
 321  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 322  				rec, e := NewTxRecord(TstSpendingSerializedTx, time.Now())
 323  				if e != nil {
 324  					return nil, e
 325  				}
 326  				e = s.InsertTx(ns, rec, nil)
 327  				if e != nil {
 328  					return nil, e
 329  				}
 330  				e = s.AddCredit(ns, rec, nil, 0, true)
 331  				return s, e
 332  			},
 333  			bal: 0,
 334  			unc: amt.Amount(TstSpendingTx.MsgTx().TxOut[0].Value),
 335  			unspents: map[wire.OutPoint]struct{}{
 336  				{
 337  					Hash:  *TstSpendingTx.Hash(),
 338  					Index: 0,
 339  				}: {},
 340  			},
 341  			unmined: map[chainhash.Hash]struct{}{
 342  				*TstSpendingTx.Hash(): {},
 343  			},
 344  		},
 345  		{
 346  			name: "insert output back to this own wallet (index 1)",
 347  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 348  				rec, e := NewTxRecord(TstSpendingSerializedTx, time.Now())
 349  				if e != nil {
 350  					return nil, e
 351  				}
 352  				e = s.InsertTx(ns, rec, nil)
 353  				if e != nil {
 354  					return nil, e
 355  				}
 356  				e = s.AddCredit(ns, rec, nil, 1, true)
 357  				return s, e
 358  			},
 359  			bal: 0,
 360  			unc: amt.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
 361  			unspents: map[wire.OutPoint]struct{}{
 362  				{
 363  					Hash:  *TstSpendingTx.Hash(),
 364  					Index: 0,
 365  				}: {},
 366  				{
 367  					Hash:  *TstSpendingTx.Hash(),
 368  					Index: 1,
 369  				}: {},
 370  			},
 371  			unmined: map[chainhash.Hash]struct{}{
 372  				*TstSpendingTx.Hash(): {},
 373  			},
 374  		},
 375  		{
 376  			name: "confirm signed tx",
 377  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 378  				rec, e := NewTxRecord(TstSpendingSerializedTx, time.Now())
 379  				if e != nil {
 380  					return nil, e
 381  				}
 382  				e = s.InsertTx(ns, rec, TstSignedTxBlockDetails)
 383  				return s, e
 384  			},
 385  			bal: amt.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
 386  			unc: 0,
 387  			unspents: map[wire.OutPoint]struct{}{
 388  				{
 389  					Hash:  *TstSpendingTx.Hash(),
 390  					Index: 0,
 391  				}: {},
 392  				{
 393  					Hash:  *TstSpendingTx.Hash(),
 394  					Index: 1,
 395  				}: {},
 396  			},
 397  			unmined: map[chainhash.Hash]struct{}{},
 398  		},
 399  		{
 400  			name: "rollback after spending tx",
 401  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 402  				e := s.Rollback(ns, TstSignedTxBlockDetails.Height+1)
 403  				return s, e
 404  			},
 405  			bal: amt.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
 406  			unc: 0,
 407  			unspents: map[wire.OutPoint]struct{}{
 408  				{
 409  					Hash:  *TstSpendingTx.Hash(),
 410  					Index: 0,
 411  				}: {},
 412  				{
 413  					Hash:  *TstSpendingTx.Hash(),
 414  					Index: 1,
 415  				}: {},
 416  			},
 417  			unmined: map[chainhash.Hash]struct{}{},
 418  		},
 419  		{
 420  			name: "rollback spending tx block",
 421  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 422  				e := s.Rollback(ns, TstSignedTxBlockDetails.Height)
 423  				return s, e
 424  			},
 425  			bal: 0,
 426  			unc: amt.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
 427  			unspents: map[wire.OutPoint]struct{}{
 428  				{
 429  					Hash:  *TstSpendingTx.Hash(),
 430  					Index: 0,
 431  				}: {},
 432  				{
 433  					Hash:  *TstSpendingTx.Hash(),
 434  					Index: 1,
 435  				}: {},
 436  			},
 437  			unmined: map[chainhash.Hash]struct{}{
 438  				*TstSpendingTx.Hash(): {},
 439  			},
 440  		},
 441  		{
 442  			name: "rollback double spend tx block",
 443  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 444  				e := s.Rollback(ns, TstRecvTxBlockDetails.Height)
 445  				return s, e
 446  			},
 447  			bal: 0,
 448  			unc: amt.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
 449  			unspents: map[wire.OutPoint]struct{}{
 450  				*wire.NewOutPoint(TstSpendingTx.Hash(), 0): {},
 451  				*wire.NewOutPoint(TstSpendingTx.Hash(), 1): {},
 452  			},
 453  			unmined: map[chainhash.Hash]struct{}{
 454  				*TstDoubleSpendTx.Hash(): {},
 455  				*TstSpendingTx.Hash():    {},
 456  			},
 457  		},
 458  		{
 459  			name: "insert original recv txout",
 460  			f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
 461  				rec, e := NewTxRecord(TstRecvSerializedTx, time.Now())
 462  				if e != nil {
 463  					return nil, e
 464  				}
 465  				e = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
 466  				if e != nil {
 467  					return nil, e
 468  				}
 469  				e = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
 470  				return s, e
 471  			},
 472  			bal: amt.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
 473  			unc: 0,
 474  			unspents: map[wire.OutPoint]struct{}{
 475  				*wire.NewOutPoint(TstRecvTx.Hash(), 0): {},
 476  			},
 477  			unmined: map[chainhash.Hash]struct{}{},
 478  		},
 479  	}
 480  	s, db, teardown, e := testStore()
 481  	if e != nil {
 482  		t.Fatal(e)
 483  	}
 484  	defer teardown()
 485  	for _, test := range tests {
 486  		e := walletdb.Update(db, func(tx walletdb.ReadWriteTx) (e error) {
 487  			ns := tx.ReadWriteBucket(namespaceKey)
 488  			tmpStore, e := test.f(s, ns)
 489  			if e != nil {
 490  				t.Fatalf("%s: got error: %v", test.name, e)
 491  			}
 492  			s = tmpStore
 493  			bal, e := s.Balance(ns, 1, TstRecvCurrentHeight)
 494  			if e != nil {
 495  				t.Fatalf("%s: Confirmed Balance failed: %v", test.name, e)
 496  			}
 497  			if bal != test.bal {
 498  				t.Fatalf("%s: balance mismatch: expected: %d, got: %d", test.name, test.bal, bal)
 499  			}
 500  			unc, e := s.Balance(ns, 0, TstRecvCurrentHeight)
 501  			if e != nil {
 502  				t.Fatalf("%s: Unconfirmed Balance failed: %v", test.name, e)
 503  			}
 504  			unc -= bal
 505  			if unc != test.unc {
 506  				t.Fatalf("%s: unconfirmed balance mismatch: expected %d, got %d", test.name, test.unc, unc)
 507  			}
 508  			// Chk that unspent outputs match expected.
 509  			unspent, e := s.UnspentOutputs(ns)
 510  			if e != nil {
 511  				t.Fatalf("%s: failed to fetch unspent outputs: %v", test.name, e)
 512  			}
 513  			for _, cred := range unspent {
 514  				if _, ok := test.unspents[cred.OutPoint]; !ok {
 515  					t.Errorf("%s: unexpected unspent output: %v", test.name, cred.OutPoint)
 516  				}
 517  				delete(test.unspents, cred.OutPoint)
 518  			}
 519  			if len(test.unspents) != 0 {
 520  				t.Fatalf("%s: missing expected unspent output(s)", test.name)
 521  			}
 522  			// Chk that unmined txs match expected.
 523  			unmined, e := s.UnminedTxs(ns)
 524  			if e != nil {
 525  				t.Fatalf("%s: cannot load unmined transactions: %v", test.name, e)
 526  			}
 527  			for _, tx := range unmined {
 528  				txHash := tx.TxHash()
 529  				if _, ok := test.unmined[txHash]; !ok {
 530  					t.Fatalf("%s: unexpected unmined tx: %v", test.name, txHash)
 531  				}
 532  				delete(test.unmined, txHash)
 533  			}
 534  			if len(test.unmined) != 0 {
 535  				t.Fatalf("%s: missing expected unmined tx(s)", test.name)
 536  			}
 537  			return nil
 538  		},
 539  		)
 540  		if e != nil {
 541  			t.Fatal(e)
 542  		}
 543  	}
 544  }
 545  func TestFindingSpentCredits(t *testing.T) {
 546  	t.Parallel()
 547  	s, db, teardown, e := testStore()
 548  	if e != nil {
 549  		t.Fatal(e)
 550  	}
 551  	defer teardown()
 552  	dbtx, e := db.BeginReadWriteTx()
 553  	if e != nil {
 554  		t.Fatal(e)
 555  	}
 556  	defer func() {
 557  		e = dbtx.Commit()
 558  		if e != nil {
 559  			t.Log(e)
 560  		}
 561  	}()
 562  	ns := dbtx.ReadWriteBucket(namespaceKey)
 563  	// Insert transaction and credit which will be spent.
 564  	recvRec, e := NewTxRecord(TstRecvSerializedTx, time.Now())
 565  	if e != nil {
 566  		t.Fatal(e)
 567  	}
 568  	e = s.InsertTx(ns, recvRec, TstRecvTxBlockDetails)
 569  	if e != nil {
 570  		t.Fatal(e)
 571  	}
 572  	e = s.AddCredit(ns, recvRec, TstRecvTxBlockDetails, 0, false)
 573  	if e != nil {
 574  		t.Fatal(e)
 575  	}
 576  	// Insert confirmed transaction which spends the above credit.
 577  	spendingRec, e := NewTxRecord(TstSpendingSerializedTx, time.Now())
 578  	if e != nil {
 579  		t.Fatal(e)
 580  	}
 581  	e = s.InsertTx(ns, spendingRec, TstSignedTxBlockDetails)
 582  	if e != nil {
 583  		t.Fatal(e)
 584  	}
 585  	e = s.AddCredit(ns, spendingRec, TstSignedTxBlockDetails, 0, false)
 586  	if e != nil {
 587  		t.Fatal(e)
 588  	}
 589  	bal, e := s.Balance(ns, 1, TstSignedTxBlockDetails.Height)
 590  	if e != nil {
 591  		t.Fatal(e)
 592  	}
 593  	expectedBal := amt.Amount(TstSpendingTx.MsgTx().TxOut[0].Value)
 594  	if bal != expectedBal {
 595  		t.Fatalf("bad balance: %v != %v", bal, expectedBal)
 596  	}
 597  	unspents, e := s.UnspentOutputs(ns)
 598  	if e != nil {
 599  		t.Fatal(e)
 600  	}
 601  	op := wire.NewOutPoint(TstSpendingTx.Hash(), 0)
 602  	if unspents[0].OutPoint != *op {
 603  		t.Fatal("unspent outpoint doesn't match expected")
 604  	}
 605  	if len(unspents) > 1 {
 606  		t.Fatal("has more than one unspent credit")
 607  	}
 608  }
 609  func newCoinBase(outputValues ...int64) *wire.MsgTx {
 610  	tx := wire.MsgTx{
 611  		TxIn: []*wire.TxIn{
 612  			{
 613  				PreviousOutPoint: wire.OutPoint{Index: ^uint32(0)},
 614  			},
 615  		},
 616  	}
 617  	for _, val := range outputValues {
 618  		tx.TxOut = append(tx.TxOut, &wire.TxOut{Value: val})
 619  	}
 620  	return &tx
 621  }
 622  func spendOutput(txHash *chainhash.Hash, index uint32, outputValues ...int64) *wire.MsgTx {
 623  	tx := wire.MsgTx{
 624  		TxIn: []*wire.TxIn{
 625  			{
 626  				PreviousOutPoint: wire.OutPoint{Hash: *txHash, Index: index},
 627  			},
 628  		},
 629  	}
 630  	for _, val := range outputValues {
 631  		tx.TxOut = append(tx.TxOut, &wire.TxOut{Value: val})
 632  	}
 633  	return &tx
 634  }
 635  func spendOutputs(outputs []wire.OutPoint, outputValues ...int64) *wire.MsgTx {
 636  	tx := &wire.MsgTx{}
 637  	for _, output := range outputs {
 638  		tx.TxIn = append(tx.TxIn, &wire.TxIn{PreviousOutPoint: output})
 639  	}
 640  	for _, value := range outputValues {
 641  		tx.TxOut = append(tx.TxOut, &wire.TxOut{Value: value})
 642  	}
 643  	return tx
 644  }
 645  func TestCoinbases(t *testing.T) {
 646  	t.Parallel()
 647  	s, db, teardown, e := testStore()
 648  	if e != nil {
 649  		t.Fatal(e)
 650  	}
 651  	defer teardown()
 652  	dbtx, e := db.BeginReadWriteTx()
 653  	if e != nil {
 654  		t.Fatal(e)
 655  	}
 656  	defer func() {
 657  		e = dbtx.Commit()
 658  		if e != nil {
 659  			t.Log(e)
 660  		}
 661  	}()
 662  	ns := dbtx.ReadWriteBucket(namespaceKey)
 663  	b100 := BlockMeta{
 664  		Block: Block{Height: 100},
 665  		Time:  time.Now(),
 666  	}
 667  	cb := newCoinBase(20e8, 10e8, 30e8)
 668  	cbRec, e := NewTxRecordFromMsgTx(cb, b100.Time)
 669  	if e != nil {
 670  		t.Fatal(e)
 671  	}
 672  	// Insert coinbase and mark outputs 0 and 2 as credits.
 673  	e = s.InsertTx(ns, cbRec, &b100)
 674  	if e != nil {
 675  		t.Fatal(e)
 676  	}
 677  	e = s.AddCredit(ns, cbRec, &b100, 0, false)
 678  	if e != nil {
 679  		t.Fatal(e)
 680  	}
 681  	e = s.AddCredit(ns, cbRec, &b100, 2, false)
 682  	if e != nil {
 683  		t.Fatal(e)
 684  	}
 685  	coinbaseMaturity := int32(chaincfg.TestNet3Params.CoinbaseMaturity)
 686  	// Balance should be 0 if the coinbase is immature, 50 DUO at and beyond
 687  	// maturity.
 688  	//
 689  	// Outputs when depth is below maturity are never included, no matter
 690  	// the required number of confirmations.  Matured outputs which have
 691  	// greater depth than minConf are still excluded.
 692  	type balTest struct {
 693  		height  int32
 694  		minConf int32
 695  		bal     amt.Amount
 696  	}
 697  	balTests := []balTest{
 698  		// Next block it is still immature
 699  		{
 700  			height:  b100.Height + coinbaseMaturity - 2,
 701  			minConf: 0,
 702  			bal:     0,
 703  		},
 704  		{
 705  			height:  b100.Height + coinbaseMaturity - 2,
 706  			minConf: coinbaseMaturity,
 707  			bal:     0,
 708  		},
 709  		// Next block it matures
 710  		{
 711  			height:  b100.Height + coinbaseMaturity - 1,
 712  			minConf: 0,
 713  			bal:     50e8,
 714  		},
 715  		{
 716  			height:  b100.Height + coinbaseMaturity - 1,
 717  			minConf: 1,
 718  			bal:     50e8,
 719  		},
 720  		{
 721  			height:  b100.Height + coinbaseMaturity - 1,
 722  			minConf: coinbaseMaturity - 1,
 723  			bal:     50e8,
 724  		},
 725  		{
 726  			height:  b100.Height + coinbaseMaturity - 1,
 727  			minConf: coinbaseMaturity,
 728  			bal:     50e8,
 729  		},
 730  		{
 731  			height:  b100.Height + coinbaseMaturity - 1,
 732  			minConf: coinbaseMaturity + 1,
 733  			bal:     0,
 734  		},
 735  		// Matures at this block
 736  		{
 737  			height:  b100.Height + coinbaseMaturity,
 738  			minConf: 0,
 739  			bal:     50e8,
 740  		},
 741  		{
 742  			height:  b100.Height + coinbaseMaturity,
 743  			minConf: 1,
 744  			bal:     50e8,
 745  		},
 746  		{
 747  			height:  b100.Height + coinbaseMaturity,
 748  			minConf: coinbaseMaturity,
 749  			bal:     50e8,
 750  		},
 751  		{
 752  			height:  b100.Height + coinbaseMaturity,
 753  			minConf: coinbaseMaturity + 1,
 754  			bal:     50e8,
 755  		},
 756  		{
 757  			height:  b100.Height + coinbaseMaturity,
 758  			minConf: coinbaseMaturity + 2,
 759  			bal:     0,
 760  		},
 761  	}
 762  	for i, tst := range balTests {
 763  		var bal amt.Amount
 764  		bal, e = s.Balance(ns, tst.minConf, tst.height)
 765  		if e != nil {
 766  			t.Fatalf("Balance test %d: Store.Balance failed: %v", i, e)
 767  		}
 768  		if bal != tst.bal {
 769  			t.Errorf("Balance test %d: Got %v Expected %v", i, bal, tst.bal)
 770  		}
 771  	}
 772  	if t.Failed() {
 773  		t.Fatal("Failed balance checks after inserting coinbase")
 774  	}
 775  	// Spend an output from the coinbase tx in an unmined transaction when
 776  	// the next block will mature the coinbase.
 777  	spenderATime := time.Now()
 778  	spenderA := spendOutput(&cbRec.Hash, 0, 5e8, 15e8)
 779  	spenderARec, e := NewTxRecordFromMsgTx(spenderA, spenderATime)
 780  	if e != nil {
 781  		t.Fatal(e)
 782  	}
 783  	e = s.InsertTx(ns, spenderARec, nil)
 784  	if e != nil {
 785  		t.Fatal(e)
 786  	}
 787  	e = s.AddCredit(ns, spenderARec, nil, 0, false)
 788  	if e != nil {
 789  		t.Fatal(e)
 790  	}
 791  	balTests = []balTest{
 792  		// Next block it matures
 793  		{
 794  			height:  b100.Height + coinbaseMaturity - 1,
 795  			minConf: 0,
 796  			bal:     35e8,
 797  		},
 798  		{
 799  			height:  b100.Height + coinbaseMaturity - 1,
 800  			minConf: 1,
 801  			bal:     30e8,
 802  		},
 803  		{
 804  			height:  b100.Height + coinbaseMaturity - 1,
 805  			minConf: coinbaseMaturity,
 806  			bal:     30e8,
 807  		},
 808  		{
 809  			height:  b100.Height + coinbaseMaturity - 1,
 810  			minConf: coinbaseMaturity + 1,
 811  			bal:     0,
 812  		},
 813  		// Matures at this block
 814  		{
 815  			height:  b100.Height + coinbaseMaturity,
 816  			minConf: 0,
 817  			bal:     35e8,
 818  		},
 819  		{
 820  			height:  b100.Height + coinbaseMaturity,
 821  			minConf: 1,
 822  			bal:     30e8,
 823  		},
 824  		{
 825  			height:  b100.Height + coinbaseMaturity,
 826  			minConf: coinbaseMaturity,
 827  			bal:     30e8,
 828  		},
 829  		{
 830  			height:  b100.Height + coinbaseMaturity,
 831  			minConf: coinbaseMaturity + 1,
 832  			bal:     30e8,
 833  		},
 834  		{
 835  			height:  b100.Height + coinbaseMaturity,
 836  			minConf: coinbaseMaturity + 2,
 837  			bal:     0,
 838  		},
 839  	}
 840  	balTestsBeforeMaturity := balTests
 841  	for i, tst := range balTests {
 842  		var bal amt.Amount
 843  		bal, e = s.Balance(ns, tst.minConf, tst.height)
 844  		if e != nil {
 845  			t.Fatalf("Balance test %d: Store.Balance failed: %v", i, e)
 846  		}
 847  		if bal != tst.bal {
 848  			t.Errorf("Balance test %d: Got %v Expected %v", i, bal, tst.bal)
 849  		}
 850  	}
 851  	if t.Failed() {
 852  		t.Fatal("Failed balance checks after spending coinbase with unmined transaction")
 853  	}
 854  	// Mine the spending transaction in the block the coinbase matures.
 855  	bMaturity := BlockMeta{
 856  		Block: Block{Height: b100.Height + coinbaseMaturity},
 857  		Time:  time.Now(),
 858  	}
 859  	e = s.InsertTx(ns, spenderARec, &bMaturity)
 860  	if e != nil {
 861  		t.Fatal(e)
 862  	}
 863  	balTests = []balTest{
 864  		// Maturity height
 865  		{
 866  			height:  bMaturity.Height,
 867  			minConf: 0,
 868  			bal:     35e8,
 869  		},
 870  		{
 871  			height:  bMaturity.Height,
 872  			minConf: 1,
 873  			bal:     35e8,
 874  		},
 875  		{
 876  			height:  bMaturity.Height,
 877  			minConf: 2,
 878  			bal:     30e8,
 879  		},
 880  		{
 881  			height:  bMaturity.Height,
 882  			minConf: coinbaseMaturity,
 883  			bal:     30e8,
 884  		},
 885  		{
 886  			height:  bMaturity.Height,
 887  			minConf: coinbaseMaturity + 1,
 888  			bal:     30e8,
 889  		},
 890  		{
 891  			height:  bMaturity.Height,
 892  			minConf: coinbaseMaturity + 2,
 893  			bal:     0,
 894  		},
 895  		// Next block after maturity height
 896  		{
 897  			height:  bMaturity.Height + 1,
 898  			minConf: 0,
 899  			bal:     35e8,
 900  		},
 901  		{
 902  			height:  bMaturity.Height + 1,
 903  			minConf: 2,
 904  			bal:     35e8,
 905  		},
 906  		{
 907  			height:  bMaturity.Height + 1,
 908  			minConf: 3,
 909  			bal:     30e8,
 910  		},
 911  		{
 912  			height:  bMaturity.Height + 1,
 913  			minConf: coinbaseMaturity + 2,
 914  			bal:     30e8,
 915  		},
 916  		{
 917  			height:  bMaturity.Height + 1,
 918  			minConf: coinbaseMaturity + 3,
 919  			bal:     0,
 920  		},
 921  	}
 922  	var bal amt.Amount
 923  	for i, tst := range balTests {
 924  		bal, e = s.Balance(ns, tst.minConf, tst.height)
 925  		if e != nil {
 926  			t.Fatalf("Balance test %d: Store.Balance failed: %v", i, e)
 927  		}
 928  		if bal != tst.bal {
 929  			t.Errorf("Balance test %d: Got %v Expected %v", i, bal, tst.bal)
 930  		}
 931  	}
 932  	if t.Failed() {
 933  		t.Fatal("Failed balance checks mining coinbase spending transaction")
 934  	}
 935  	// Create another spending transaction which spends the credit from the
 936  	// first spender.  This will be used to test removing the entire
 937  	// conflict chain when the coinbase is later reorged out.
 938  	//
 939  	// Use the same output amount as spender A and mark it as a credit.
 940  	// This will mean the balance tests should report identical results.
 941  	spenderBTime := time.Now()
 942  	spenderB := spendOutput(&spenderARec.Hash, 0, 5e8)
 943  	spenderBRec, e := NewTxRecordFromMsgTx(spenderB, spenderBTime)
 944  	if e != nil {
 945  		t.Fatal(e)
 946  	}
 947  	e = s.InsertTx(ns, spenderBRec, &bMaturity)
 948  	if e != nil {
 949  		t.Fatal(e)
 950  	}
 951  	e = s.AddCredit(ns, spenderBRec, &bMaturity, 0, false)
 952  	if e != nil {
 953  		t.Fatal(e)
 954  	}
 955  	for i, tst := range balTests {
 956  		bal, e = s.Balance(ns, tst.minConf, tst.height)
 957  		if e != nil {
 958  			t.Fatalf("Balance test %d: Store.Balance failed: %v", i, e)
 959  		}
 960  		if bal != tst.bal {
 961  			t.Errorf("Balance test %d: Got %v Expected %v", i, bal, tst.bal)
 962  		}
 963  	}
 964  	if t.Failed() {
 965  		t.Fatal("Failed balance checks mining second spending transaction")
 966  	}
 967  	// Reorg out the block that matured the coinbase and check balances
 968  	// again.
 969  	e = s.Rollback(ns, bMaturity.Height)
 970  	if e != nil {
 971  		t.Fatal(e)
 972  	}
 973  	balTests = balTestsBeforeMaturity
 974  	for i, tst := range balTests {
 975  		bal, e = s.Balance(ns, tst.minConf, tst.height)
 976  		if e != nil {
 977  			t.Fatalf("Balance test %d: Store.Balance failed: %v", i, e)
 978  		}
 979  		if bal != tst.bal {
 980  			t.Errorf("Balance test %d: Got %v Expected %v", i, bal, tst.bal)
 981  		}
 982  	}
 983  	if t.Failed() {
 984  		t.Fatal("Failed balance checks after reorging maturity block")
 985  	}
 986  	// Reorg out the block which contained the coinbase.  There should be no
 987  	// more transactions in the store (since the previous outputs referenced
 988  	// by the spending tx no longer exist), and the balance will always be
 989  	// zero.
 990  	e = s.Rollback(ns, b100.Height)
 991  	if e != nil {
 992  		t.Fatal(e)
 993  	}
 994  	balTests = []balTest{
 995  		// Current height
 996  		{
 997  			height:  b100.Height - 1,
 998  			minConf: 0,
 999  			bal:     0,
1000  		},
1001  		{
1002  			height:  b100.Height - 1,
1003  			minConf: 1,
1004  			bal:     0,
1005  		},
1006  		// Next height
1007  		{
1008  			height:  b100.Height,
1009  			minConf: 0,
1010  			bal:     0,
1011  		},
1012  		{
1013  			height:  b100.Height,
1014  			minConf: 1,
1015  			bal:     0,
1016  		},
1017  	}
1018  	for i, tst := range balTests {
1019  		bal, e = s.Balance(ns, tst.minConf, tst.height)
1020  		if e != nil {
1021  			t.Fatalf("Balance test %d: Store.Balance failed: %v", i, e)
1022  		}
1023  		if bal != tst.bal {
1024  			t.Errorf("Balance test %d: Got %v Expected %v", i, bal, tst.bal)
1025  		}
1026  	}
1027  	if t.Failed() {
1028  		t.Fatal("Failed balance checks after reorging coinbase block")
1029  	}
1030  	unminedTxs, e := s.UnminedTxs(ns)
1031  	if e != nil {
1032  		t.Fatal(e)
1033  	}
1034  	if len(unminedTxs) != 0 {
1035  		t.Fatalf("Should have no unmined transactions after coinbase reorg, found %d", len(unminedTxs))
1036  	}
1037  }
1038  
1039  // Test moving multiple transactions from unmined buckets to the same block.
1040  func TestMoveMultipleToSameBlock(t *testing.T) {
1041  	t.Parallel()
1042  	s, db, teardown, e := testStore()
1043  	if e != nil {
1044  		t.Fatal(e)
1045  	}
1046  	defer teardown()
1047  	dbtx, e := db.BeginReadWriteTx()
1048  	if e != nil {
1049  		t.Fatal(e)
1050  	}
1051  	defer func() {
1052  		e = dbtx.Commit()
1053  		if e != nil {
1054  			t.Log(e)
1055  		}
1056  	}()
1057  	ns := dbtx.ReadWriteBucket(namespaceKey)
1058  	b100 := BlockMeta{
1059  		Block: Block{Height: 100},
1060  		Time:  time.Now(),
1061  	}
1062  	cb := newCoinBase(20e8, 30e8)
1063  	cbRec, e := NewTxRecordFromMsgTx(cb, b100.Time)
1064  	if e != nil {
1065  		t.Fatal(e)
1066  	}
1067  	// Insert coinbase and mark both outputs as credits.
1068  	e = s.InsertTx(ns, cbRec, &b100)
1069  	if e != nil {
1070  		t.Fatal(e)
1071  	}
1072  	e = s.AddCredit(ns, cbRec, &b100, 0, false)
1073  	if e != nil {
1074  		t.Fatal(e)
1075  	}
1076  	e = s.AddCredit(ns, cbRec, &b100, 1, false)
1077  	if e != nil {
1078  		t.Fatal(e)
1079  	}
1080  	// Create and insert two unmined transactions which spend both coinbase
1081  	// outputs.
1082  	spenderATime := time.Now()
1083  	spenderA := spendOutput(&cbRec.Hash, 0, 1e8, 2e8, 18e8)
1084  	spenderARec, e := NewTxRecordFromMsgTx(spenderA, spenderATime)
1085  	if e != nil {
1086  		t.Fatal(e)
1087  	}
1088  	e = s.InsertTx(ns, spenderARec, nil)
1089  	if e != nil {
1090  		t.Fatal(e)
1091  	}
1092  	e = s.AddCredit(ns, spenderARec, nil, 0, false)
1093  	if e != nil {
1094  		t.Fatal(e)
1095  	}
1096  	e = s.AddCredit(ns, spenderARec, nil, 1, false)
1097  	if e != nil {
1098  		t.Fatal(e)
1099  	}
1100  	spenderBTime := time.Now()
1101  	spenderB := spendOutput(&cbRec.Hash, 1, 4e8, 8e8, 18e8)
1102  	spenderBRec, e := NewTxRecordFromMsgTx(spenderB, spenderBTime)
1103  	if e != nil {
1104  		t.Fatal(e)
1105  	}
1106  	e = s.InsertTx(ns, spenderBRec, nil)
1107  	if e != nil {
1108  		t.Fatal(e)
1109  	}
1110  	e = s.AddCredit(ns, spenderBRec, nil, 0, false)
1111  	if e != nil {
1112  		t.Fatal(e)
1113  	}
1114  	e = s.AddCredit(ns, spenderBRec, nil, 1, false)
1115  	if e != nil {
1116  		t.Fatal(e)
1117  	}
1118  	coinbaseMaturity := int32(chaincfg.TestNet3Params.CoinbaseMaturity)
1119  	// Mine both transactions in the block that matures the coinbase.
1120  	bMaturity := BlockMeta{
1121  		Block: Block{Height: b100.Height + coinbaseMaturity},
1122  		Time:  time.Now(),
1123  	}
1124  	e = s.InsertTx(ns, spenderARec, &bMaturity)
1125  	if e != nil {
1126  		t.Fatal(e)
1127  	}
1128  	e = s.InsertTx(ns, spenderBRec, &bMaturity)
1129  	if e != nil {
1130  		t.Fatal(e)
1131  	}
1132  	// Chk that both transactions can be queried at the maturity block.
1133  	detailsA, e := s.UniqueTxDetails(ns, &spenderARec.Hash, &bMaturity.Block)
1134  	if e != nil {
1135  		t.Fatal(e)
1136  	}
1137  	if detailsA == nil {
1138  		t.Fatal("No details found for first spender")
1139  	}
1140  	detailsB, e := s.UniqueTxDetails(ns, &spenderBRec.Hash, &bMaturity.Block)
1141  	if e != nil {
1142  		t.Fatal(e)
1143  	}
1144  	if detailsB == nil {
1145  		t.Fatal("No details found for second spender")
1146  	}
1147  	// Verify that the balance was correctly updated on the block record
1148  	// append and that no unmined transactions remain.
1149  	balTests := []struct {
1150  		height  int32
1151  		minConf int32
1152  		bal     amt.Amount
1153  	}{
1154  		// Maturity height
1155  		{
1156  			height:  bMaturity.Height,
1157  			minConf: 0,
1158  			bal:     15e8,
1159  		},
1160  		{
1161  			height:  bMaturity.Height,
1162  			minConf: 1,
1163  			bal:     15e8,
1164  		},
1165  		{
1166  			height:  bMaturity.Height,
1167  			minConf: 2,
1168  			bal:     0,
1169  		},
1170  		// Next block after maturity height
1171  		{
1172  			height:  bMaturity.Height + 1,
1173  			minConf: 0,
1174  			bal:     15e8,
1175  		},
1176  		{
1177  			height:  bMaturity.Height + 1,
1178  			minConf: 2,
1179  			bal:     15e8,
1180  		},
1181  		{
1182  			height:  bMaturity.Height + 1,
1183  			minConf: 3,
1184  			bal:     0,
1185  		},
1186  	}
1187  	for i, tst := range balTests {
1188  		var bal amt.Amount
1189  		bal, e = s.Balance(ns, tst.minConf, tst.height)
1190  		if e != nil {
1191  			t.Fatalf("Balance test %d: Store.Balance failed: %v", i, e)
1192  		}
1193  		if bal != tst.bal {
1194  			t.Errorf("Balance test %d: Got %v Expected %v", i, bal, tst.bal)
1195  		}
1196  	}
1197  	if t.Failed() {
1198  		t.Fatal("Failed balance checks after moving both coinbase spenders")
1199  	}
1200  	unminedTxs, e := s.UnminedTxs(ns)
1201  	if e != nil {
1202  		t.Fatal(e)
1203  	}
1204  	if len(unminedTxs) != 0 {
1205  		t.Fatalf("Should have no unmined transactions mining both, found %d", len(unminedTxs))
1206  	}
1207  }
1208  
1209  // Test the optional-ness of the serialized transaction in a TxRecord.
1210  // NewTxRecord and NewTxRecordFromMsgTx both save the serialized transaction, so
1211  // manually strip it out to test this code path.
1212  func TestInsertUnserializedTx(t *testing.T) {
1213  	t.Parallel()
1214  	s, db, teardown, e := testStore()
1215  	if e != nil {
1216  		t.Fatal(e)
1217  	}
1218  	defer teardown()
1219  	dbtx, e := db.BeginReadWriteTx()
1220  	if e != nil {
1221  		t.Fatal(e)
1222  	}
1223  	defer func() {
1224  		e = dbtx.Commit()
1225  		if e != nil {
1226  			t.Log(e)
1227  		}
1228  	}()
1229  	ns := dbtx.ReadWriteBucket(namespaceKey)
1230  	tx := newCoinBase(50e8)
1231  	rec, e := NewTxRecordFromMsgTx(tx, timeNow())
1232  	if e != nil {
1233  		t.Fatal(e)
1234  	}
1235  	b100 := makeBlockMeta(100)
1236  	e = s.InsertTx(ns, stripSerializedTx(rec), &b100)
1237  	if e != nil {
1238  		t.Fatalf("Insert for stripped TxRecord failed: %v", e)
1239  	}
1240  	// Ensure it can be retreived successfully.
1241  	details, e := s.UniqueTxDetails(ns, &rec.Hash, &b100.Block)
1242  	if e != nil {
1243  		t.Fatal(e)
1244  	}
1245  	rec2, e := NewTxRecordFromMsgTx(&details.MsgTx, rec.Received)
1246  	if e != nil {
1247  		t.Fatal(e)
1248  	}
1249  	if !bytes.Equal(rec.SerializedTx, rec2.SerializedTx) {
1250  		t.Fatal("Serialized txs for coinbase do not match")
1251  	}
1252  	// Now test that path with an unmined transaction.
1253  	tx = spendOutput(&rec.Hash, 0, 50e8)
1254  	rec, e = NewTxRecordFromMsgTx(tx, timeNow())
1255  	if e != nil {
1256  		t.Fatal(e)
1257  	}
1258  	e = s.InsertTx(ns, rec, nil)
1259  	if e != nil {
1260  		t.Fatal(e)
1261  	}
1262  	details, e = s.UniqueTxDetails(ns, &rec.Hash, nil)
1263  	if e != nil {
1264  		t.Fatal(e)
1265  	}
1266  	rec2, e = NewTxRecordFromMsgTx(&details.MsgTx, rec.Received)
1267  	if e != nil {
1268  		t.Fatal(e)
1269  	}
1270  	if !bytes.Equal(rec.SerializedTx, rec2.SerializedTx) {
1271  		t.Fatal("Serialized txs for coinbase spender do not match")
1272  	}
1273  }
1274  
1275  // TestRemoveUnminedTx tests that if we add an umined transaction, then we're
1276  // able to remove that unmined transaction later along with any of its
1277  // descendants. Any balance modifications due to the unmined transaction should
1278  // be revered.
1279  func TestRemoveUnminedTx(t *testing.T) {
1280  	t.Parallel()
1281  	store, db, teardown, e := testStore()
1282  	if e != nil {
1283  		t.Fatal(e)
1284  	}
1285  	defer teardown()
1286  	// In order to reproduce real-world scenarios, we'll use a new database transaction for each interaction with the
1287  	// wallet.
1288  	//
1289  	// We'll start off the test by creating a new coinbase output at height 100 and inserting it into the store.
1290  	b100 := &BlockMeta{
1291  		Block: Block{Height: 100},
1292  		Time:  time.Now(),
1293  	}
1294  	initialBalance := int64(1e8)
1295  	cb := newCoinBase(initialBalance)
1296  	cbRec, e := NewTxRecordFromMsgTx(cb, b100.Time)
1297  	if e != nil {
1298  		t.Fatal(e)
1299  	}
1300  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1301  		if e = store.InsertTx(ns, cbRec, b100); E.Chk(e) {
1302  			t.Fatal(e)
1303  		}
1304  		e = store.AddCredit(ns, cbRec, b100, 0, false)
1305  		if e != nil {
1306  			t.Fatal(e)
1307  		}
1308  	},
1309  	)
1310  	// Determine the maturity height for the coinbase output created.
1311  	coinbaseMaturity := int32(chaincfg.TestNet3Params.CoinbaseMaturity)
1312  	maturityHeight := b100.Block.Height + coinbaseMaturity
1313  	// checkBalance is a helper function that compares the balance of the store with the expected value. The
1314  	// includeUnconfirmed boolean can be used to include the unconfirmed balance as a part of the total balance.
1315  	checkBalance := func(expectedBalance amt.Amount,
1316  		includeUnconfirmed bool,
1317  	) {
1318  		t.Helper()
1319  		minConfs := int32(1)
1320  		if includeUnconfirmed {
1321  			minConfs = 0
1322  		}
1323  		commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1324  			t.Helper()
1325  			var b amt.Amount
1326  			b, e = store.Balance(ns, minConfs, maturityHeight)
1327  			if e != nil {
1328  				t.Fatalf("unable to retrieve balance: %v", e)
1329  			}
1330  			if b != expectedBalance {
1331  				t.Fatalf("expected balance of %d, got %d",
1332  					expectedBalance, b,
1333  				)
1334  			}
1335  		},
1336  		)
1337  	}
1338  	// Since we don't have any unconfirmed transactions within the store, the total balance reflecting confirmed and
1339  	// unconfirmed outputs should match the initial balance.
1340  	checkBalance(amt.Amount(initialBalance), false)
1341  	checkBalance(amt.Amount(initialBalance), true)
1342  	// Then, we'll create an unconfirmed spend for the coinbase output and insert it into the store.
1343  	b101 := &BlockMeta{
1344  		Block: Block{Height: 201},
1345  		Time:  time.Now(),
1346  	}
1347  	changeAmount := int64(4e7)
1348  	spendTx := spendOutput(&cbRec.Hash, 0, 5e7, changeAmount)
1349  	spendTxRec, e := NewTxRecordFromMsgTx(spendTx, b101.Time)
1350  	if e != nil {
1351  		t.Fatal(e)
1352  	}
1353  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1354  		if e := store.InsertTx(ns, spendTxRec, nil); E.Chk(e) {
1355  			t.Fatal(e)
1356  		}
1357  		e := store.AddCredit(ns, spendTxRec, nil, 1, true)
1358  		if e != nil {
1359  			t.Fatal(e)
1360  		}
1361  	},
1362  	)
1363  	// With the unconfirmed spend inserted into the store, we'll query it for its unconfirmed tranasctions to ensure it
1364  	// was properly added.
1365  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1366  		unminedTxs, e := store.UnminedTxs(ns)
1367  		if e != nil {
1368  			t.Fatalf("unable to query for unmined txs: %v", e)
1369  		}
1370  		if len(unminedTxs) != 1 {
1371  			t.Fatalf("expected 1 mined tx, instead got %v",
1372  				len(unminedTxs),
1373  			)
1374  		}
1375  		unminedTxHash := unminedTxs[0].TxHash()
1376  		spendTxHash := spendTx.TxHash()
1377  		if !unminedTxHash.IsEqual(&spendTxHash) {
1378  			t.Fatalf("mismatch tx hashes: expected %v, got %v",
1379  				spendTxHash, unminedTxHash,
1380  			)
1381  		}
1382  	},
1383  	)
1384  	// Now that an unconfirmed spend exists, there should no longer be any confirmed balance. The total balance should
1385  	// now all be unconfirmed and it should match the change amount of the unconfirmed spend tranasction.
1386  	checkBalance(0, false)
1387  	checkBalance(amt.Amount(changeAmount), true)
1388  	// Now, we'll remove the unconfirmed spend tranaction from the store.
1389  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1390  		if e := store.RemoveUnminedTx(ns, spendTxRec); E.Chk(e) {
1391  			t.Fatal(e)
1392  		}
1393  	},
1394  	)
1395  	// We'll query the store one last time for its unconfirmed transactions to ensure the unconfirmed spend was properly
1396  	// removed above.
1397  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1398  		unminedTxs, e := store.UnminedTxs(ns)
1399  		if e != nil {
1400  			t.Fatalf("unable to query for unmined txs: %v", e)
1401  		}
1402  		if len(unminedTxs) != 0 {
1403  			t.Fatalf("expected 0 mined txs, instead got %v",
1404  				len(unminedTxs),
1405  			)
1406  		}
1407  	},
1408  	)
1409  	// Finally, the total balance (including confirmed and unconfirmed) should once again match the initial balance, as
1410  	// the uncofirmed spend has already been removed.
1411  	checkBalance(amt.Amount(initialBalance), false)
1412  	checkBalance(amt.Amount(initialBalance), true)
1413  }
1414  
1415  // commitDBTx is a helper function that allows us to perform multiple operations on a specific database's bucket as a
1416  // single atomic operation.
1417  func commitDBTx(t *testing.T, store *Store, db walletdb.DB,
1418  	f func(walletdb.ReadWriteBucket),
1419  ) {
1420  	t.Helper()
1421  	dbTx, e := db.BeginReadWriteTx()
1422  	if e != nil {
1423  		t.Fatal(e)
1424  	}
1425  	defer func() {
1426  		e := dbTx.Commit()
1427  		if e != nil {
1428  			t.Log(e)
1429  		}
1430  	}()
1431  	ns := dbTx.ReadWriteBucket(namespaceKey)
1432  	f(ns)
1433  }
1434  
1435  // testInsertDoubleSpendTx is a helper test which double spends an output. The boolean parameter indicates whether the
1436  // first spending transaction should be the one confirmed. This test ensures that when a double spend occurs and both
1437  // spending transactions are present in the mempool, if one of them confirms, then the remaining conflicting
1438  // transactions within the mempool should be removed from the wallet's store.
1439  func testInsertMempoolDoubleSpendTx(t *testing.T, first bool) {
1440  	store, db, teardown, e := testStore()
1441  	if e != nil {
1442  		t.Fatal(e)
1443  	}
1444  	defer teardown()
1445  	// In order to reproduce real-world scenarios, we'll use a new database transaction for each interaction with the
1446  	// wallet.
1447  	//
1448  	// We'll start off the test by creating a new coinbase output at height 100 and inserting it into the store.
1449  	b100 := BlockMeta{
1450  		Block: Block{Height: 100},
1451  		Time:  time.Now(),
1452  	}
1453  	cb := newCoinBase(1e8)
1454  	cbRec, e := NewTxRecordFromMsgTx(cb, b100.Time)
1455  	if e != nil {
1456  		t.Fatal(e)
1457  	}
1458  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1459  		if e = store.InsertTx(ns, cbRec, &b100); E.Chk(e) {
1460  			t.Fatal(e)
1461  		}
1462  		e = store.AddCredit(ns, cbRec, &b100, 0, false)
1463  		if e != nil {
1464  			t.Fatal(e)
1465  		}
1466  	},
1467  	)
1468  	// Then, we'll create two spends from the same coinbase output, in order to replicate a double spend scenario.
1469  	firstSpend := spendOutput(&cbRec.Hash, 0, 5e7, 5e7)
1470  	firstSpendRec, e := NewTxRecordFromMsgTx(firstSpend, time.Now())
1471  	if e != nil {
1472  		t.Fatal(e)
1473  	}
1474  	secondSpend := spendOutput(&cbRec.Hash, 0, 4e7, 6e7)
1475  	secondSpendRec, e := NewTxRecordFromMsgTx(secondSpend, time.Now())
1476  	if e != nil {
1477  		t.Fatal(e)
1478  	}
1479  	// We'll insert both of them into the store without confirming them.
1480  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1481  		if e := store.InsertTx(ns, firstSpendRec, nil); E.Chk(e) {
1482  			t.Fatal(e)
1483  		}
1484  		e := store.AddCredit(ns, firstSpendRec, nil, 0, false)
1485  		if e != nil {
1486  			t.Fatal(e)
1487  		}
1488  	},
1489  	)
1490  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1491  		if e := store.InsertTx(ns, secondSpendRec, nil); E.Chk(e) {
1492  			t.Fatal(e)
1493  		}
1494  		e := store.AddCredit(ns, secondSpendRec, nil, 0, false)
1495  		if e != nil {
1496  			t.Fatal(e)
1497  		}
1498  	},
1499  	)
1500  	// Ensure that both spends are found within the unconfirmed transactions in the wallet's store.
1501  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1502  		unminedTxs, e := store.UnminedTxs(ns)
1503  		if e != nil {
1504  			t.Fatal(e)
1505  		}
1506  		if len(unminedTxs) != 2 {
1507  			t.Fatalf("expected 2 unmined txs, got %v",
1508  				len(unminedTxs),
1509  			)
1510  		}
1511  	},
1512  	)
1513  	// Then, we'll confirm either the first or second spend, depending on the boolean passed, with a height deep enough
1514  	// that allows us to succesfully spend the coinbase output.
1515  	coinbaseMaturity := int32(chaincfg.TestNet3Params.CoinbaseMaturity)
1516  	bMaturity := BlockMeta{
1517  		Block: Block{Height: b100.Height + coinbaseMaturity},
1518  		Time:  time.Now(),
1519  	}
1520  	var confirmedSpendRec *TxRecord
1521  	if first {
1522  		confirmedSpendRec = firstSpendRec
1523  	} else {
1524  		confirmedSpendRec = secondSpendRec
1525  	}
1526  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1527  		e := store.InsertTx(ns, confirmedSpendRec, &bMaturity)
1528  		if e != nil {
1529  			t.Fatal(e)
1530  		}
1531  		e = store.AddCredit(
1532  			ns, confirmedSpendRec, &bMaturity, 0, false,
1533  		)
1534  		if e != nil {
1535  			t.Fatal(e)
1536  		}
1537  	},
1538  	)
1539  	// This should now trigger the store to remove any other pending double spends for this coinbase output, as we've
1540  	// already seen the confirmed one. Therefore, we shouldn't see any other unconfirmed transactions within it. We also
1541  	// ensure that the transaction that confirmed and is now listed as a UTXO within the wallet is the correct one.
1542  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1543  		unminedTxs, e := store.UnminedTxs(ns)
1544  		if e != nil {
1545  			t.Fatal(e)
1546  		}
1547  		if len(unminedTxs) != 0 {
1548  			t.Fatalf("expected 0 unmined txs, got %v",
1549  				len(unminedTxs),
1550  			)
1551  		}
1552  		minedTxs, e := store.UnspentOutputs(ns)
1553  		if e != nil {
1554  			t.Fatal(e)
1555  		}
1556  		if len(minedTxs) != 1 {
1557  			t.Fatalf("expected 1 mined tx, got %v", len(minedTxs))
1558  		}
1559  		if !minedTxs[0].Hash.IsEqual(&confirmedSpendRec.Hash) {
1560  			t.Fatalf("expected confirmed tx hash %v, got %v",
1561  				confirmedSpendRec.Hash, minedTxs[0].Hash,
1562  			)
1563  		}
1564  	},
1565  	)
1566  }
1567  
1568  // TestInsertMempoolDoubleSpendConfirmedFirstTx ensures that when a double spend occurs and both spending transactions
1569  // are present in the mempool, if the first spend seen is confirmed, then the second spend transaction within the
1570  // mempool should be removed from the wallet's store.
1571  func TestInsertMempoolDoubleSpendConfirmedFirstTx(t *testing.T) {
1572  	t.Parallel()
1573  	testInsertMempoolDoubleSpendTx(t, true)
1574  }
1575  
1576  // TestInsertMempoolDoubleSpendConfirmedFirstTx ensures that when a double spend occurs and both spending transactions
1577  // are present in the mempool, if the second spend seen is confirmed, then the first spend transaction within the
1578  // mempool should be removed from the wallet's store.
1579  func TestInsertMempoolDoubleSpendConfirmSecondTx(t *testing.T) {
1580  	t.Parallel()
1581  	testInsertMempoolDoubleSpendTx(t, false)
1582  }
1583  
1584  // TestInsertConfirmedDoubleSpendTx tests that when one or more double spends occur and a spending transaction confirms
1585  // that was not known to the wallet, then the unconfirmed double spends within the mempool should be removed from the
1586  // wallet's store.
1587  func TestInsertConfirmedDoubleSpendTx(t *testing.T) {
1588  	t.Parallel()
1589  	store, db, teardown, e := testStore()
1590  	if e != nil {
1591  		t.Fatal(e)
1592  	}
1593  	defer teardown()
1594  	// In order to reproduce real-world scenarios, we'll use a new database transaction for each interaction with the
1595  	// wallet.
1596  	//
1597  	// We'll start off the test by creating a new coinbase output at height 100 and inserting it into the store.
1598  	b100 := BlockMeta{
1599  		Block: Block{Height: 100},
1600  		Time:  time.Now(),
1601  	}
1602  	cb1 := newCoinBase(1e8)
1603  	cbRec1, e := NewTxRecordFromMsgTx(cb1, b100.Time)
1604  	if e != nil {
1605  		t.Fatal(e)
1606  	}
1607  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1608  		if e = store.InsertTx(ns, cbRec1, &b100); E.Chk(e) {
1609  			t.Fatal(e)
1610  		}
1611  		e = store.AddCredit(ns, cbRec1, &b100, 0, false)
1612  		if e != nil {
1613  			t.Fatal(e)
1614  		}
1615  	},
1616  	)
1617  	// Then, we'll create three spends from the same coinbase output. The first two will remain unconfirmed, while the
1618  	// last should confirm and remove the remaining unconfirmed from the wallet's store.
1619  	firstSpend1 := spendOutput(&cbRec1.Hash, 0, 5e7)
1620  	firstSpendRec1, e := NewTxRecordFromMsgTx(firstSpend1, time.Now())
1621  	if e != nil {
1622  		t.Fatal(e)
1623  	}
1624  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1625  		if e = store.InsertTx(ns, firstSpendRec1, nil); E.Chk(e) {
1626  			t.Fatal(e)
1627  		}
1628  		e = store.AddCredit(ns, firstSpendRec1, nil, 0, false)
1629  		if e != nil {
1630  			t.Fatal(e)
1631  		}
1632  	},
1633  	)
1634  	secondSpend1 := spendOutput(&cbRec1.Hash, 0, 4e7)
1635  	secondSpendRec1, e := NewTxRecordFromMsgTx(secondSpend1, time.Now())
1636  	if e != nil {
1637  		t.Fatal(e)
1638  	}
1639  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1640  		if e = store.InsertTx(ns, secondSpendRec1, nil); E.Chk(e) {
1641  			t.Fatal(e)
1642  		}
1643  		e = store.AddCredit(ns, secondSpendRec1, nil, 0, false)
1644  		if e != nil {
1645  			t.Fatal(e)
1646  		}
1647  	},
1648  	)
1649  	// We'll also create another output and have one unconfirmed and one confirmed spending transaction also spend it.
1650  	cb2 := newCoinBase(2e8)
1651  	cbRec2, e := NewTxRecordFromMsgTx(cb2, b100.Time)
1652  	if e != nil {
1653  		t.Fatal(e)
1654  	}
1655  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1656  		if e = store.InsertTx(ns, cbRec2, &b100); E.Chk(e) {
1657  			t.Fatal(e)
1658  		}
1659  		e = store.AddCredit(ns, cbRec2, &b100, 0, false)
1660  		if e != nil {
1661  			t.Fatal(e)
1662  		}
1663  	},
1664  	)
1665  	firstSpend2 := spendOutput(&cbRec2.Hash, 0, 5e7)
1666  	firstSpendRec2, e := NewTxRecordFromMsgTx(firstSpend2, time.Now())
1667  	if e != nil {
1668  		t.Fatal(e)
1669  	}
1670  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1671  		if e = store.InsertTx(ns, firstSpendRec2, nil); E.Chk(e) {
1672  			t.Fatal(e)
1673  		}
1674  		e = store.AddCredit(ns, firstSpendRec2, nil, 0, false)
1675  		if e != nil {
1676  			t.Fatal(e)
1677  		}
1678  	},
1679  	)
1680  	// At this point, we should see all unconfirmed transactions within the store.
1681  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1682  		var unminedTxs []*wire.MsgTx
1683  		unminedTxs, e = store.UnminedTxs(ns)
1684  		if e != nil {
1685  			t.Fatal(e)
1686  		}
1687  		if len(unminedTxs) != 3 {
1688  			t.Fatalf("expected 3 unmined txs, got %d",
1689  				len(unminedTxs),
1690  			)
1691  		}
1692  	},
1693  	)
1694  	// Then, we'll insert the confirmed spend at a height deep enough that allows us to successfully spend the coinbase
1695  	// outputs.
1696  	coinbaseMaturity := int32(chaincfg.TestNet3Params.CoinbaseMaturity)
1697  	bMaturity := BlockMeta{
1698  		Block: Block{Height: b100.Height + coinbaseMaturity},
1699  		Time:  time.Now(),
1700  	}
1701  	outputsToSpend := []wire.OutPoint{
1702  		{Hash: cbRec1.Hash, Index: 0},
1703  		{Hash: cbRec2.Hash, Index: 0},
1704  	}
1705  	confirmedSpend := spendOutputs(outputsToSpend, 3e7)
1706  	confirmedSpendRec, e := NewTxRecordFromMsgTx(
1707  		confirmedSpend, bMaturity.Time,
1708  	)
1709  	if e != nil {
1710  		t.Fatal(e)
1711  	}
1712  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1713  		e := store.InsertTx(ns, confirmedSpendRec, &bMaturity)
1714  		if e != nil {
1715  			t.Fatal(e)
1716  		}
1717  		e = store.AddCredit(
1718  			ns, confirmedSpendRec, &bMaturity, 0, false,
1719  		)
1720  		if e != nil {
1721  			t.Fatal(e)
1722  		}
1723  	},
1724  	)
1725  	// Now that the confirmed spend exists within the store, we should no longer see the unconfirmed spends within it.
1726  	// We also ensure that the transaction that confirmed and is now listed as a UTXO within the wallet is the correct
1727  	// one.
1728  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1729  		unminedTxs, e := store.UnminedTxs(ns)
1730  		if e != nil {
1731  			t.Fatal(e)
1732  		}
1733  		if len(unminedTxs) != 0 {
1734  			t.Fatalf("expected 0 unmined txs, got %v",
1735  				len(unminedTxs),
1736  			)
1737  		}
1738  		minedTxs, e := store.UnspentOutputs(ns)
1739  		if e != nil {
1740  			t.Fatal(e)
1741  		}
1742  		if len(minedTxs) != 1 {
1743  			t.Fatalf("expected 1 mined tx, got %v", len(minedTxs))
1744  		}
1745  		if !minedTxs[0].Hash.IsEqual(&confirmedSpendRec.Hash) {
1746  			t.Fatalf("expected confirmed tx hash %v, got %v",
1747  				confirmedSpend, minedTxs[0].Hash,
1748  			)
1749  		}
1750  	},
1751  	)
1752  }
1753  
1754  // TestAddDuplicateCreditAfterConfirm aims to test the case where a duplicate unconfirmed credit is added to the store
1755  // after the intial credit has already confirmed. This can lead to outputs being duplicated in the store, which can lead
1756  // to creating double spends when querying the wallet's UTXO set.
1757  func TestAddDuplicateCreditAfterConfirm(t *testing.T) {
1758  	t.Parallel()
1759  	store, db, teardown, e := testStore()
1760  	if e != nil {
1761  		t.Fatal(e)
1762  	}
1763  	defer teardown()
1764  	// In order to reproduce real-world scenarios, we'll use a new database transaction for each interaction with the
1765  	// wallet.
1766  	//
1767  	// We'll start off the test by creating a new coinbase output at height 100 and inserting it into the store.
1768  	b100 := &BlockMeta{
1769  		Block: Block{Height: 100},
1770  		Time:  time.Now(),
1771  	}
1772  	cb := newCoinBase(1e8)
1773  	cbRec, e := NewTxRecordFromMsgTx(cb, b100.Time)
1774  	if e != nil {
1775  		t.Fatal(e)
1776  	}
1777  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1778  		if e = store.InsertTx(ns, cbRec, b100); E.Chk(e) {
1779  			t.Fatal(e)
1780  		}
1781  		e = store.AddCredit(ns, cbRec, b100, 0, false)
1782  		if e != nil {
1783  			t.Fatal(e)
1784  		}
1785  	},
1786  	)
1787  	// We'll confirm that there is one unspent output in the store, which should be the coinbase output created above.
1788  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1789  		var minedTxs []Credit
1790  		minedTxs, e = store.UnspentOutputs(ns)
1791  		if e != nil {
1792  			t.Fatal(e)
1793  		}
1794  		if len(minedTxs) != 1 {
1795  			t.Fatalf("expected 1 mined tx, got %v", len(minedTxs))
1796  		}
1797  		if !minedTxs[0].Hash.IsEqual(&cbRec.Hash) {
1798  			t.Fatalf("expected tx hash %v, got %v", cbRec.Hash,
1799  				minedTxs[0].Hash,
1800  			)
1801  		}
1802  	},
1803  	)
1804  	// Then, we'll create an unconfirmed spend for the coinbase output.
1805  	b101 := &BlockMeta{
1806  		Block: Block{Height: 101},
1807  		Time:  time.Now(),
1808  	}
1809  	spendTx := spendOutput(&cbRec.Hash, 0, 5e7, 4e7)
1810  	spendTxRec, e := NewTxRecordFromMsgTx(spendTx, b101.Time)
1811  	if e != nil {
1812  		t.Fatal(e)
1813  	}
1814  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1815  		if e := store.InsertTx(ns, spendTxRec, nil); E.Chk(e) {
1816  			t.Fatal(e)
1817  		}
1818  		e := store.AddCredit(ns, spendTxRec, nil, 1, true)
1819  		if e != nil {
1820  			t.Fatal(e)
1821  		}
1822  	},
1823  	)
1824  	// Confirm the spending transaction at the next height.
1825  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1826  		if e := store.InsertTx(ns, spendTxRec, b101); E.Chk(e) {
1827  			t.Fatal(e)
1828  		}
1829  		e := store.AddCredit(ns, spendTxRec, b101, 1, true)
1830  		if e != nil {
1831  			t.Fatal(e)
1832  		}
1833  	},
1834  	)
1835  	// We should see one unspent output within the store once again, this time being the change output of the spending
1836  	// transaction.
1837  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1838  		minedTxs, e := store.UnspentOutputs(ns)
1839  		if e != nil {
1840  			t.Fatal(e)
1841  		}
1842  		if len(minedTxs) != 1 {
1843  			t.Fatalf("expected 1 mined txs, got %v", len(minedTxs))
1844  		}
1845  		if !minedTxs[0].Hash.IsEqual(&spendTxRec.Hash) {
1846  			t.Fatalf("expected tx hash %v, got %v", spendTxRec.Hash,
1847  				minedTxs[0].Hash,
1848  			)
1849  		}
1850  	},
1851  	)
1852  	// Now, we'll insert the spending transaction once again, this time as unconfirmed. This can happen if the backend
1853  	// happens to forward an unconfirmed chain.RelevantTx notification to the client even after it has confirmed, which
1854  	// results in us adding it to the store once again.
1855  	//
1856  	// TODO(wilmer): ideally this shouldn't happen, so we should identify the real reason for this.
1857  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1858  		if e := store.InsertTx(ns, spendTxRec, nil); E.Chk(e) {
1859  			t.Fatal(e)
1860  		}
1861  		e := store.AddCredit(ns, spendTxRec, nil, 1, true)
1862  		if e != nil {
1863  			t.Fatal(e)
1864  		}
1865  	},
1866  	)
1867  	// Finally, we'll ensure the change output is still the only unspent output within the store.
1868  	commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) {
1869  		minedTxs, e := store.UnspentOutputs(ns)
1870  		if e != nil {
1871  			t.Fatal(e)
1872  		}
1873  		if len(minedTxs) != 1 {
1874  			t.Fatalf("expected 1 mined txs, got %v", len(minedTxs))
1875  		}
1876  		if !minedTxs[0].Hash.IsEqual(&spendTxRec.Hash) {
1877  			t.Fatalf("expected tx hash %v, got %v", spendTxRec.Hash,
1878  				minedTxs[0].Hash,
1879  			)
1880  		}
1881  	},
1882  	)
1883  }
1884