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.
10 "github.com/btcsuite/btcd/chaincfg/chainhash"
11 "github.com/btcsuite/btcd/database"
12 "github.com/btcsuite/btcd/txscript"
13 "github.com/btcsuite/btcutil"
16 // utxoOutput houses details about an individual unspent transaction output such
17 // as whether or not it is spent, its public key script, and how much it pays.
19 // Standard public key scripts are stored in the database using a compressed
20 // format. Since the vast majority of scripts are of the standard form, a fairly
21 // significant savings is achieved by discarding the portions of the standard
22 // scripts that can be reconstructed.
24 // Also, since it is common for only a specific output in a given utxo entry to
25 // be referenced from a redeeming transaction, the script and amount for a given
26 // output is not uncompressed until the first time it is accessed. This
27 // provides a mechanism to avoid the overhead of needlessly uncompressing all
28 // outputs for a given utxo entry at the time of load.
29 type utxoOutput struct {
30 spent bool // Output is spent.
31 compressed bool // The amount and public key script are compressed.
32 amount int64 // The amount of the output.
33 pkScript []byte // The public key script for the output.
36 // maybeDecompress decompresses the amount and public key script fields of the
37 // utxo and marks it decompressed if needed.
38 func (o *utxoOutput) maybeDecompress(version int32) {
39 // Nothing to do if it's not compressed.
44 o.amount = int64(decompressTxOutAmount(uint64(o.amount)))
45 o.pkScript = decompressScript(o.pkScript, version)
49 // UtxoEntry contains contextual information about an unspent transaction such
50 // as whether or not it is a coinbase transaction, which block it was found in,
51 // and the spent status of its outputs.
52 type UtxoEntry struct {
53 modified bool // Entry changed since load.
54 version int32 // The version of this tx.
55 isCoinBase bool // Whether entry is a coinbase tx.
56 blockHeight int32 // Height of block containing tx.
57 sparseOutputs map[uint32]*utxoOutput // Sparse map of unspent outputs.
60 // Version returns the version of the transaction the utxo represents.
61 func (entry *UtxoEntry) Version() int32 {
65 // IsCoinBase returns whether or not the transaction the utxo entry represents
67 func (entry *UtxoEntry) IsCoinBase() bool {
68 return entry.isCoinBase
71 // BlockHeight returns the height of the block containing the transaction the
72 // utxo entry represents.
73 func (entry *UtxoEntry) BlockHeight() int32 {
74 return entry.blockHeight
77 // IsOutputSpent returns whether or not the provided output index has been
78 // spent based upon the current state of the unspent transaction output view
79 // the entry was obtained from.
81 // Returns true if the output index references an output that does not exist
82 // either due to it being invalid or because the output is not part of the view
83 // due to previously being spent/pruned.
84 func (entry *UtxoEntry) IsOutputSpent(outputIndex uint32) bool {
85 output, ok := entry.sparseOutputs[outputIndex]
93 // SpendOutput marks the output at the provided index as spent. Specifying an
94 // output index that does not exist will not have any effect.
95 func (entry *UtxoEntry) SpendOutput(outputIndex uint32) {
96 output, ok := entry.sparseOutputs[outputIndex]
101 // Nothing to do if the output is already spent.
106 entry.modified = true
110 // IsFullySpent returns whether or not the transaction the utxo entry represents
112 func (entry *UtxoEntry) IsFullySpent() bool {
113 // The entry is not fully spent if any of the outputs are unspent.
114 for _, output := range entry.sparseOutputs {
123 // AmountByIndex returns the amount of the provided output index.
125 // Returns 0 if the output index references an output that does not exist
126 // either due to it being invalid or because the output is not part of the view
127 // due to previously being spent/pruned.
128 func (entry *UtxoEntry) AmountByIndex(outputIndex uint32) int64 {
129 output, ok := entry.sparseOutputs[outputIndex]
134 // Ensure the output is decompressed before returning the amount.
135 output.maybeDecompress(entry.version)
139 // PkScriptByIndex returns the public key script for the provided output index.
141 // Returns nil if the output index references an output that does not exist
142 // either due to it being invalid or because the output is not part of the view
143 // due to previously being spent/pruned.
144 func (entry *UtxoEntry) PkScriptByIndex(outputIndex uint32) []byte {
145 output, ok := entry.sparseOutputs[outputIndex]
150 // Ensure the output is decompressed before returning the script.
151 output.maybeDecompress(entry.version)
152 return output.pkScript
155 // Clone returns a deep copy of the utxo entry.
156 func (entry *UtxoEntry) Clone() *UtxoEntry {
161 newEntry := &UtxoEntry{
162 version: entry.version,
163 isCoinBase: entry.isCoinBase,
164 blockHeight: entry.blockHeight,
165 sparseOutputs: make(map[uint32]*utxoOutput),
167 for outputIndex, output := range entry.sparseOutputs {
168 newEntry.sparseOutputs[outputIndex] = &utxoOutput{
170 compressed: output.compressed,
171 amount: output.amount,
172 pkScript: output.pkScript,
178 // newUtxoEntry returns a new unspent transaction output entry with the provided
179 // coinbase flag and block height ready to have unspent outputs added.
180 func newUtxoEntry(version int32, isCoinBase bool, blockHeight int32) *UtxoEntry {
183 isCoinBase: isCoinBase,
184 blockHeight: blockHeight,
185 sparseOutputs: make(map[uint32]*utxoOutput),
189 // UtxoViewpoint represents a view into the set of unspent transaction outputs
190 // from a specific point of view in the chain. For example, it could be for
191 // the end of the main chain, some point in the history of the main chain, or
192 // down a side chain.
194 // The unspent outputs are needed by other transactions for things such as
195 // script validation and double spend prevention.
196 type UtxoViewpoint struct {
197 entries map[chainhash.Hash]*UtxoEntry
198 bestHash chainhash.Hash
201 // BestHash returns the hash of the best block in the chain the view currently
203 func (view *UtxoViewpoint) BestHash() *chainhash.Hash {
204 return &view.bestHash
207 // SetBestHash sets the hash of the best block in the chain the view currently
209 func (view *UtxoViewpoint) SetBestHash(hash *chainhash.Hash) {
210 view.bestHash = *hash
213 // LookupEntry returns information about a given transaction according to the
214 // current state of the view. It will return nil if the passed transaction
215 // hash does not exist in the view or is otherwise not available such as when
216 // it has been disconnected during a reorg.
217 func (view *UtxoViewpoint) LookupEntry(txHash *chainhash.Hash) *UtxoEntry {
218 entry, ok := view.entries[*txHash]
226 // AddTxOuts adds all outputs in the passed transaction which are not provably
227 // unspendable to the view. When the view already has entries for any of the
228 // outputs, they are simply marked unspent. All fields will be updated for
229 // existing entries since it's possible it has changed during a reorg.
230 func (view *UtxoViewpoint) AddTxOuts(tx *btcutil.Tx, blockHeight int32) {
231 // When there are not already any utxos associated with the transaction,
232 // add a new entry for it to the view.
233 entry := view.LookupEntry(tx.Hash())
235 entry = newUtxoEntry(tx.MsgTx().Version, IsCoinBase(tx),
237 view.entries[*tx.Hash()] = entry
239 entry.blockHeight = blockHeight
241 entry.modified = true
243 // Loop all of the transaction outputs and add those which are not
244 // provably unspendable.
245 for txOutIdx, txOut := range tx.MsgTx().TxOut {
246 if txscript.IsUnspendable(txOut.PkScript) {
250 // Update existing entries. All fields are updated because it's
251 // possible (although extremely unlikely) that the existing
252 // entry is being replaced by a different transaction with the
253 // same hash. This is allowed so long as the previous
254 // transaction is fully spent.
255 if output, ok := entry.sparseOutputs[uint32(txOutIdx)]; ok {
257 output.compressed = false
258 output.amount = txOut.Value
259 output.pkScript = txOut.PkScript
263 // Add the unspent transaction output.
264 entry.sparseOutputs[uint32(txOutIdx)] = &utxoOutput{
268 pkScript: txOut.PkScript,
273 // connectTransaction updates the view by adding all new utxos created by the
274 // passed transaction and marking all utxos that the transactions spend as
275 // spent. In addition, when the 'stxos' argument is not nil, it will be updated
276 // to append an entry for each spent txout. An error will be returned if the
277 // view does not contain the required utxos.
278 func (view *UtxoViewpoint) connectTransaction(tx *btcutil.Tx, blockHeight int32, stxos *[]spentTxOut) error {
279 // Coinbase transactions don't have any inputs to spend.
281 // Add the transaction's outputs as available utxos.
282 view.AddTxOuts(tx, blockHeight)
286 // Spend the referenced utxos by marking them spent in the view and,
287 // if a slice was provided for the spent txout details, append an entry
289 for _, txIn := range tx.MsgTx().TxIn {
290 originIndex := txIn.PreviousOutPoint.Index
291 entry := view.entries[txIn.PreviousOutPoint.Hash]
293 // Ensure the referenced utxo exists in the view. This should
294 // never happen unless there is a bug is introduced in the code.
296 return AssertError(fmt.Sprintf("view missing input %v",
297 txIn.PreviousOutPoint))
299 entry.SpendOutput(originIndex)
301 // Don't create the stxo details if not requested.
306 // Populate the stxo details using the utxo entry. When the
307 // transaction is fully spent, set the additional stxo fields
308 // accordingly since those details will no longer be available
310 var stxo = spentTxOut{
312 version: entry.Version(),
313 amount: entry.AmountByIndex(originIndex),
314 pkScript: entry.PkScriptByIndex(originIndex),
316 if entry.IsFullySpent() {
317 stxo.height = entry.BlockHeight()
318 stxo.isCoinBase = entry.IsCoinBase()
321 // Append the entry to the provided spent txouts slice.
322 *stxos = append(*stxos, stxo)
325 // Add the transaction's outputs as available utxos.
326 view.AddTxOuts(tx, blockHeight)
330 // connectTransactions updates the view by adding all new utxos created by all
331 // of the transactions in the passed block, marking all utxos the transactions
332 // spend as spent, and setting the best hash for the view to the passed block.
333 // In addition, when the 'stxos' argument is not nil, it will be updated to
334 // append an entry for each spent txout.
335 func (view *UtxoViewpoint) connectTransactions(block *btcutil.Block, stxos *[]spentTxOut) error {
336 for _, tx := range block.Transactions() {
337 err := view.connectTransaction(tx, block.Height(), stxos)
343 // Update the best hash for view to include this block since all of its
344 // transactions have been connected.
345 view.SetBestHash(block.Hash())
349 // disconnectTransactions updates the view by removing all of the transactions
350 // created by the passed block, restoring all utxos the transactions spent by
351 // using the provided spent txo information, and setting the best hash for the
352 // view to the block before the passed block.
353 func (view *UtxoViewpoint) disconnectTransactions(block *btcutil.Block, stxos []spentTxOut) error {
354 // Sanity check the correct number of stxos are provided.
355 if len(stxos) != countSpentOutputs(block) {
356 return AssertError("disconnectTransactions called with bad " +
357 "spent transaction out information")
360 // Loop backwards through all transactions so everything is unspent in
361 // reverse order. This is necessary since transactions later in a block
362 // can spend from previous ones.
363 stxoIdx := len(stxos) - 1
364 transactions := block.Transactions()
365 for txIdx := len(transactions) - 1; txIdx > -1; txIdx-- {
366 tx := transactions[txIdx]
368 // Clear this transaction from the view if it already exists or
369 // create a new empty entry for when it does not. This is done
370 // because the code relies on its existence in the view in order
371 // to signal modifications have happened.
372 isCoinbase := txIdx == 0
373 entry := view.entries[*tx.Hash()]
375 entry = newUtxoEntry(tx.MsgTx().Version, isCoinbase,
377 view.entries[*tx.Hash()] = entry
379 entry.modified = true
380 entry.sparseOutputs = make(map[uint32]*utxoOutput)
382 // Loop backwards through all of the transaction inputs (except
383 // for the coinbase which has no inputs) and unspend the
384 // referenced txos. This is necessary to match the order of the
385 // spent txout entries.
389 for txInIdx := len(tx.MsgTx().TxIn) - 1; txInIdx > -1; txInIdx-- {
390 // Ensure the spent txout index is decremented to stay
391 // in sync with the transaction input.
392 stxo := &stxos[stxoIdx]
395 // When there is not already an entry for the referenced
396 // transaction in the view, it means it was fully spent,
397 // so create a new utxo entry in order to resurrect it.
398 txIn := tx.MsgTx().TxIn[txInIdx]
399 originHash := &txIn.PreviousOutPoint.Hash
400 originIndex := txIn.PreviousOutPoint.Index
401 entry := view.entries[*originHash]
403 entry = newUtxoEntry(stxo.version,
404 stxo.isCoinBase, stxo.height)
405 view.entries[*originHash] = entry
408 // Mark the entry as modified since it is either new
409 // or will be changed below.
410 entry.modified = true
412 // Restore the specific utxo using the stxo data from
413 // the spend journal if it doesn't already exist in the
415 output, ok := entry.sparseOutputs[originIndex]
417 // Add the unspent transaction output.
418 entry.sparseOutputs[originIndex] = &utxoOutput{
420 compressed: stxo.compressed,
422 pkScript: stxo.pkScript,
427 // Mark the existing referenced transaction output as
433 // Update the best hash for view to the previous block since all of the
434 // transactions for the current block have been disconnected.
435 view.SetBestHash(&block.MsgBlock().Header.PrevBlock)
439 // Entries returns the underlying map that stores of all the utxo entries.
440 func (view *UtxoViewpoint) Entries() map[chainhash.Hash]*UtxoEntry {
444 // commit prunes all entries marked modified that are now fully spent and marks
445 // all entries as unmodified.
446 func (view *UtxoViewpoint) commit() {
447 for txHash, entry := range view.entries {
448 if entry == nil || (entry.modified && entry.IsFullySpent()) {
449 delete(view.entries, txHash)
453 entry.modified = false
457 // fetchUtxosMain fetches unspent transaction output data about the provided
458 // set of transactions from the point of view of the end of the main chain at
459 // the time of the call.
461 // Upon completion of this function, the view will contain an entry for each
462 // requested transaction. Fully spent transactions, or those which otherwise
463 // don't exist, will result in a nil entry in the view.
464 func (view *UtxoViewpoint) fetchUtxosMain(db database.DB, txSet map[chainhash.Hash]struct{}) error {
465 // Nothing to do if there are no requested hashes.
470 // Load the unspent transaction output information for the requested set
471 // of transactions from the point of view of the end of the main chain.
473 // NOTE: Missing entries are not considered an error here and instead
474 // will result in nil entries in the view. This is intentionally done
475 // since other code uses the presence of an entry in the store as a way
476 // to optimize spend and unspend updates to apply only to the specific
477 // utxos that the caller needs access to.
478 return db.View(func(dbTx database.Tx) error {
479 for hash := range txSet {
481 entry, err := dbFetchUtxoEntry(dbTx, &hashCopy)
486 view.entries[hash] = entry
493 // fetchUtxos loads utxo details about provided set of transaction hashes into
494 // the view from the database as needed unless they already exist in the view in
495 // which case they are ignored.
496 func (view *UtxoViewpoint) fetchUtxos(db database.DB, txSet map[chainhash.Hash]struct{}) error {
497 // Nothing to do if there are no requested hashes.
502 // Filter entries that are already in the view.
503 txNeededSet := make(map[chainhash.Hash]struct{})
504 for hash := range txSet {
505 // Already loaded into the current view.
506 if _, ok := view.entries[hash]; ok {
510 txNeededSet[hash] = struct{}{}
513 // Request the input utxos from the database.
514 return view.fetchUtxosMain(db, txNeededSet)
517 // fetchInputUtxos loads utxo details about the input transactions referenced
518 // by the transactions in the given block into the view from the database as
519 // needed. In particular, referenced entries that are earlier in the block are
520 // added to the view and entries that are already in the view are not modified.
521 func (view *UtxoViewpoint) fetchInputUtxos(db database.DB, block *btcutil.Block) error {
522 // Build a map of in-flight transactions because some of the inputs in
523 // this block could be referencing other transactions earlier in this
524 // block which are not yet in the chain.
525 txInFlight := map[chainhash.Hash]int{}
526 transactions := block.Transactions()
527 for i, tx := range transactions {
528 txInFlight[*tx.Hash()] = i
531 // Loop through all of the transaction inputs (except for the coinbase
532 // which has no inputs) collecting them into sets of what is needed and
533 // what is already known (in-flight).
534 txNeededSet := make(map[chainhash.Hash]struct{})
535 for i, tx := range transactions[1:] {
536 for _, txIn := range tx.MsgTx().TxIn {
537 // It is acceptable for a transaction input to reference
538 // the output of another transaction in this block only
539 // if the referenced transaction comes before the
540 // current one in this block. Add the outputs of the
541 // referenced transaction as available utxos when this
542 // is the case. Otherwise, the utxo details are still
545 // NOTE: The >= is correct here because i is one less
546 // than the actual position of the transaction within
547 // the block due to skipping the coinbase.
548 originHash := &txIn.PreviousOutPoint.Hash
549 if inFlightIndex, ok := txInFlight[*originHash]; ok &&
552 originTx := transactions[inFlightIndex]
553 view.AddTxOuts(originTx, block.Height())
557 // Don't request entries that are already in the view
558 // from the database.
559 if _, ok := view.entries[*originHash]; ok {
563 txNeededSet[*originHash] = struct{}{}
567 // Request the input utxos from the database.
568 return view.fetchUtxosMain(db, txNeededSet)
571 // NewUtxoViewpoint returns a new empty unspent transaction output view.
572 func NewUtxoViewpoint() *UtxoViewpoint {
573 return &UtxoViewpoint{
574 entries: make(map[chainhash.Hash]*UtxoEntry),
578 // FetchUtxoView loads utxo details about the input transactions referenced by
579 // the passed transaction from the point of view of the end of the main chain.
580 // It also attempts to fetch the utxo details for the transaction itself so the
581 // returned view can be examined for duplicate unspent transaction outputs.
583 // This function is safe for concurrent access however the returned view is NOT.
584 func (b *BlockChain) FetchUtxoView(tx *btcutil.Tx) (*UtxoViewpoint, error) {
586 defer b.chainLock.RUnlock()
588 // Create a set of needed transactions based on those referenced by the
589 // inputs of the passed transaction. Also, add the passed transaction
590 // itself as a way for the caller to detect duplicates that are not
592 txNeededSet := make(map[chainhash.Hash]struct{})
593 txNeededSet[*tx.Hash()] = struct{}{}
595 for _, txIn := range tx.MsgTx().TxIn {
596 txNeededSet[txIn.PreviousOutPoint.Hash] = struct{}{}
600 // Request the utxos from the point of view of the end of the main
602 view := NewUtxoViewpoint()
603 err := view.fetchUtxosMain(b.db, txNeededSet)
607 // FetchUtxoEntry loads and returns the unspent transaction output entry for the
608 // passed hash from the point of view of the end of the main chain.
610 // NOTE: Requesting a hash for which there is no data will NOT return an error.
611 // Instead both the entry and the error will be nil. This is done to allow
612 // pruning of fully spent transactions. In practice this means the caller must
613 // check if the returned entry is nil before invoking methods on it.
615 // This function is safe for concurrent access however the returned entry (if
617 func (b *BlockChain) FetchUtxoEntry(txHash *chainhash.Hash) (*UtxoEntry, error) {
619 defer b.chainLock.RUnlock()
622 err := b.db.View(func(dbTx database.Tx) error {
624 entry, err = dbFetchUtxoEntry(dbTx, txHash)