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

     1  // Copyright (c) 2015-2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/lbryio/lbcd/chaincfg/chainhash"
    11  	"github.com/lbryio/lbcd/database"
    12  	"github.com/lbryio/lbcd/txscript"
    13  	"github.com/lbryio/lbcd/wire"
    14  	btcutil "github.com/lbryio/lbcutil"
    15  )
    16  
    17  // txoFlags is a bitmask defining additional information and state for a
    18  // transaction output in a utxo view.
    19  type txoFlags uint8
    20  
    21  const (
    22  	// tfCoinBase indicates that a txout was contained in a coinbase tx.
    23  	tfCoinBase txoFlags = 1 << iota
    24  
    25  	// tfSpent indicates that a txout is spent.
    26  	tfSpent
    27  
    28  	// tfModified indicates that a txout has been modified since it was
    29  	// loaded.
    30  	tfModified
    31  )
    32  
    33  // UtxoEntry houses details about an individual transaction output in a utxo
    34  // view such as whether or not it was contained in a coinbase tx, the height of
    35  // the block that contains the tx, whether or not it is spent, its public key
    36  // script, and how much it pays.
    37  type UtxoEntry struct {
    38  	// NOTE: Additions, deletions, or modifications to the order of the
    39  	// definitions in this struct should not be changed without considering
    40  	// how it affects alignment on 64-bit platforms.  The current order is
    41  	// specifically crafted to result in minimal padding.  There will be a
    42  	// lot of these in memory, so a few extra bytes of padding adds up.
    43  
    44  	amount      int64
    45  	pkScript    []byte // The public key script for the output.
    46  	blockHeight int32  // Height of block containing tx.
    47  
    48  	// packedFlags contains additional info about output such as whether it
    49  	// is a coinbase, whether it is spent, and whether it has been modified
    50  	// since it was loaded.  This approach is used in order to reduce memory
    51  	// usage since there will be a lot of these in memory.
    52  	packedFlags txoFlags
    53  }
    54  
    55  // isModified returns whether or not the output has been modified since it was
    56  // loaded.
    57  func (entry *UtxoEntry) isModified() bool {
    58  	return entry.packedFlags&tfModified == tfModified
    59  }
    60  
    61  // IsCoinBase returns whether or not the output was contained in a coinbase
    62  // transaction.
    63  func (entry *UtxoEntry) IsCoinBase() bool {
    64  	return entry.packedFlags&tfCoinBase == tfCoinBase
    65  }
    66  
    67  // BlockHeight returns the height of the block containing the output.
    68  func (entry *UtxoEntry) BlockHeight() int32 {
    69  	return entry.blockHeight
    70  }
    71  
    72  // IsSpent returns whether or not the output has been spent based upon the
    73  // current state of the unspent transaction output view it was obtained from.
    74  func (entry *UtxoEntry) IsSpent() bool {
    75  	return entry.packedFlags&tfSpent == tfSpent
    76  }
    77  
    78  // Spend marks the output as spent.  Spending an output that is already spent
    79  // has no effect.
    80  func (entry *UtxoEntry) Spend() {
    81  	// Nothing to do if the output is already spent.
    82  	if entry.IsSpent() {
    83  		return
    84  	}
    85  
    86  	// Mark the output as spent and modified.
    87  	entry.packedFlags |= tfSpent | tfModified
    88  }
    89  
    90  // Amount returns the amount of the output.
    91  func (entry *UtxoEntry) Amount() int64 {
    92  	return entry.amount
    93  }
    94  
    95  // PkScript returns the public key script for the output.
    96  func (entry *UtxoEntry) PkScript() []byte {
    97  	return entry.pkScript
    98  }
    99  
   100  // Clone returns a shallow copy of the utxo entry.
   101  func (entry *UtxoEntry) Clone() *UtxoEntry {
   102  	if entry == nil {
   103  		return nil
   104  	}
   105  
   106  	return &UtxoEntry{
   107  		amount:      entry.amount,
   108  		pkScript:    entry.pkScript,
   109  		blockHeight: entry.blockHeight,
   110  		packedFlags: entry.packedFlags,
   111  	}
   112  }
   113  
   114  // NewUtxoEntry returns a new UtxoEntry built from the arguments.
   115  func NewUtxoEntry(
   116  	txOut *wire.TxOut, blockHeight int32, isCoinbase bool) *UtxoEntry {
   117  	var cbFlag txoFlags
   118  	if isCoinbase {
   119  		cbFlag |= tfCoinBase
   120  	}
   121  
   122  	return &UtxoEntry{
   123  		amount:      txOut.Value,
   124  		pkScript:    txOut.PkScript,
   125  		blockHeight: blockHeight,
   126  		packedFlags: cbFlag,
   127  	}
   128  }
   129  
   130  // UtxoViewpoint represents a view into the set of unspent transaction outputs
   131  // from a specific point of view in the chain.  For example, it could be for
   132  // the end of the main chain, some point in the history of the main chain, or
   133  // down a side chain.
   134  //
   135  // The unspent outputs are needed by other transactions for things such as
   136  // script validation and double spend prevention.
   137  type UtxoViewpoint struct {
   138  	entries  map[wire.OutPoint]*UtxoEntry
   139  	bestHash chainhash.Hash
   140  }
   141  
   142  // BestHash returns the hash of the best block in the chain the view currently
   143  // respresents.
   144  func (view *UtxoViewpoint) BestHash() *chainhash.Hash {
   145  	return &view.bestHash
   146  }
   147  
   148  // SetBestHash sets the hash of the best block in the chain the view currently
   149  // respresents.
   150  func (view *UtxoViewpoint) SetBestHash(hash *chainhash.Hash) {
   151  	view.bestHash = *hash
   152  }
   153  
   154  // LookupEntry returns information about a given transaction output according to
   155  // the current state of the view.  It will return nil if the passed output does
   156  // not exist in the view or is otherwise not available such as when it has been
   157  // disconnected during a reorg.
   158  func (view *UtxoViewpoint) LookupEntry(outpoint wire.OutPoint) *UtxoEntry {
   159  	return view.entries[outpoint]
   160  }
   161  
   162  // addTxOut adds the specified output to the view if it is not provably
   163  // unspendable.  When the view already has an entry for the output, it will be
   164  // marked unspent.  All fields will be updated for existing entries since it's
   165  // possible it has changed during a reorg.
   166  func (view *UtxoViewpoint) addTxOut(outpoint wire.OutPoint, txOut *wire.TxOut, isCoinBase bool, blockHeight int32) {
   167  	// Don't add provably unspendable outputs.
   168  	if txscript.IsUnspendable(txOut.PkScript) {
   169  		return
   170  	}
   171  
   172  	// Update existing entries.  All fields are updated because it's
   173  	// possible (although extremely unlikely) that the existing entry is
   174  	// being replaced by a different transaction with the same hash.  This
   175  	// is allowed so long as the previous transaction is fully spent.
   176  	entry := view.LookupEntry(outpoint)
   177  	if entry == nil {
   178  		entry = new(UtxoEntry)
   179  		view.entries[outpoint] = entry
   180  	}
   181  
   182  	entry.amount = txOut.Value
   183  	entry.pkScript = txOut.PkScript
   184  	entry.blockHeight = blockHeight
   185  	entry.packedFlags = tfModified
   186  	if isCoinBase {
   187  		entry.packedFlags |= tfCoinBase
   188  	}
   189  }
   190  
   191  // AddTxOut adds the specified output of the passed transaction to the view if
   192  // it exists and is not provably unspendable.  When the view already has an
   193  // entry for the output, it will be marked unspent.  All fields will be updated
   194  // for existing entries since it's possible it has changed during a reorg.
   195  func (view *UtxoViewpoint) AddTxOut(tx *btcutil.Tx, txOutIdx uint32, blockHeight int32) {
   196  	// Can't add an output for an out of bounds index.
   197  	if txOutIdx >= uint32(len(tx.MsgTx().TxOut)) {
   198  		return
   199  	}
   200  
   201  	// Update existing entries.  All fields are updated because it's
   202  	// possible (although extremely unlikely) that the existing entry is
   203  	// being replaced by a different transaction with the same hash.  This
   204  	// is allowed so long as the previous transaction is fully spent.
   205  	prevOut := wire.OutPoint{Hash: *tx.Hash(), Index: txOutIdx}
   206  	txOut := tx.MsgTx().TxOut[txOutIdx]
   207  	view.addTxOut(prevOut, txOut, IsCoinBase(tx), blockHeight)
   208  }
   209  
   210  // AddTxOuts adds all outputs in the passed transaction which are not provably
   211  // unspendable to the view.  When the view already has entries for any of the
   212  // outputs, they are simply marked unspent.  All fields will be updated for
   213  // existing entries since it's possible it has changed during a reorg.
   214  func (view *UtxoViewpoint) AddTxOuts(tx *btcutil.Tx, blockHeight int32) {
   215  	// Loop all of the transaction outputs and add those which are not
   216  	// provably unspendable.
   217  	isCoinBase := IsCoinBase(tx)
   218  	prevOut := wire.OutPoint{Hash: *tx.Hash()}
   219  	for txOutIdx, txOut := range tx.MsgTx().TxOut {
   220  		// Update existing entries.  All fields are updated because it's
   221  		// possible (although extremely unlikely) that the existing
   222  		// entry is being replaced by a different transaction with the
   223  		// same hash.  This is allowed so long as the previous
   224  		// transaction is fully spent.
   225  		prevOut.Index = uint32(txOutIdx)
   226  		view.addTxOut(prevOut, txOut, isCoinBase, blockHeight)
   227  	}
   228  }
   229  
   230  // connectTransaction updates the view by adding all new utxos created by the
   231  // passed transaction and marking all utxos that the transactions spend as
   232  // spent.  In addition, when the 'stxos' argument is not nil, it will be updated
   233  // to append an entry for each spent txout.  An error will be returned if the
   234  // view does not contain the required utxos.
   235  func (view *UtxoViewpoint) connectTransaction(tx *btcutil.Tx, blockHeight int32, stxos *[]SpentTxOut) error {
   236  	// Coinbase transactions don't have any inputs to spend.
   237  	if IsCoinBase(tx) {
   238  		// Add the transaction's outputs as available utxos.
   239  		view.AddTxOuts(tx, blockHeight)
   240  		return nil
   241  	}
   242  
   243  	// Spend the referenced utxos by marking them spent in the view and,
   244  	// if a slice was provided for the spent txout details, append an entry
   245  	// to it.
   246  	for _, txIn := range tx.MsgTx().TxIn {
   247  		// Ensure the referenced utxo exists in the view.  This should
   248  		// never happen unless there is a bug is introduced in the code.
   249  		entry := view.entries[txIn.PreviousOutPoint]
   250  		if entry == nil {
   251  			return AssertError(fmt.Sprintf("view missing input %v",
   252  				txIn.PreviousOutPoint))
   253  		}
   254  
   255  		// Only create the stxo details if requested.
   256  		if stxos != nil {
   257  			// Populate the stxo details using the utxo entry.
   258  			var stxo = SpentTxOut{
   259  				Amount:     entry.Amount(),
   260  				PkScript:   entry.PkScript(),
   261  				Height:     entry.BlockHeight(),
   262  				IsCoinBase: entry.IsCoinBase(),
   263  			}
   264  			*stxos = append(*stxos, stxo)
   265  		}
   266  
   267  		// Mark the entry as spent.  This is not done until after the
   268  		// relevant details have been accessed since spending it might
   269  		// clear the fields from memory in the future.
   270  		entry.Spend()
   271  	}
   272  
   273  	// Add the transaction's outputs as available utxos.
   274  	view.AddTxOuts(tx, blockHeight)
   275  	return nil
   276  }
   277  
   278  // connectTransactions updates the view by adding all new utxos created by all
   279  // of the transactions in the passed block, marking all utxos the transactions
   280  // spend as spent, and setting the best hash for the view to the passed block.
   281  // In addition, when the 'stxos' argument is not nil, it will be updated to
   282  // append an entry for each spent txout.
   283  func (view *UtxoViewpoint) connectTransactions(block *btcutil.Block, stxos *[]SpentTxOut) error {
   284  	for _, tx := range block.Transactions() {
   285  		err := view.connectTransaction(tx, block.Height(), stxos)
   286  		if err != nil {
   287  			return err
   288  		}
   289  	}
   290  
   291  	// Update the best hash for view to include this block since all of its
   292  	// transactions have been connected.
   293  	view.SetBestHash(block.Hash())
   294  	return nil
   295  }
   296  
   297  // fetchEntryByHash attempts to find any available utxo for the given hash by
   298  // searching the entire set of possible outputs for the given hash.  It checks
   299  // the view first and then falls back to the database if needed.
   300  func (view *UtxoViewpoint) fetchEntryByHash(db database.DB, hash *chainhash.Hash) (*UtxoEntry, error) {
   301  	// First attempt to find a utxo with the provided hash in the view.
   302  	prevOut := wire.OutPoint{Hash: *hash}
   303  	for idx := uint32(0); idx < MaxOutputsPerBlock; idx++ {
   304  		prevOut.Index = idx
   305  		entry := view.LookupEntry(prevOut)
   306  		if entry != nil {
   307  			return entry, nil
   308  		}
   309  	}
   310  
   311  	// Check the database since it doesn't exist in the view.  This will
   312  	// often by the case since only specifically referenced utxos are loaded
   313  	// into the view.
   314  	var entry *UtxoEntry
   315  	err := db.View(func(dbTx database.Tx) error {
   316  		var err error
   317  		entry, err = dbFetchUtxoEntryByHash(dbTx, hash)
   318  		return err
   319  	})
   320  	return entry, err
   321  }
   322  
   323  // disconnectTransactions updates the view by removing all of the transactions
   324  // created by the passed block, restoring all utxos the transactions spent by
   325  // using the provided spent txo information, and setting the best hash for the
   326  // view to the block before the passed block.
   327  func (view *UtxoViewpoint) disconnectTransactions(db database.DB, block *btcutil.Block, stxos []SpentTxOut) error {
   328  	// Sanity check the correct number of stxos are provided.
   329  	if len(stxos) != countSpentOutputs(block) {
   330  		return AssertError("disconnectTransactions called with bad " +
   331  			"spent transaction out information")
   332  	}
   333  
   334  	// Loop backwards through all transactions so everything is unspent in
   335  	// reverse order.  This is necessary since transactions later in a block
   336  	// can spend from previous ones.
   337  	stxoIdx := len(stxos) - 1
   338  	transactions := block.Transactions()
   339  	for txIdx := len(transactions) - 1; txIdx > -1; txIdx-- {
   340  		tx := transactions[txIdx]
   341  
   342  		// All entries will need to potentially be marked as a coinbase.
   343  		var packedFlags txoFlags
   344  		isCoinBase := txIdx == 0
   345  		if isCoinBase {
   346  			packedFlags |= tfCoinBase
   347  		}
   348  
   349  		// Mark all of the spendable outputs originally created by the
   350  		// transaction as spent.  It is instructive to note that while
   351  		// the outputs aren't actually being spent here, rather they no
   352  		// longer exist, since a pruned utxo set is used, there is no
   353  		// practical difference between a utxo that does not exist and
   354  		// one that has been spent.
   355  		//
   356  		// When the utxo does not already exist in the view, add an
   357  		// entry for it and then mark it spent.  This is done because
   358  		// the code relies on its existence in the view in order to
   359  		// signal modifications have happened.
   360  		txHash := tx.Hash()
   361  		prevOut := wire.OutPoint{Hash: *txHash}
   362  		for txOutIdx, txOut := range tx.MsgTx().TxOut {
   363  			if txscript.IsUnspendable(txOut.PkScript) {
   364  				continue
   365  			}
   366  
   367  			prevOut.Index = uint32(txOutIdx)
   368  			entry := view.entries[prevOut]
   369  			if entry == nil {
   370  				entry = &UtxoEntry{
   371  					amount:      txOut.Value,
   372  					pkScript:    txOut.PkScript,
   373  					blockHeight: block.Height(),
   374  					packedFlags: packedFlags,
   375  				}
   376  
   377  				view.entries[prevOut] = entry
   378  			}
   379  
   380  			entry.Spend()
   381  		}
   382  
   383  		// Loop backwards through all of the transaction inputs (except
   384  		// for the coinbase which has no inputs) and unspend the
   385  		// referenced txos.  This is necessary to match the order of the
   386  		// spent txout entries.
   387  		if isCoinBase {
   388  			continue
   389  		}
   390  		for txInIdx := len(tx.MsgTx().TxIn) - 1; txInIdx > -1; txInIdx-- {
   391  			// Ensure the spent txout index is decremented to stay
   392  			// in sync with the transaction input.
   393  			stxo := &stxos[stxoIdx]
   394  			stxoIdx--
   395  
   396  			// When there is not already an entry for the referenced
   397  			// output in the view, it means it was previously spent,
   398  			// so create a new utxo entry in order to resurrect it.
   399  			originOut := &tx.MsgTx().TxIn[txInIdx].PreviousOutPoint
   400  			entry := view.entries[*originOut]
   401  			if entry == nil {
   402  				entry = new(UtxoEntry)
   403  				view.entries[*originOut] = entry
   404  			}
   405  
   406  			// The legacy v1 spend journal format only stored the
   407  			// coinbase flag and height when the output was the last
   408  			// unspent output of the transaction.  As a result, when
   409  			// the information is missing, search for it by scanning
   410  			// all possible outputs of the transaction since it must
   411  			// be in one of them.
   412  			//
   413  			// It should be noted that this is quite inefficient,
   414  			// but it realistically will almost never run since all
   415  			// new entries include the information for all outputs
   416  			// and thus the only way this will be hit is if a long
   417  			// enough reorg happens such that a block with the old
   418  			// spend data is being disconnected.  The probability of
   419  			// that in practice is extremely low to begin with and
   420  			// becomes vanishingly small the more new blocks are
   421  			// connected.  In the case of a fresh database that has
   422  			// only ever run with the new v2 format, this code path
   423  			// will never run.
   424  			if stxo.Height == 0 {
   425  				utxo, err := view.fetchEntryByHash(db, txHash)
   426  				if err != nil {
   427  					return err
   428  				}
   429  				if utxo == nil {
   430  					return AssertError(fmt.Sprintf("unable "+
   431  						"to resurrect legacy stxo %v",
   432  						*originOut))
   433  				}
   434  
   435  				stxo.Height = utxo.BlockHeight()
   436  				stxo.IsCoinBase = utxo.IsCoinBase()
   437  			}
   438  
   439  			// Restore the utxo using the stxo data from the spend
   440  			// journal and mark it as modified.
   441  			entry.amount = stxo.Amount
   442  			entry.pkScript = stxo.PkScript
   443  			entry.blockHeight = stxo.Height
   444  			entry.packedFlags = tfModified
   445  			if stxo.IsCoinBase {
   446  				entry.packedFlags |= tfCoinBase
   447  			}
   448  		}
   449  	}
   450  
   451  	// Update the best hash for view to the previous block since all of the
   452  	// transactions for the current block have been disconnected.
   453  	view.SetBestHash(&block.MsgBlock().Header.PrevBlock)
   454  	return nil
   455  }
   456  
   457  // RemoveEntry removes the given transaction output from the current state of
   458  // the view.  It will have no effect if the passed output does not exist in the
   459  // view.
   460  func (view *UtxoViewpoint) RemoveEntry(outpoint wire.OutPoint) {
   461  	delete(view.entries, outpoint)
   462  }
   463  
   464  // Entries returns the underlying map that stores of all the utxo entries.
   465  func (view *UtxoViewpoint) Entries() map[wire.OutPoint]*UtxoEntry {
   466  	return view.entries
   467  }
   468  
   469  // commit prunes all entries marked modified that are now fully spent and marks
   470  // all entries as unmodified.
   471  func (view *UtxoViewpoint) commit() {
   472  	for outpoint, entry := range view.entries {
   473  		if entry == nil || (entry.isModified() && entry.IsSpent()) {
   474  			delete(view.entries, outpoint)
   475  			continue
   476  		}
   477  
   478  		entry.packedFlags ^= tfModified
   479  	}
   480  }
   481  
   482  // fetchUtxosMain fetches unspent transaction output data about the provided
   483  // set of outpoints from the point of view of the end of the main chain at the
   484  // time of the call.
   485  //
   486  // Upon completion of this function, the view will contain an entry for each
   487  // requested outpoint.  Spent outputs, or those which otherwise don't exist,
   488  // will result in a nil entry in the view.
   489  func (view *UtxoViewpoint) fetchUtxosMain(db database.DB, outpoints map[wire.OutPoint]struct{}) error {
   490  	// Nothing to do if there are no requested outputs.
   491  	if len(outpoints) == 0 {
   492  		return nil
   493  	}
   494  
   495  	// Load the requested set of unspent transaction outputs from the point
   496  	// of view of the end of the main chain.
   497  	//
   498  	// NOTE: Missing entries are not considered an error here and instead
   499  	// will result in nil entries in the view.  This is intentionally done
   500  	// so other code can use the presence of an entry in the store as a way
   501  	// to unnecessarily avoid attempting to reload it from the database.
   502  	return db.View(func(dbTx database.Tx) error {
   503  		for outpoint := range outpoints {
   504  			entry, err := dbFetchUtxoEntry(dbTx, outpoint)
   505  			if err != nil {
   506  				return err
   507  			}
   508  
   509  			view.entries[outpoint] = entry
   510  		}
   511  
   512  		return nil
   513  	})
   514  }
   515  
   516  // fetchUtxos loads the unspent transaction outputs for the provided set of
   517  // outputs into the view from the database as needed unless they already exist
   518  // in the view in which case they are ignored.
   519  func (view *UtxoViewpoint) fetchUtxos(db database.DB, outpoints map[wire.OutPoint]struct{}) error {
   520  	// Nothing to do if there are no requested outputs.
   521  	if len(outpoints) == 0 {
   522  		return nil
   523  	}
   524  
   525  	// Filter entries that are already in the view.
   526  	neededSet := make(map[wire.OutPoint]struct{})
   527  	for outpoint := range outpoints {
   528  		// Already loaded into the current view.
   529  		if _, ok := view.entries[outpoint]; ok {
   530  			continue
   531  		}
   532  
   533  		neededSet[outpoint] = struct{}{}
   534  	}
   535  
   536  	// Request the input utxos from the database.
   537  	return view.fetchUtxosMain(db, neededSet)
   538  }
   539  
   540  // fetchInputUtxos loads the unspent transaction outputs for the inputs
   541  // referenced by the transactions in the given block into the view from the
   542  // database as needed.  In particular, referenced entries that are earlier in
   543  // the block are added to the view and entries that are already in the view are
   544  // not modified.
   545  func (view *UtxoViewpoint) fetchInputUtxos(db database.DB, block *btcutil.Block) error {
   546  	// Build a map of in-flight transactions because some of the inputs in
   547  	// this block could be referencing other transactions earlier in this
   548  	// block which are not yet in the chain.
   549  	txInFlight := map[chainhash.Hash]int{}
   550  	transactions := block.Transactions()
   551  	for i, tx := range transactions {
   552  		txInFlight[*tx.Hash()] = i
   553  	}
   554  
   555  	// Loop through all of the transaction inputs (except for the coinbase
   556  	// which has no inputs) collecting them into sets of what is needed and
   557  	// what is already known (in-flight).
   558  	neededSet := make(map[wire.OutPoint]struct{})
   559  	for i, tx := range transactions[1:] {
   560  		for _, txIn := range tx.MsgTx().TxIn {
   561  			// It is acceptable for a transaction input to reference
   562  			// the output of another transaction in this block only
   563  			// if the referenced transaction comes before the
   564  			// current one in this block.  Add the outputs of the
   565  			// referenced transaction as available utxos when this
   566  			// is the case.  Otherwise, the utxo details are still
   567  			// needed.
   568  			//
   569  			// NOTE: The >= is correct here because i is one less
   570  			// than the actual position of the transaction within
   571  			// the block due to skipping the coinbase.
   572  			originHash := &txIn.PreviousOutPoint.Hash
   573  			if inFlightIndex, ok := txInFlight[*originHash]; ok &&
   574  				i >= inFlightIndex {
   575  
   576  				originTx := transactions[inFlightIndex]
   577  				view.AddTxOuts(originTx, block.Height())
   578  				continue
   579  			}
   580  
   581  			// Don't request entries that are already in the view
   582  			// from the database.
   583  			if _, ok := view.entries[txIn.PreviousOutPoint]; ok {
   584  				continue
   585  			}
   586  
   587  			neededSet[txIn.PreviousOutPoint] = struct{}{}
   588  		}
   589  	}
   590  
   591  	// Request the input utxos from the database.
   592  	return view.fetchUtxosMain(db, neededSet)
   593  }
   594  
   595  // NewUtxoViewpoint returns a new empty unspent transaction output view.
   596  func NewUtxoViewpoint() *UtxoViewpoint {
   597  	return &UtxoViewpoint{
   598  		entries: make(map[wire.OutPoint]*UtxoEntry),
   599  	}
   600  }
   601  
   602  // FetchUtxoView loads unspent transaction outputs for the inputs referenced by
   603  // the passed transaction from the point of view of the end of the main chain.
   604  // It also attempts to fetch the utxos for the outputs of the transaction itself
   605  // so the returned view can be examined for duplicate transactions.
   606  //
   607  // This function is safe for concurrent access however the returned view is NOT.
   608  func (b *BlockChain) FetchUtxoView(tx *btcutil.Tx) (*UtxoViewpoint, error) {
   609  	// Create a set of needed outputs based on those referenced by the
   610  	// inputs of the passed transaction and the outputs of the transaction
   611  	// itself.
   612  	neededSet := make(map[wire.OutPoint]struct{})
   613  	prevOut := wire.OutPoint{Hash: *tx.Hash()}
   614  	for txOutIdx := range tx.MsgTx().TxOut {
   615  		prevOut.Index = uint32(txOutIdx)
   616  		neededSet[prevOut] = struct{}{}
   617  	}
   618  	if !IsCoinBase(tx) {
   619  		for _, txIn := range tx.MsgTx().TxIn {
   620  			neededSet[txIn.PreviousOutPoint] = struct{}{}
   621  		}
   622  	}
   623  
   624  	// Request the utxos from the point of view of the end of the main
   625  	// chain.
   626  	view := NewUtxoViewpoint()
   627  	b.chainLock.RLock()
   628  	err := view.fetchUtxosMain(b.db, neededSet)
   629  	b.chainLock.RUnlock()
   630  	return view, err
   631  }
   632  
   633  // FetchUtxoEntry loads and returns the requested unspent transaction output
   634  // from the point of view of the end of the main chain.
   635  //
   636  // NOTE: Requesting an output for which there is no data will NOT return an
   637  // error.  Instead both the entry and the error will be nil.  This is done to
   638  // allow pruning of spent transaction outputs.  In practice this means the
   639  // caller must check if the returned entry is nil before invoking methods on it.
   640  //
   641  // This function is safe for concurrent access however the returned entry (if
   642  // any) is NOT.
   643  func (b *BlockChain) FetchUtxoEntry(outpoint wire.OutPoint) (*UtxoEntry, error) {
   644  	b.chainLock.RLock()
   645  	defer b.chainLock.RUnlock()
   646  
   647  	var entry *UtxoEntry
   648  	err := b.db.View(func(dbTx database.Tx) error {
   649  		var err error
   650  		entry, err = dbFetchUtxoEntry(dbTx, outpoint)
   651  		return err
   652  	})
   653  	if err != nil {
   654  		return nil, err
   655  	}
   656  
   657  	return entry, nil
   658  }