tx.go raw

   1  package wtxmgr
   2  
   3  import (
   4  	"bytes"
   5  	"github.com/p9c/p9/pkg/amt"
   6  	"github.com/p9c/p9/pkg/chaincfg"
   7  	"time"
   8  	
   9  	"github.com/p9c/p9/pkg/blockchain"
  10  	"github.com/p9c/p9/pkg/chainhash"
  11  	"github.com/p9c/p9/pkg/walletdb"
  12  	"github.com/p9c/p9/pkg/wire"
  13  )
  14  
  15  type (
  16  	// Block contains the minimum amount of data to uniquely identify any block on either the best or side chain.
  17  	Block struct {
  18  		Hash   chainhash.Hash
  19  		Height int32
  20  	}
  21  	// BlockMeta contains the unique identification for a block and any metadata pertaining to the block. At the moment,
  22  	// this additional metadata only includes the block time from the block header.
  23  	BlockMeta struct {
  24  		Block
  25  		Time time.Time
  26  	}
  27  	// blockRecord is an in-memory representation of the block record saved in the database.
  28  	blockRecord struct {
  29  		Block
  30  		Time         time.Time
  31  		transactions []chainhash.Hash
  32  	}
  33  	// incidence records the block hash and blockchain height of a mined transaction. Since a transaction hash alone is
  34  	// not enough to uniquely identify a mined transaction (duplicate transaction hashes are allowed), the incidence is
  35  	// used instead.
  36  	incidence struct {
  37  		txHash chainhash.Hash
  38  		block  Block
  39  	}
  40  	// indexedIncidence records the transaction incidence and an input or output index.
  41  	indexedIncidence struct {
  42  		incidence
  43  		index uint32
  44  	}
  45  	// debit records the debits a transaction record makes from previous wallet transaction credits.
  46  	debit struct {
  47  		txHash chainhash.Hash
  48  		index  uint32
  49  		amount amt.Amount
  50  		spends indexedIncidence
  51  	}
  52  	// credit describes a transaction output which was or is spendable by
  53  	// wallet.
  54  	credit struct {
  55  		outPoint wire.OutPoint
  56  		block    Block
  57  		amount   amt.Amount
  58  		change   bool
  59  		spentBy  indexedIncidence // Index == ^uint32(0) if unspent
  60  	}
  61  	// TxRecord represents a transaction managed by the Store.
  62  	TxRecord struct {
  63  		MsgTx        wire.MsgTx
  64  		Hash         chainhash.Hash
  65  		Received     time.Time
  66  		SerializedTx []byte // Optional: may be nil
  67  	}
  68  	// Credit is the type representing a transaction output which was spent or is still spendable by wallet. A UTXO is
  69  	// an unspent Credit, but not all Credits are UTXOs.
  70  	Credit struct {
  71  		wire.OutPoint
  72  		BlockMeta
  73  		Amount       amt.Amount
  74  		PkScript     []byte
  75  		Received     time.Time
  76  		FromCoinBase bool
  77  	}
  78  	// Store implements a transaction store for storing and managing wallet transactions.
  79  	Store struct {
  80  		chainParams *chaincfg.Params
  81  		// Event callbacks. These execute in the same goroutine as the wtxmgr caller.
  82  		NotifyUnspent func(hash *chainhash.Hash, index uint32)
  83  	}
  84  )
  85  
  86  // NewTxRecord creates a new transaction record that may be inserted into the store. It uses memoization to save the
  87  // transaction hash and the serialized transaction.
  88  func NewTxRecord(serializedTx []byte, received time.Time) (*TxRecord, error) {
  89  	rec := &TxRecord{
  90  		Received:     received,
  91  		SerializedTx: serializedTx,
  92  	}
  93  	e := rec.MsgTx.Deserialize(bytes.NewReader(serializedTx))
  94  	if e != nil {
  95  		str := "failed to deserialize transaction"
  96  		return nil, storeError(ErrInput, str, e)
  97  	}
  98  	copy(rec.Hash[:], chainhash.DoubleHashB(serializedTx))
  99  	return rec, nil
 100  }
 101  
 102  // NewTxRecordFromMsgTx creates a new transaction record that may be inserted into the store.
 103  func NewTxRecordFromMsgTx(msgTx *wire.MsgTx, received time.Time) (*TxRecord, error) {
 104  	buf := bytes.NewBuffer(make([]byte, 0, msgTx.SerializeSize()))
 105  	e := msgTx.Serialize(buf)
 106  	if e != nil {
 107  		str := "failed to serialize transaction"
 108  		return nil, storeError(ErrInput, str, e)
 109  	}
 110  	rec := &TxRecord{
 111  		MsgTx:        *msgTx,
 112  		Received:     received,
 113  		SerializedTx: buf.Bytes(),
 114  		Hash:         msgTx.TxHash(),
 115  	}
 116  	return rec, nil
 117  }
 118  
 119  // DoUpgrades performs any necessary upgrades to the transaction history contained in the wallet database, namespaced by
 120  // the top level bucket key namespaceKey.
 121  func DoUpgrades(db walletdb.DB, namespaceKey []byte) (e error) {
 122  	// No upgrades
 123  	return nil
 124  }
 125  
 126  // Open opens the wallet transaction store from a walletdb namespace.
 127  // If the store does not exist, ErrNoExist is returned.
 128  func Open(ns walletdb.ReadBucket, chainParams *chaincfg.Params) (*Store, error) {
 129  	// Open the store.
 130  	e := openStore(ns)
 131  	if e != nil {
 132  		return nil, e
 133  	}
 134  	s := &Store{chainParams, nil} // TODO: set callbacks
 135  	return s, nil
 136  }
 137  
 138  // Create creates a new persistent transaction store in the walletdb namespace. Creating the store when one already
 139  // exists in this namespace will error with ErrAlreadyExists.
 140  func Create(ns walletdb.ReadWriteBucket) (e error) {
 141  	return createStore(ns)
 142  }
 143  
 144  // updateMinedBalance updates the mined balance within the store, if changed, after processing the given transaction
 145  // record.
 146  func (s *Store) updateMinedBalance(
 147  	ns walletdb.ReadWriteBucket, rec *TxRecord,
 148  	block *BlockMeta,
 149  ) (e error) {
 150  	// Fetch the mined balance in case we need to update it.
 151  	minedBalance, e := fetchMinedBalance(ns)
 152  	if e != nil {
 153  		return e
 154  	}
 155  	// Add a debit record for each unspent credit spent by this transaction. The index is set in each iteration below.
 156  	spender := indexedIncidence{
 157  		incidence: incidence{
 158  			txHash: rec.Hash,
 159  			block:  block.Block,
 160  		},
 161  	}
 162  	newMinedBalance := minedBalance
 163  	for i, input := range rec.MsgTx.TxIn {
 164  		unspentKey, credKey := existsUnspent(ns, &input.PreviousOutPoint)
 165  		if credKey == nil {
 166  			// Debits for unmined transactions are not explicitly tracked. Instead, all previous outputs spent by any
 167  			// unmined transaction are added to a map for quick lookups when it must be checked whether a mined output
 168  			// is unspent or not.
 169  			//
 170  			// Tracking individual debits for unmined transactions could be added later to simplify (and increase
 171  			// performance of) determining some details that need the previous outputs (e.g. determining a fee), but at
 172  			// the moment that is not done (and a db lookup is used for those cases instead).
 173  			//
 174  			// There is also a good chance that all unmined transaction handling will move entirely to the db rather
 175  			// than being handled in memory for atomicity reasons, so the simplist implementation is currently used.
 176  			continue
 177  		}
 178  		// If this output is relevant to us, we'll mark the it as spent and remove its amount from the store.
 179  		spender.index = uint32(i)
 180  		var amount amt.Amount
 181  		amount, e = spendCredit(ns, credKey, &spender)
 182  		if e != nil {
 183  			return e
 184  		}
 185  		e = putDebit(
 186  			ns, &rec.Hash, uint32(i), amount, &block.Block, credKey,
 187  		)
 188  		if e != nil {
 189  			return e
 190  		}
 191  		if e = deleteRawUnspent(ns, unspentKey); E.Chk(e) {
 192  			return e
 193  		}
 194  		newMinedBalance -= amount
 195  	}
 196  	// For each output of the record that is marked as a credit, if the output is marked as a credit by the unconfirmed
 197  	// store, remove the marker and mark the output as a credit in the db.
 198  	//
 199  	// Moved credits are added as unspents, even if there is another unconfirmed transaction which spends them.
 200  	cred := credit{
 201  		outPoint: wire.OutPoint{Hash: rec.Hash},
 202  		block:    block.Block,
 203  		spentBy:  indexedIncidence{index: ^uint32(0)},
 204  	}
 205  	it := makeUnminedCreditIterator(ns, &rec.Hash)
 206  	for it.next() {
 207  		// TODO: This should use the raw apis. The credit value (it.cv) can be moved from unmined directly to the
 208  		//  credits bucket. The key needs a modification to include the block height/hash.
 209  		var index uint32
 210  		index, e = fetchRawUnminedCreditIndex(it.ck)
 211  		if e != nil {
 212  			return e
 213  		}
 214  		amount, change, e := fetchRawUnminedCreditAmountChange(it.cv)
 215  		if e != nil {
 216  			return e
 217  		}
 218  		cred.outPoint.Index = index
 219  		cred.amount = amount
 220  		cred.change = change
 221  		if e = putUnspentCredit(ns, &cred); E.Chk(e) {
 222  			return e
 223  		}
 224  		e = putUnspent(ns, &cred.outPoint, &block.Block)
 225  		if e != nil {
 226  			return e
 227  		}
 228  		newMinedBalance += amount
 229  	}
 230  	if it.err != nil {
 231  		return it.err
 232  	}
 233  	// Update the balance if it has changed.
 234  	if newMinedBalance != minedBalance {
 235  		return putMinedBalance(ns, newMinedBalance)
 236  	}
 237  	return nil
 238  }
 239  
 240  // deleteUnminedTx deletes an unmined transaction from the store.
 241  //
 242  // NOTE: This should only be used once the transaction has been mined.
 243  func (s *Store) deleteUnminedTx(ns walletdb.ReadWriteBucket, rec *TxRecord) (e error) {
 244  	for i := range rec.MsgTx.TxOut {
 245  		k := canonicalOutPoint(&rec.Hash, uint32(i))
 246  		if e := deleteRawUnminedCredit(ns, k); E.Chk(e) {
 247  			return e
 248  		}
 249  	}
 250  	return deleteRawUnmined(ns, rec.Hash[:])
 251  }
 252  
 253  // InsertTx records a transaction as belonging to a wallet's transaction history. If block is nil, the transaction is
 254  // considered unspent, and the transaction's index must be unset.
 255  func (s *Store) InsertTx(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta) (e error) {
 256  	if block == nil {
 257  		return s.insertMemPoolTx(ns, rec)
 258  	}
 259  	return s.insertMinedTx(ns, rec, block)
 260  }
 261  
 262  // RemoveUnminedTx attempts to remove an unmined transaction from the transaction store. This is to be used in the
 263  // scenario that a transaction that we attempt to rebroadcast, turns out to double spend one of our existing inputs.
 264  // This function we remove the conflicting transaction identified by the tx record, and also recursively remove all
 265  // transactions that depend on it.
 266  func (s *Store) RemoveUnminedTx(ns walletdb.ReadWriteBucket, rec *TxRecord) (e error) {
 267  	// As we already have a tx record, we can directly call the RemoveConflict method. This will do the job of
 268  	// recursively removing this unmined transaction, and any transactions that depend on it.
 269  	return RemoveConflict(ns, rec)
 270  }
 271  
 272  // insertMinedTx inserts a new transaction record for a mined transaction into the database under the confirmed bucket.
 273  // It guarantees that, if the tranasction was previously unconfirmed, then it will take care of cleaning up the
 274  // unconfirmed state. All other unconfirmed double spend attempts will be removed as well.
 275  func (s *Store) insertMinedTx(
 276  	ns walletdb.ReadWriteBucket, rec *TxRecord,
 277  	block *BlockMeta,
 278  ) (e error) {
 279  	// If a transaction record for this hash and block already exists, we can exit early.
 280  	if _, v := existsTxRecord(ns, &rec.Hash, &block.Block); v != nil {
 281  		return nil
 282  	}
 283  	// If a block record does not yet exist for any transactions from this block, insert a block record first.
 284  	// Otherwise, update it by adding the transaction hash to the set of transactions from this block.
 285  	blockKey, blockValue := existsBlockRecord(ns, block.Height)
 286  	if blockValue == nil {
 287  		e = putBlockRecord(ns, block, &rec.Hash)
 288  	} else {
 289  		blockValue, e = appendRawBlockRecord(blockValue, &rec.Hash)
 290  		if e != nil {
 291  			return e
 292  		}
 293  		e = putRawBlockRecord(ns, blockKey, blockValue)
 294  	}
 295  	if e != nil {
 296  		return e
 297  	}
 298  	if e := putTxRecord(ns, rec, &block.Block); E.Chk(e) {
 299  		return e
 300  	}
 301  	// Determine if this transaction has affected our balance, and if so, update it.
 302  	if e := s.updateMinedBalance(ns, rec, block); E.Chk(e) {
 303  		return e
 304  	}
 305  	// If this transaction previously existed within the store as unmined, we'll need to remove it from the unmined
 306  	// bucket.
 307  	if v := existsRawUnmined(ns, rec.Hash[:]); v != nil {
 308  		I.F("marking unconfirmed transaction %v mined in block %d", &rec.Hash, block.Height)
 309  		if e := s.deleteUnminedTx(ns, rec); E.Chk(e) {
 310  			return e
 311  		}
 312  	}
 313  	// As there may be unconfirmed transactions that are invalidated by this transaction (either being duplicates, or
 314  	// double spends), remove them from the unconfirmed set. This also handles removing unconfirmed transaction spend
 315  	// chains if any other unconfirmed transactions spend outputs of the removed double spend.
 316  	return s.removeDoubleSpends(ns, rec)
 317  }
 318  
 319  // AddCredit marks a transaction record as containing a transaction output spendable by wallet. The output is added
 320  // unspent, and is marked spent when a new transaction spending the output is inserted into the store.
 321  //
 322  // TODO(jrick): This should not be necessary. Instead, pass the indexes that are known to contain credits when a
 323  //  transaction or merkleblock is inserted into the store.
 324  func (s *Store) AddCredit(
 325  	ns walletdb.ReadWriteBucket,
 326  	rec *TxRecord,
 327  	block *BlockMeta,
 328  	index uint32,
 329  	change bool,
 330  ) (e error) {
 331  	if int(index) >= len(rec.MsgTx.TxOut) {
 332  		str := "transaction output does not exist"
 333  		return storeError(ErrInput, str, nil)
 334  	}
 335  	isNew, e := s.addCredit(ns, rec, block, index, change)
 336  	if e == nil && isNew && s.NotifyUnspent != nil {
 337  		s.NotifyUnspent(&rec.Hash, index)
 338  	}
 339  	return e
 340  }
 341  
 342  // addCredit is an AddCredit helper that runs in an update transaction. The bool return specifies whether the unspent
 343  // output is newly added ( true) or a duplicate (false).
 344  func (s *Store) addCredit(
 345  	ns walletdb.ReadWriteBucket,
 346  	rec *TxRecord,
 347  	block *BlockMeta,
 348  	index uint32,
 349  	change bool,
 350  ) (bool, error) {
 351  	if block == nil {
 352  		// If the outpoint that we should mark as credit already exists within the store, either as unconfirmed or
 353  		// confirmed, then we have nothing left to do and can exit.
 354  		k := canonicalOutPoint(&rec.Hash, index)
 355  		if existsRawUnminedCredit(ns, k) != nil {
 356  			return false, nil
 357  		}
 358  		if existsRawUnspent(ns, k) != nil {
 359  			return false, nil
 360  		}
 361  		v := valueUnminedCredit(amt.Amount(rec.MsgTx.TxOut[index].Value), change)
 362  		return true, putRawUnminedCredit(ns, k, v)
 363  	}
 364  	k, v := existsCredit(ns, &rec.Hash, index, &block.Block)
 365  	if v != nil {
 366  		return false, nil
 367  	}
 368  	txOutAmt := amt.Amount(rec.MsgTx.TxOut[index].Value)
 369  	T.F(
 370  		"marking transaction %v output %d (%v) spendable",
 371  		rec.Hash, index, txOutAmt,
 372  	)
 373  	cred := credit{
 374  		outPoint: wire.OutPoint{
 375  			Hash:  rec.Hash,
 376  			Index: index,
 377  		},
 378  		block:   block.Block,
 379  		amount:  txOutAmt,
 380  		change:  change,
 381  		spentBy: indexedIncidence{index: ^uint32(0)},
 382  	}
 383  	v = valueUnspentCredit(&cred)
 384  	e := putRawCredit(ns, k, v)
 385  	if e != nil {
 386  		return false, e
 387  	}
 388  	minedBalance, e := fetchMinedBalance(ns)
 389  	if e != nil {
 390  		return false, e
 391  	}
 392  	e = putMinedBalance(ns, minedBalance+txOutAmt)
 393  	if e != nil {
 394  		return false, e
 395  	}
 396  	return true, putUnspent(ns, &cred.outPoint, &block.Block)
 397  }
 398  
 399  // Rollback removes all blocks at height onwards, moving any transactions within each block to the unconfirmed pool.
 400  func (s *Store) Rollback(ns walletdb.ReadWriteBucket, height int32) (e error) {
 401  	return s.rollback(ns, height)
 402  }
 403  func (s *Store) rollback(ns walletdb.ReadWriteBucket, height int32) (e error) {
 404  	minedBalance, e := fetchMinedBalance(ns)
 405  	if e != nil {
 406  		return e
 407  	}
 408  	// Keep track of all credits that were removed from coinbase transactions. After detaching all blocks, if any
 409  	// transaction record exists in unmined that spends these outputs, remove them and their spend chains.
 410  	//
 411  	// It is necessary to keep these in memory and fix the unmined transactions later since blocks are removed in
 412  	// increasing order.
 413  	var coinBaseCredits []wire.OutPoint
 414  	var heightsToRemove []int32
 415  	it := makeReverseBlockIterator(ns)
 416  	for it.prev() {
 417  		b := &it.elem
 418  		if it.elem.Height < height {
 419  			break
 420  		}
 421  		heightsToRemove = append(heightsToRemove, it.elem.Height)
 422  		T.F("rolling back %d transactions from block %v height %d", len(b.transactions), b.Hash, b.Height)
 423  		for i := range b.transactions {
 424  			txHash := &b.transactions[i]
 425  			recKey := keyTxRecord(txHash, &b.Block)
 426  			recVal := existsRawTxRecord(ns, recKey)
 427  			var rec TxRecord
 428  			e = readRawTxRecord(txHash, recVal, &rec)
 429  			if e != nil {
 430  				return e
 431  			}
 432  			e = deleteTxRecord(ns, txHash, &b.Block)
 433  			if e != nil {
 434  				return e
 435  			}
 436  			// Handle coinbase transactions specially since they are not moved to the unconfirmed store. A coinbase
 437  			// cannot contain any debits, but all credits should be removed and the mined balance decremented.
 438  			if blockchain.IsCoinBaseTx(&rec.MsgTx) {
 439  				op := wire.OutPoint{Hash: rec.Hash}
 440  				for i, output := range rec.MsgTx.TxOut {
 441  					k, v := existsCredit(
 442  						ns, &rec.Hash,
 443  						uint32(i), &b.Block,
 444  					)
 445  					if v == nil {
 446  						continue
 447  					}
 448  					op.Index = uint32(i)
 449  					coinBaseCredits = append(coinBaseCredits, op)
 450  					unspentKey, credKey := existsUnspent(ns, &op)
 451  					if credKey != nil {
 452  						minedBalance -= amt.Amount(output.Value)
 453  						e = deleteRawUnspent(ns, unspentKey)
 454  						if e != nil {
 455  							return e
 456  						}
 457  					}
 458  					e = deleteRawCredit(ns, k)
 459  					if e != nil {
 460  						return e
 461  					}
 462  				}
 463  				continue
 464  			}
 465  			e = putRawUnmined(ns, txHash[:], recVal)
 466  			if e != nil {
 467  				return e
 468  			}
 469  			// For each debit recorded for this transaction, mark the credit it spends as unspent (as long as it still
 470  			// exists) and delete the debit. The previous output is recorded in the unconfirmed store for every previous
 471  			// output, not just debits.
 472  			for i, input := range rec.MsgTx.TxIn {
 473  				prevOut := &input.PreviousOutPoint
 474  				prevOutKey := canonicalOutPoint(
 475  					&prevOut.Hash,
 476  					prevOut.Index,
 477  				)
 478  				e = putRawUnminedInput(ns, prevOutKey, rec.Hash[:])
 479  				if e != nil {
 480  					return e
 481  				}
 482  				// If this input is a debit, remove the debit record and mark the credit that it spent as unspent,
 483  				// incrementing the mined balance.
 484  				var debKey, credKey []byte
 485  				debKey, credKey, e = existsDebit(
 486  					ns,
 487  					&rec.Hash, uint32(i), &b.Block,
 488  				)
 489  				if e != nil {
 490  					return e
 491  				}
 492  				if debKey == nil {
 493  					continue
 494  				}
 495  				// unspendRawCredit does not error in case the no credit exists for this key, but this behavior is
 496  				// correct. Since blocks are removed in increasing order, this credit may have already been removed from
 497  				// a previously removed transaction record in this rollback.
 498  				var amount amt.Amount
 499  				amount, e = unspendRawCredit(ns, credKey)
 500  				if e != nil {
 501  					return e
 502  				}
 503  				e = deleteRawDebit(ns, debKey)
 504  				if e != nil {
 505  					return e
 506  				}
 507  				// If the credit was previously removed in the rollback, the credit amount is zero. Only mark the
 508  				// previously spent credit as unspent if it still exists.
 509  				if amount == 0 {
 510  					continue
 511  				}
 512  				var unspentVal []byte
 513  				unspentVal, e = fetchRawCreditUnspentValue(credKey)
 514  				if e != nil {
 515  					return e
 516  				}
 517  				minedBalance += amount
 518  				e = putRawUnspent(ns, prevOutKey, unspentVal)
 519  				if e != nil {
 520  					return e
 521  				}
 522  			}
 523  			// For each detached non-coinbase credit, move the credit output to unmined. If the credit is marked
 524  			// unspent, it is removed from the utxo set and the mined balance is decremented.
 525  			//
 526  			// TODO: use a credit iterator
 527  			for i, output := range rec.MsgTx.TxOut {
 528  				k, v := existsCredit(
 529  					ns, &rec.Hash, uint32(i),
 530  					&b.Block,
 531  				)
 532  				if v == nil {
 533  					continue
 534  				}
 535  				var amountChange amt.Amount
 536  				change := false
 537  				amountChange, change, e = fetchRawCreditAmountChange(v)
 538  				if e != nil {
 539  					return e
 540  				}
 541  				outPointKey := canonicalOutPoint(&rec.Hash, uint32(i))
 542  				unminedCredVal := valueUnminedCredit(amountChange, change)
 543  				e = putRawUnminedCredit(ns, outPointKey, unminedCredVal)
 544  				if e != nil {
 545  					return e
 546  				}
 547  				e = deleteRawCredit(ns, k)
 548  				if e != nil {
 549  					return e
 550  				}
 551  				credKey := existsRawUnspent(ns, outPointKey)
 552  				if credKey != nil {
 553  					minedBalance -= amt.Amount(output.Value)
 554  					e = deleteRawUnspent(ns, outPointKey)
 555  					if e != nil {
 556  						return e
 557  					}
 558  				}
 559  			}
 560  		}
 561  		// reposition cursor before deleting this k/v pair and advancing to the previous.
 562  		it.reposition(it.elem.Height)
 563  		// Avoid cursor deletion until bolt issue #620 is resolved.
 564  		//
 565  		// e = it.delete() if e != nil  {
 566  		// 	return e
 567  		// }
 568  	}
 569  	if it.err != nil {
 570  		return it.err
 571  	}
 572  	// Delete the block records outside of the iteration since cursor deletion is broken.
 573  	for _, h := range heightsToRemove {
 574  		e = deleteBlockRecord(ns, h)
 575  		if e != nil {
 576  			return e
 577  		}
 578  	}
 579  	for _, op := range coinBaseCredits {
 580  		opKey := canonicalOutPoint(&op.Hash, op.Index)
 581  		unminedSpendTxHashKeys := fetchUnminedInputSpendTxHashes(ns, opKey)
 582  		for _, unminedSpendTxHashKey := range unminedSpendTxHashKeys {
 583  			unminedVal := existsRawUnmined(ns, unminedSpendTxHashKey[:])
 584  			// If the spending transaction spends multiple outputs
 585  			// from the same transaction, we'll find duplicate
 586  			// entries within the store, so it's possible we're
 587  			// unable to find it if the conflicts have already been
 588  			// removed in a previous iteration.
 589  			if unminedVal == nil {
 590  				continue
 591  			}
 592  			var unminedRec TxRecord
 593  			unminedRec.Hash = unminedSpendTxHashKey
 594  			e = readRawTxRecord(&unminedRec.Hash, unminedVal, &unminedRec)
 595  			if e != nil {
 596  				return e
 597  			}
 598  			D.F(
 599  				"transaction %v spends a removed coinbase output -- removing as well %s",
 600  				unminedRec.Hash,
 601  			)
 602  			e = RemoveConflict(ns, &unminedRec)
 603  			if e != nil {
 604  				return e
 605  			}
 606  		}
 607  	}
 608  	return putMinedBalance(ns, minedBalance)
 609  }
 610  
 611  func // UnspentOutputs returns all unspent received transaction outputs.
 612  // The order is undefined.
 613  (s *Store) UnspentOutputs(ns walletdb.ReadBucket) ([]Credit, error) {
 614  	var unspent []Credit
 615  	var op wire.OutPoint
 616  	var block Block
 617  	e := ns.NestedReadBucket(bucketUnspent).ForEach(
 618  		func(k, v []byte) (e error) {
 619  			e = readCanonicalOutPoint(k, &op)
 620  			if e != nil {
 621  				return e
 622  			}
 623  			if existsRawUnminedInput(ns, k) != nil {
 624  				// Output is spent by an unmined transaction.
 625  				// Skip this k/v pair.
 626  				return nil
 627  			}
 628  			e = readUnspentBlock(v, &block)
 629  			if e != nil {
 630  				return e
 631  			}
 632  			blockTime, e := fetchBlockTime(ns, block.Height)
 633  			if e != nil {
 634  				return e
 635  			}
 636  			// TODO(jrick): reading the entire transaction should
 637  			// be avoidable.  Creating the credit only requires the
 638  			// output amount and pkScript.
 639  			rec, e := fetchTxRecord(ns, &op.Hash, &block)
 640  			if e != nil {
 641  				return e
 642  			}
 643  			txOut := rec.MsgTx.TxOut[op.Index]
 644  			cred := Credit{
 645  				OutPoint: op,
 646  				BlockMeta: BlockMeta{
 647  					Block: block,
 648  					Time:  blockTime,
 649  				},
 650  				Amount:       amt.Amount(txOut.Value),
 651  				PkScript:     txOut.PkScript,
 652  				Received:     rec.Received,
 653  				FromCoinBase: blockchain.IsCoinBaseTx(&rec.MsgTx),
 654  			}
 655  			unspent = append(unspent, cred)
 656  			return nil
 657  		},
 658  	)
 659  	if e != nil {
 660  		if _, ok := e.(TxMgrError); ok {
 661  			return nil, e
 662  		}
 663  		str := "failed iterating unspent bucket"
 664  		return nil, storeError(ErrDatabase, str, e)
 665  	}
 666  	e = ns.NestedReadBucket(bucketUnminedCredits).ForEach(
 667  		func(k, v []byte) (e error) {
 668  			if existsRawUnminedInput(ns, k) != nil {
 669  				// Output is spent by an unmined transaction.
 670  				// Skip to next unmined credit.
 671  				return nil
 672  			}
 673  			e = readCanonicalOutPoint(k, &op)
 674  			if e != nil {
 675  				return e
 676  			}
 677  			// TODO(jrick): Reading/parsing the entire transaction record
 678  			// just for the output amount and script can be avoided.
 679  			recVal := existsRawUnmined(ns, op.Hash[:])
 680  			var rec TxRecord
 681  			e = readRawTxRecord(&op.Hash, recVal, &rec)
 682  			if e != nil {
 683  				return e
 684  			}
 685  			txOut := rec.MsgTx.TxOut[op.Index]
 686  			cred := Credit{
 687  				OutPoint: op,
 688  				BlockMeta: BlockMeta{
 689  					Block: Block{Height: -1},
 690  				},
 691  				Amount:       amt.Amount(txOut.Value),
 692  				PkScript:     txOut.PkScript,
 693  				Received:     rec.Received,
 694  				FromCoinBase: blockchain.IsCoinBaseTx(&rec.MsgTx),
 695  			}
 696  			unspent = append(unspent, cred)
 697  			return nil
 698  		},
 699  	)
 700  	if e != nil {
 701  		if _, ok := e.(TxMgrError); ok {
 702  			return nil, e
 703  		}
 704  		str := "failed iterating unmined credits bucket"
 705  		return nil, storeError(ErrDatabase, str, e)
 706  	}
 707  	return unspent, nil
 708  }
 709  
 710  func // Balance returns the spendable wallet balance (total value of all unspent
 711  // transaction outputs) given a minimum of minConf confirmations, calculated
 712  // at a current chain height of curHeight.  Coinbase outputs are only included
 713  // in the balance if maturity has been reached.
 714  //
 715  // Balance may return unexpected results if syncHeight is lower than the block
 716  // height of the most recent mined transaction in the store.
 717  (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32) (amt.Amount, error) {
 718  	bal, e := fetchMinedBalance(ns)
 719  	if e != nil {
 720  		return 0, e
 721  	}
 722  	// Subtract the balance for each credit that is spent by an unmined
 723  	// transaction.
 724  	var op wire.OutPoint
 725  	var block Block
 726  	e = ns.NestedReadBucket(bucketUnspent).ForEach(
 727  		func(k, v []byte) (e error) {
 728  			e = readCanonicalOutPoint(k, &op)
 729  			if e != nil {
 730  				return e
 731  			}
 732  			e = readUnspentBlock(v, &block)
 733  			if e != nil {
 734  				return e
 735  			}
 736  			if existsRawUnminedInput(ns, k) != nil {
 737  				_, v := existsCredit(ns, &op.Hash, op.Index, &block)
 738  				var amount amt.Amount
 739  				amount, e = fetchRawCreditAmount(v)
 740  				if e != nil {
 741  					return e
 742  				}
 743  				bal -= amount
 744  			}
 745  			return nil
 746  		},
 747  	)
 748  	if e != nil {
 749  		if _, ok := e.(TxMgrError); ok {
 750  			return 0, e
 751  		}
 752  		str := "failed iterating unspent outputs"
 753  		return 0, storeError(ErrDatabase, str, e)
 754  	}
 755  	// Decrement the balance for any unspent credit with less than
 756  	// minConf confirmations and any (unspent) immature coinbase credit.
 757  	coinbaseMaturity := int32(s.chainParams.CoinbaseMaturity)
 758  	stopConf := minConf
 759  	if coinbaseMaturity > stopConf {
 760  		stopConf = coinbaseMaturity
 761  	}
 762  	lastHeight := syncHeight - stopConf
 763  	blockIt := makeReadReverseBlockIterator(ns)
 764  	for blockIt.prev() {
 765  		block := &blockIt.elem
 766  		if block.Height < lastHeight {
 767  			break
 768  		}
 769  		for i := range block.transactions {
 770  			txHash := &block.transactions[i]
 771  			var rec *TxRecord
 772  			rec, e = fetchTxRecord(ns, txHash, &block.Block)
 773  			if e != nil {
 774  				return 0, e
 775  			}
 776  			numOuts := uint32(len(rec.MsgTx.TxOut))
 777  			for i := uint32(0); i < numOuts; i++ {
 778  				// Avoid double decrementing the credit amount
 779  				// if it was already removed for being spent by
 780  				// an unmined tx.
 781  				opKey := canonicalOutPoint(txHash, i)
 782  				if existsRawUnminedInput(ns, opKey) != nil {
 783  					continue
 784  				}
 785  				_, v := existsCredit(ns, txHash, i, &block.Block)
 786  				if v == nil {
 787  					continue
 788  				}
 789  				var amountSpent amt.Amount
 790  				var spent bool
 791  				amountSpent, spent, e = fetchRawCreditAmountSpent(v)
 792  				if e != nil {
 793  					return 0, e
 794  				}
 795  				if spent {
 796  					continue
 797  				}
 798  				confs := syncHeight - block.Height + 1
 799  				if confs < minConf || (blockchain.IsCoinBaseTx(&rec.MsgTx) &&
 800  					confs < coinbaseMaturity) {
 801  					bal -= amountSpent
 802  				}
 803  			}
 804  		}
 805  	}
 806  	if blockIt.err != nil {
 807  		return 0, blockIt.err
 808  	}
 809  	// If unmined outputs are included, increment the balance for each
 810  	// output that is unspent.
 811  	if minConf == 0 {
 812  		e = ns.NestedReadBucket(bucketUnminedCredits).ForEach(
 813  			func(k, v []byte) (e error) {
 814  				if existsRawUnminedInput(ns, k) != nil {
 815  					// Output is spent by an unmined transaction.
 816  					// Skip to next unmined credit.
 817  					return nil
 818  				}
 819  				amount, e := fetchRawUnminedCreditAmount(v)
 820  				if e != nil {
 821  					return e
 822  				}
 823  				bal += amount
 824  				return nil
 825  			},
 826  		)
 827  		if e != nil {
 828  			if _, ok := e.(TxMgrError); ok {
 829  				return 0, e
 830  			}
 831  			str := "failed to iterate over unmined credits bucket"
 832  			return 0, storeError(ErrDatabase, str, e)
 833  		}
 834  	}
 835  	return bal, nil
 836  }
 837