github.com/decred/dcrd/blockchain@v1.2.1/utxoviewpoint.go (about) 1 // Copyright (c) 2015-2016 The btcsuite developers 2 // Copyright (c) 2015-2019 The Decred developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package blockchain 7 8 import ( 9 "fmt" 10 11 "github.com/decred/dcrd/blockchain/stake" 12 "github.com/decred/dcrd/blockchain/standalone" 13 "github.com/decred/dcrd/chaincfg/chainhash" 14 "github.com/decred/dcrd/database" 15 "github.com/decred/dcrd/dcrutil" 16 "github.com/decred/dcrd/txscript" 17 ) 18 19 // utxoOutput houses details about an individual unspent transaction output such 20 // as whether or not it is spent, its public key script, and how much it pays. 21 // 22 // Standard public key scripts are stored in the database using a compressed 23 // format. Since the vast majority of scripts are of the standard form, a fairly 24 // significant savings is achieved by discarding the portions of the standard 25 // scripts that can be reconstructed. 26 // 27 // Also, since it is common for only a specific output in a given utxo entry to 28 // be referenced from a redeeming transaction, the script and amount for a given 29 // output is not uncompressed until the first time it is accessed. This 30 // provides a mechanism to avoid the overhead of needlessly uncompressing all 31 // outputs for a given utxo entry at the time of load. 32 // 33 // The struct is aligned for memory efficiency. 34 type utxoOutput struct { 35 pkScript []byte // The public key script for the output. 36 amount int64 // The amount of the output. 37 scriptVersion uint16 // The script version 38 compressed bool // The public key script is compressed. 39 spent bool // Output is spent. 40 } 41 42 // maybeDecompress decompresses the amount and public key script fields of the 43 // utxo and marks it decompressed if needed. 44 func (o *utxoOutput) maybeDecompress(compressionVersion uint32) { 45 // Nothing to do if it's not compressed. 46 if !o.compressed { 47 return 48 } 49 50 o.pkScript = decompressScript(o.pkScript, compressionVersion) 51 o.compressed = false 52 } 53 54 // UtxoEntry contains contextual information about an unspent transaction such 55 // as whether or not it is a coinbase transaction, which block it was found in, 56 // and the spent status of its outputs. 57 // 58 // The struct is aligned for memory efficiency. 59 type UtxoEntry struct { 60 sparseOutputs map[uint32]*utxoOutput // Sparse map of unspent outputs. 61 stakeExtra []byte // Extra data for the staking system. 62 63 txType stake.TxType // The stake type of the transaction. 64 height uint32 // Height of block containing tx. 65 index uint32 // Index of containing tx in block. 66 txVersion uint16 // The tx version of this tx. 67 68 isCoinBase bool // Whether entry is a coinbase tx. 69 hasExpiry bool // Whether entry has an expiry. 70 modified bool // Entry changed since load. 71 } 72 73 // TxVersion returns the transaction version of the transaction the 74 // utxo represents. 75 func (entry *UtxoEntry) TxVersion() uint16 { 76 return entry.txVersion 77 } 78 79 // HasExpiry returns the transaction expiry for the transaction that the utxo 80 // entry represents. 81 func (entry *UtxoEntry) HasExpiry() bool { 82 return entry.hasExpiry 83 } 84 85 // IsCoinBase returns whether or not the transaction the utxo entry represents 86 // is a coinbase. 87 func (entry *UtxoEntry) IsCoinBase() bool { 88 return entry.isCoinBase 89 } 90 91 // BlockHeight returns the height of the block containing the transaction the 92 // utxo entry represents. 93 func (entry *UtxoEntry) BlockHeight() int64 { 94 return int64(entry.height) 95 } 96 97 // BlockIndex returns the height of the block containing the transaction the 98 // utxo entry represents. 99 func (entry *UtxoEntry) BlockIndex() uint32 { 100 return entry.index 101 } 102 103 // TransactionType returns the transaction type of the transaction the utxo entry 104 // represents. 105 func (entry *UtxoEntry) TransactionType() stake.TxType { 106 return entry.txType 107 } 108 109 // IsOutputSpent returns whether or not the provided output index has been 110 // spent based upon the current state of the unspent transaction output view 111 // the entry was obtained from. 112 // 113 // Returns true if the output index references an output that does not exist 114 // either due to it being invalid or because the output is not part of the view 115 // due to previously being spent/pruned. 116 func (entry *UtxoEntry) IsOutputSpent(outputIndex uint32) bool { 117 output, ok := entry.sparseOutputs[outputIndex] 118 if !ok { 119 return true 120 } 121 122 return output.spent 123 } 124 125 // SpendOutput marks the output at the provided index as spent. Specifying an 126 // output index that does not exist will not have any effect. 127 func (entry *UtxoEntry) SpendOutput(outputIndex uint32) { 128 output, ok := entry.sparseOutputs[outputIndex] 129 if !ok { 130 return 131 } 132 133 // Nothing to do if the output is already spent. 134 if output.spent { 135 return 136 } 137 138 entry.modified = true 139 output.spent = true 140 } 141 142 // IsFullySpent returns whether or not the transaction the utxo entry represents 143 // is fully spent. 144 func (entry *UtxoEntry) IsFullySpent() bool { 145 // The entry is not fully spent if any of the outputs are unspent. 146 for _, output := range entry.sparseOutputs { 147 if !output.spent { 148 return false 149 } 150 } 151 152 return true 153 } 154 155 // AmountByIndex returns the amount of the provided output index. 156 // 157 // Returns 0 if the output index references an output that does not exist 158 // either due to it being invalid or because the output is not part of the view 159 // due to previously being spent/pruned. 160 func (entry *UtxoEntry) AmountByIndex(outputIndex uint32) int64 { 161 output, ok := entry.sparseOutputs[outputIndex] 162 if !ok { 163 return 0 164 } 165 166 return output.amount 167 } 168 169 // ScriptVersionByIndex returns the public key script for the provided output 170 // index. 171 // 172 // Returns 0 if the output index references an output that does not exist 173 // either due to it being invalid or because the output is not part of the view 174 // due to previously being spent/pruned. 175 func (entry *UtxoEntry) ScriptVersionByIndex(outputIndex uint32) uint16 { 176 output, ok := entry.sparseOutputs[outputIndex] 177 if !ok { 178 return 0 179 } 180 181 return output.scriptVersion 182 } 183 184 // PkScriptByIndex returns the public key script for the provided output index. 185 // 186 // Returns nil if the output index references an output that does not exist 187 // either due to it being invalid or because the output is not part of the view 188 // due to previously being spent/pruned. 189 func (entry *UtxoEntry) PkScriptByIndex(outputIndex uint32) []byte { 190 output, ok := entry.sparseOutputs[outputIndex] 191 if !ok { 192 return nil 193 } 194 195 // Ensure the output is decompressed before returning the script. 196 output.maybeDecompress(currentCompressionVersion) 197 return output.pkScript 198 } 199 200 // Clone returns a deep copy of the utxo entry. 201 func (entry *UtxoEntry) Clone() *UtxoEntry { 202 if entry == nil { 203 return nil 204 } 205 206 newEntry := &UtxoEntry{ 207 stakeExtra: make([]byte, len(entry.stakeExtra)), 208 txVersion: entry.txVersion, 209 height: entry.height, 210 index: entry.index, 211 txType: entry.txType, 212 isCoinBase: entry.isCoinBase, 213 hasExpiry: entry.hasExpiry, 214 sparseOutputs: make(map[uint32]*utxoOutput), 215 } 216 copy(newEntry.stakeExtra, entry.stakeExtra) 217 for outputIndex, output := range entry.sparseOutputs { 218 newEntry.sparseOutputs[outputIndex] = &utxoOutput{ 219 pkScript: output.pkScript, 220 amount: output.amount, 221 scriptVersion: output.scriptVersion, 222 compressed: output.compressed, 223 spent: output.spent, 224 } 225 } 226 return newEntry 227 } 228 229 // newUtxoEntry returns a new unspent transaction output entry with the provided 230 // coinbase flag and block height ready to have unspent outputs added. 231 func newUtxoEntry(txVersion uint16, height uint32, index uint32, isCoinBase bool, hasExpiry bool, txType stake.TxType) *UtxoEntry { 232 return &UtxoEntry{ 233 sparseOutputs: make(map[uint32]*utxoOutput), 234 txVersion: txVersion, 235 height: height, 236 index: index, 237 isCoinBase: isCoinBase, 238 hasExpiry: hasExpiry, 239 txType: txType, 240 } 241 } 242 243 // UtxoViewpoint represents a view into the set of unspent transaction outputs 244 // from a specific point of view in the chain. For example, it could be for 245 // the end of the main chain, some point in the history of the main chain, or 246 // down a side chain. 247 // 248 // The unspent outputs are needed by other transactions for things such as 249 // script validation and double spend prevention. 250 type UtxoViewpoint struct { 251 entries map[chainhash.Hash]*UtxoEntry 252 bestHash chainhash.Hash 253 } 254 255 // BestHash returns the hash of the best block in the chain the view currently 256 // respresents. 257 func (view *UtxoViewpoint) BestHash() *chainhash.Hash { 258 return &view.bestHash 259 } 260 261 // SetBestHash sets the hash of the best block in the chain the view currently 262 // respresents. 263 func (view *UtxoViewpoint) SetBestHash(hash *chainhash.Hash) { 264 view.bestHash = *hash 265 } 266 267 // LookupEntry returns information about a given transaction according to the 268 // current state of the view. It will return nil if the passed transaction 269 // hash does not exist in the view or is otherwise not available such as when 270 // it has been disconnected during a reorg. 271 func (view *UtxoViewpoint) LookupEntry(txHash *chainhash.Hash) *UtxoEntry { 272 entry, ok := view.entries[*txHash] 273 if !ok { 274 return nil 275 } 276 277 return entry 278 } 279 280 // AddTxOuts adds all outputs in the passed transaction which are not provably 281 // unspendable to the view. When the view already has entries for any of the 282 // outputs, they are simply marked unspent. All fields will be updated for 283 // existing entries since it's possible it has changed during a reorg. 284 func (view *UtxoViewpoint) AddTxOuts(tx *dcrutil.Tx, blockHeight int64, blockIndex uint32) { 285 // When there are not already any utxos associated with the transaction, 286 // add a new entry for it to the view. 287 entry := view.LookupEntry(tx.Hash()) 288 if entry == nil { 289 msgTx := tx.MsgTx() 290 txType := stake.DetermineTxType(msgTx) 291 entry = newUtxoEntry(msgTx.Version, uint32(blockHeight), 292 blockIndex, standalone.IsCoinBaseTx(msgTx), msgTx.Expiry != 0, 293 txType) 294 if txType == stake.TxTypeSStx { 295 stakeExtra := make([]byte, serializeSizeForMinimalOutputs(tx)) 296 putTxToMinimalOutputs(stakeExtra, tx) 297 entry.stakeExtra = stakeExtra 298 } 299 view.entries[*tx.Hash()] = entry 300 } else { 301 entry.height = uint32(blockHeight) 302 entry.index = blockIndex 303 } 304 entry.modified = true 305 306 // Loop all of the transaction outputs and add those which are not 307 // provably unspendable. 308 for txOutIdx, txOut := range tx.MsgTx().TxOut { 309 // TODO allow pruning of stake utxs after all other outputs are spent 310 if txscript.IsUnspendable(txOut.Value, txOut.PkScript) { 311 continue 312 } 313 314 // Update existing entries. All fields are updated because it's 315 // possible (although extremely unlikely) that the existing 316 // entry is being replaced by a different transaction with the 317 // same hash. This is allowed so long as the previous 318 // transaction is fully spent. 319 if output, ok := entry.sparseOutputs[uint32(txOutIdx)]; ok { 320 output.spent = false 321 output.amount = txOut.Value 322 output.scriptVersion = txOut.Version 323 output.pkScript = txOut.PkScript 324 output.compressed = false 325 continue 326 } 327 328 // Add the unspent transaction output. 329 entry.sparseOutputs[uint32(txOutIdx)] = &utxoOutput{ 330 spent: false, 331 amount: txOut.Value, 332 scriptVersion: txOut.Version, 333 pkScript: txOut.PkScript, 334 compressed: false, 335 } 336 } 337 } 338 339 // connectTransaction updates the view by adding all new utxos created by the 340 // passed transaction and marking all utxos that the transactions spend as 341 // spent. In addition, when the 'stxos' argument is not nil, it will be updated 342 // to append an entry for each spent txout. An error will be returned if the 343 // view does not contain the required utxos. 344 func (view *UtxoViewpoint) connectTransaction(tx *dcrutil.Tx, blockHeight int64, blockIndex uint32, stxos *[]spentTxOut) error { 345 // Coinbase transactions don't have any inputs to spend. 346 msgTx := tx.MsgTx() 347 if standalone.IsCoinBaseTx(msgTx) { 348 // Add the transaction's outputs as available utxos. 349 view.AddTxOuts(tx, blockHeight, blockIndex) 350 return nil 351 } 352 353 // Spend the referenced utxos by marking them spent in the view and, 354 // if a slice was provided for the spent txout details, append an entry 355 // to it. 356 isVote := stake.IsSSGen(msgTx) 357 for txInIdx, txIn := range msgTx.TxIn { 358 // Ignore stakebase since it has no input. 359 if isVote && txInIdx == 0 { 360 continue 361 } 362 363 // Ensure the referenced utxo exists in the view. This should 364 // never happen unless there is a bug is introduced in the code. 365 originIndex := txIn.PreviousOutPoint.Index 366 entry := view.entries[txIn.PreviousOutPoint.Hash] 367 if entry == nil { 368 return AssertError(fmt.Sprintf("view missing input %v", 369 txIn.PreviousOutPoint)) 370 } 371 entry.SpendOutput(originIndex) 372 373 // Don't create the stxo details if not requested. 374 if stxos == nil { 375 continue 376 } 377 378 // Populate the stxo details using the utxo entry. When the 379 // transaction is fully spent, set the additional stxo fields 380 // accordingly since those details will no longer be available 381 // in the utxo set. 382 var stxo = spentTxOut{ 383 compressed: false, 384 amount: txIn.ValueIn, 385 scriptVersion: entry.ScriptVersionByIndex(originIndex), 386 pkScript: entry.PkScriptByIndex(originIndex), 387 } 388 if entry.IsFullySpent() { 389 stxo.txVersion = entry.TxVersion() 390 stxo.height = uint32(entry.BlockHeight()) 391 stxo.index = entry.BlockIndex() 392 stxo.isCoinBase = entry.IsCoinBase() 393 stxo.hasExpiry = entry.HasExpiry() 394 stxo.txType = entry.TransactionType() 395 stxo.txFullySpent = true 396 397 if entry.txType == stake.TxTypeSStx { 398 stxo.stakeExtra = entry.stakeExtra 399 } 400 } 401 402 // Append the entry to the provided spent txouts slice. 403 *stxos = append(*stxos, stxo) 404 } 405 406 // Add the transaction's outputs as available utxos. 407 view.AddTxOuts(tx, blockHeight, blockIndex) 408 409 return nil 410 } 411 412 // disconnectTransactions updates the view by removing all utxos created by 413 // the transactions in either the regular or stake tree of the block, depending 414 // on the flag, and unspending all of the txos spent by those same transactions 415 // by using the provided spent txo information. 416 func (view *UtxoViewpoint) disconnectTransactions(block *dcrutil.Block, stxos []spentTxOut, stakeTree bool) error { 417 // Choose which transaction tree to use and the appropriate offset into the 418 // spent transaction outputs that corresponds to them depending on the flag. 419 // Transactions in the stake tree are spent before transactions in the 420 // regular tree, thus skip all of the outputs spent by the regular tree when 421 // disconnecting stake transactions. 422 stxoIdx := len(stxos) - 1 423 transactions := block.Transactions() 424 if stakeTree { 425 stxoIdx = len(stxos) - countSpentRegularOutputs(block) - 1 426 transactions = block.STransactions() 427 } 428 429 for txIdx := len(transactions) - 1; txIdx > -1; txIdx-- { 430 tx := transactions[txIdx] 431 msgTx := tx.MsgTx() 432 txType := stake.TxTypeRegular 433 if stakeTree { 434 txType = stake.DetermineTxType(msgTx) 435 } 436 isVote := txType == stake.TxTypeSSGen 437 438 // Clear this transaction from the view if it already exists or create a 439 // new empty entry for when it does not. This is done because the code 440 // relies on its existence in the view in order to signal modifications 441 // have happened. 442 isCoinbase := !stakeTree && txIdx == 0 443 entry := view.entries[*tx.Hash()] 444 if entry == nil { 445 entry = newUtxoEntry(msgTx.Version, uint32(block.Height()), 446 uint32(txIdx), isCoinbase, msgTx.Expiry != 0, txType) 447 view.entries[*tx.Hash()] = entry 448 } 449 entry.modified = true 450 entry.sparseOutputs = make(map[uint32]*utxoOutput) 451 452 // Loop backwards through all of the transaction inputs (except for the 453 // coinbase which has no inputs) and unspend the referenced txos. This 454 // is necessary to match the order of the spent txout entries. 455 if isCoinbase { 456 continue 457 } 458 for txInIdx := len(msgTx.TxIn) - 1; txInIdx > -1; txInIdx-- { 459 // Ignore stakebase since it has no input. 460 if isVote && txInIdx == 0 { 461 continue 462 } 463 464 // Ensure the spent txout index is decremented to stay in sync with 465 // the transaction input. 466 stxo := &stxos[stxoIdx] 467 stxoIdx-- 468 469 // When there is not already an entry for the referenced transaction 470 // in the view, it means it was fully spent, so create a new utxo 471 // entry in order to resurrect it. 472 txIn := msgTx.TxIn[txInIdx] 473 originHash := &txIn.PreviousOutPoint.Hash 474 originIndex := txIn.PreviousOutPoint.Index 475 entry := view.entries[*originHash] 476 if entry == nil { 477 if !stxo.txFullySpent { 478 return AssertError(fmt.Sprintf("tried to revive unspent "+ 479 "tx %v from non-fully spent stx entry", originHash)) 480 } 481 entry = newUtxoEntry(msgTx.Version, stxo.height, stxo.index, 482 stxo.isCoinBase, stxo.hasExpiry, stxo.txType) 483 if stxo.txType == stake.TxTypeSStx { 484 entry.stakeExtra = stxo.stakeExtra 485 } 486 view.entries[*originHash] = entry 487 } 488 489 // Mark the entry as modified since it is either new or will be 490 // changed below. 491 entry.modified = true 492 493 // Restore the specific utxo using the stxo data from the spend 494 // journal if it doesn't already exist in the view. 495 output, ok := entry.sparseOutputs[originIndex] 496 if !ok { 497 // Add the unspent transaction output. 498 entry.sparseOutputs[originIndex] = &utxoOutput{ 499 compressed: stxo.compressed, 500 spent: false, 501 amount: txIn.ValueIn, 502 scriptVersion: stxo.scriptVersion, 503 pkScript: stxo.pkScript, 504 } 505 continue 506 } 507 508 // Mark the existing referenced transaction output as unspent. 509 output.spent = false 510 } 511 } 512 513 return nil 514 } 515 516 // disconnectRegularTransactions updates the view by removing all utxos created 517 // by the transactions in regular tree of the provided block and unspending all 518 // of the txos spent by those same transactions by using the provided spent txo 519 // information. 520 func (view *UtxoViewpoint) disconnectRegularTransactions(block *dcrutil.Block, stxos []spentTxOut) error { 521 return view.disconnectTransactions(block, stxos, false) 522 } 523 524 // disconnectStakeTransactions updates the view by removing all utxos created 525 // by the transactions in stake tree of the provided block and unspending all 526 // of the txos spent by those same transactions by using the provided spent txo 527 // information. 528 func (view *UtxoViewpoint) disconnectStakeTransactions(block *dcrutil.Block, stxos []spentTxOut) error { 529 return view.disconnectTransactions(block, stxos, true) 530 } 531 532 // disconnectDisapprovedBlock updates the view by disconnecting all of the 533 // transactions in the regular tree of the passed block. 534 // 535 // Disconnecting a transaction entails removing the utxos created by it and 536 // restoring the outputs spent by it with the help of the provided spent txo 537 // information. 538 //func (view *UtxoViewpoint) disconnectDisapprovedBlock(db database.DB, block *dcrutil.Block, stxos []spentTxOut) error { 539 func (view *UtxoViewpoint) disconnectDisapprovedBlock(db database.DB, block *dcrutil.Block) error { 540 // Load all of the spent txos for the block from the database spend journal. 541 var stxos []spentTxOut 542 err := db.View(func(dbTx database.Tx) error { 543 var err error 544 stxos, err = dbFetchSpendJournalEntry(dbTx, block) 545 return err 546 }) 547 if err != nil { 548 return err 549 } 550 551 // Load all of the utxos referenced by the inputs for all transactions in 552 // the block that don't already exist in the utxo view from the database. 553 err = view.fetchRegularInputUtxos(db, block) 554 if err != nil { 555 return err 556 } 557 558 // Sanity check the correct number of stxos are provided. 559 if len(stxos) != countSpentOutputs(block) { 560 panicf("provided %v stxos for block %v (height %v) which spends %v "+ 561 "outputs", len(stxos), block.Hash(), block.MsgBlock().Header.Height, 562 countSpentOutputs(block)) 563 } 564 565 return view.disconnectRegularTransactions(block, stxos) 566 } 567 568 // connectBlock updates the view by potentially disconnecting all of the 569 // transactions in the regular tree of the parent block of the passed block in 570 // the case the passed block disapproves the parent block, connecting all of 571 // transactions in both the regular and stake trees of the passed block, and 572 // setting the best hash for the view to the passed block. 573 // 574 // Connecting a transaction entails marking all utxos it spends as spent, and 575 // adding all of the new utxos created by it. 576 // 577 // Disconnecting a transaction entails removing the utxos created by it and 578 // restoring the outputs spent by it with the help of the provided spent txo 579 // information. 580 // 581 // In addition, when the 'stxos' argument is not nil, it will be updated to 582 // append an entry for each spent txout. 583 func (view *UtxoViewpoint) connectBlock(db database.DB, block, parent *dcrutil.Block, stxos *[]spentTxOut) error { 584 // Disconnect the transactions in the regular tree of the parent block if 585 // the passed block disapproves it. 586 if !headerApprovesParent(&block.MsgBlock().Header) { 587 err := view.disconnectDisapprovedBlock(db, parent) 588 if err != nil { 589 return err 590 } 591 } 592 593 // Load all of the utxos referenced by the inputs for all transactions in 594 // the block that don't already exist in the utxo view from the database. 595 err := view.fetchInputUtxos(db, block) 596 if err != nil { 597 return err 598 } 599 600 // Connect all of the transactions in both the regular and stake trees of 601 // the block. Notice that the stake tree is connected before the regular 602 // tree. This means that stake transactions are not able to redeem outputs 603 // of transactions created in the regular tree of the same block, which is 604 // important since the regular tree may be disapproved by the subsequent 605 // block while the stake tree must remain valid. 606 for i, stx := range block.STransactions() { 607 err := view.connectTransaction(stx, block.Height(), uint32(i), stxos) 608 if err != nil { 609 return err 610 } 611 } 612 for i, tx := range block.Transactions() { 613 err := view.connectTransaction(tx, block.Height(), uint32(i), stxos) 614 if err != nil { 615 return err 616 } 617 } 618 619 // Update the best hash for view to include this block since all of its 620 // transactions have been connected. 621 view.SetBestHash(block.Hash()) 622 return nil 623 } 624 625 // disconnectBlock updates the view by disconnecting all transactions in both 626 // the regular and stake trees of the passed block, in the case the block 627 // disapproves the parent block, reconnecting all of the transactions in the 628 // regular tree of the previous block, and setting the best hash for the view to 629 // the parent block. 630 // 631 // Connecting a transaction entails marking all utxos it spends as spent, and 632 // adding all of the new utxos created by it. 633 // 634 // Disconnecting a transaction entails removing the utxos created by it and 635 // restoring the outputs spent by it with the help of the provided spent txo 636 // information. 637 // 638 // Note that, unlike block connection, the spent transaction output (stxo) 639 // information is required and failure to provide it will result in an assertion 640 // panic. 641 func (view *UtxoViewpoint) disconnectBlock(db database.DB, block, parent *dcrutil.Block, stxos []spentTxOut) error { 642 // Sanity check the correct number of stxos are provided. 643 if len(stxos) != countSpentOutputs(block) { 644 panicf("provided %v stxos for block %v (height %v) which spends %v "+ 645 "outputs", len(stxos), block.Hash(), block.MsgBlock().Header.Height, 646 countSpentOutputs(block)) 647 } 648 649 // Load all of the utxos referenced by the inputs for all transactions in 650 // the block don't already exist in the utxo view from the database. 651 err := view.fetchInputUtxos(db, block) 652 if err != nil { 653 return err 654 } 655 656 // Disconnect all of the transactions in both the regular and stake trees of 657 // the block. Notice that the regular tree is disconnected before the stake 658 // tree since that is the reverse of how they are connected. 659 err = view.disconnectRegularTransactions(block, stxos) 660 if err != nil { 661 return err 662 } 663 err = view.disconnectStakeTransactions(block, stxos) 664 if err != nil { 665 return err 666 } 667 668 // Reconnect the transactions in the regular tree of the parent block if the 669 // block that is being disconnected disapproves it. 670 if !headerApprovesParent(&block.MsgBlock().Header) { 671 // Load all of the utxos referenced by the inputs for all transactions 672 // in the regular tree of the parent block that don't already exist in 673 // the utxo view from the database. 674 err := view.fetchRegularInputUtxos(db, parent) 675 if err != nil { 676 return err 677 } 678 679 for i, tx := range parent.Transactions() { 680 err := view.connectTransaction(tx, parent.Height(), uint32(i), nil) 681 if err != nil { 682 return err 683 } 684 } 685 } 686 687 // Update the best hash for view to include this block since all of its 688 // transactions have been connected. 689 view.SetBestHash(&block.MsgBlock().Header.PrevBlock) 690 return nil 691 } 692 693 // Entries returns the underlying map that stores of all the utxo entries. 694 func (view *UtxoViewpoint) Entries() map[chainhash.Hash]*UtxoEntry { 695 return view.entries 696 } 697 698 // commit prunes all entries marked modified that are now fully spent and marks 699 // all entries as unmodified. 700 func (view *UtxoViewpoint) commit() { 701 for txHash, entry := range view.entries { 702 if entry == nil || (entry.modified && entry.IsFullySpent()) { 703 delete(view.entries, txHash) 704 continue 705 } 706 707 entry.modified = false 708 } 709 } 710 711 // viewFilteredSet represents a set of utxos to fetch from the database that are 712 // not already in a view. 713 type viewFilteredSet map[chainhash.Hash]struct{} 714 715 // add conditionally adds the provided utxo hash to the set if it does not 716 // already exist in the provided view. 717 func (set viewFilteredSet) add(view *UtxoViewpoint, hash *chainhash.Hash) { 718 if _, ok := view.entries[*hash]; !ok { 719 set[*hash] = struct{}{} 720 } 721 } 722 723 // fetchUtxosMain fetches unspent transaction output data about the provided 724 // set of transactions from the point of view of the end of the main chain at 725 // the time of the call. 726 // 727 // Upon completion of this function, the view will contain an entry for each 728 // requested transaction. Fully spent transactions, or those which otherwise 729 // don't exist, will result in a nil entry in the view. 730 func (view *UtxoViewpoint) fetchUtxosMain(db database.DB, filteredSet viewFilteredSet) error { 731 // Nothing to do if there are no requested hashes. 732 if len(filteredSet) == 0 { 733 return nil 734 } 735 736 // Load the unspent transaction output information for the requested set 737 // of transactions from the point of view of the end of the main chain. 738 // 739 // NOTE: Missing entries are not considered an error here and instead 740 // will result in nil entries in the view. This is intentionally done 741 // since other code uses the presence of an entry in the store as a way 742 // to optimize spend and unspend updates to apply only to the specific 743 // utxos that the caller needs access to. 744 return db.View(func(dbTx database.Tx) error { 745 for hash := range filteredSet { 746 hashCopy := hash 747 entry, err := dbFetchUtxoEntry(dbTx, &hashCopy) 748 if err != nil { 749 return err 750 } 751 752 view.entries[hash] = entry 753 } 754 755 return nil 756 }) 757 } 758 759 // addRegularInputUtxos adds any outputs of transactions in the regular tree of 760 // the provided block that are referenced by inputs of transactions that are 761 // located later in the regular tree of the block and returns a set of the 762 // referenced outputs that are not already in the view and thus need to be 763 // fetched from the database. 764 func (view *UtxoViewpoint) addRegularInputUtxos(block *dcrutil.Block) viewFilteredSet { 765 // Build a map of in-flight transactions because some of the inputs in the 766 // regular transaction tree of this block could be referencing other 767 // transactions earlier in the block which are not yet in the chain. 768 txInFlight := map[chainhash.Hash]int{} 769 regularTxns := block.Transactions() 770 for i, tx := range regularTxns { 771 txInFlight[*tx.Hash()] = i 772 } 773 774 // Loop through all of the inputs of the transactions in the regular 775 // transaction tree (except for the coinbase which has no inputs) collecting 776 // them into sets of what is needed and what is already known (in-flight). 777 filteredSet := make(viewFilteredSet) 778 for i, tx := range regularTxns[1:] { 779 for _, txIn := range tx.MsgTx().TxIn { 780 // It is acceptable for a transaction input in the regular tree to 781 // reference the output of another transaction in the regular tree 782 // of this block only if the referenced transaction comes before the 783 // current one in this block. Add the outputs of the referenced 784 // transaction as available utxos when this is the case. Otherwise, 785 // the utxo details are still needed. 786 // 787 // NOTE: The >= is correct here because i is one less than the 788 // actual position of the transaction within the block due to 789 // skipping the coinbase. 790 originHash := &txIn.PreviousOutPoint.Hash 791 if inFlightIndex, ok := txInFlight[*originHash]; ok && 792 i >= inFlightIndex { 793 794 originTx := regularTxns[inFlightIndex] 795 view.AddTxOuts(originTx, block.Height(), uint32(inFlightIndex)) 796 continue 797 } 798 799 // Only request entries that are not already in the view from the 800 // database. 801 filteredSet.add(view, originHash) 802 } 803 } 804 805 return filteredSet 806 } 807 808 // fetchRegularInputUtxos loads utxo details about the input transactions 809 // referenced by the transactions in the regular tree of the given block into 810 // the view from the database as needed. In particular, referenced entries that 811 // are earlier in the block are added to the view and entries that are already 812 // in the view are not modified. 813 func (view *UtxoViewpoint) fetchRegularInputUtxos(db database.DB, block *dcrutil.Block) error { 814 // Add any outputs of transactions in the regular tree of the block that are 815 // referenced by inputs of transactions that are located later in the tree 816 // and fetch any inputs that are not already in the view from the database. 817 filteredSet := view.addRegularInputUtxos(block) 818 return view.fetchUtxosMain(db, filteredSet) 819 } 820 821 // fetchInputUtxos loads utxo details about the input transactions referenced 822 // by the transactions in both the regular and stake trees of the given block 823 // into the view from the database as needed. In the case of regular tree, 824 // referenced entries that are earlier in the regular tree of the block are 825 // added to the view. In all cases, entries that are already in the view are 826 // not modified. 827 func (view *UtxoViewpoint) fetchInputUtxos(db database.DB, block *dcrutil.Block) error { 828 // Add any outputs of transactions in the regular tree of the block that are 829 // referenced by inputs of transactions that are located later in the tree 830 // and, while doing so, determine which inputs are not already in the view 831 // and thus need to be fetched from the database. 832 filteredSet := view.addRegularInputUtxos(block) 833 834 // Loop through all of the inputs of the transaction in the stake tree and 835 // add those that aren't already known to the set of what is needed. 836 // 837 // Note that, unlike in the regular transaction tree, transactions in the 838 // stake tree are not allowed to access outputs of transactions earlier in 839 // the block. This applies to both transactions earlier in the stake tree 840 // as well as those in the regular tree. 841 for _, stx := range block.STransactions() { 842 isVote := stake.IsSSGen(stx.MsgTx()) 843 for txInIdx, txIn := range stx.MsgTx().TxIn { 844 // Ignore stakebase since it has no input. 845 if isVote && txInIdx == 0 { 846 continue 847 } 848 849 // Only request entries that are not already in the view 850 // from the database. 851 originHash := &txIn.PreviousOutPoint.Hash 852 filteredSet.add(view, originHash) 853 } 854 } 855 856 // Request the input utxos from the database. 857 return view.fetchUtxosMain(db, filteredSet) 858 } 859 860 // clone returns a deep copy of the view. 861 func (view *UtxoViewpoint) clone() *UtxoViewpoint { 862 clonedView := &UtxoViewpoint{ 863 entries: make(map[chainhash.Hash]*UtxoEntry), 864 bestHash: view.bestHash, 865 } 866 867 for txHash, entry := range view.entries { 868 clonedView.entries[txHash] = entry.Clone() 869 } 870 871 return clonedView 872 } 873 874 // NewUtxoViewpoint returns a new empty unspent transaction output view. 875 func NewUtxoViewpoint() *UtxoViewpoint { 876 return &UtxoViewpoint{ 877 entries: make(map[chainhash.Hash]*UtxoEntry), 878 } 879 } 880 881 // FetchUtxoView loads utxo details about the input transactions referenced by 882 // the passed transaction from the point of view of the end of the main chain 883 // while taking into account whether or not the transactions in the regular tree 884 // of the block just prior should be included or not depending on the provided 885 // flag. It also attempts to fetch the utxo details for the transaction itself 886 // so the returned view can be examined for duplicate unspent transaction 887 // outputs. 888 // 889 // This function is safe for concurrent access however the returned view is NOT. 890 func (b *BlockChain) FetchUtxoView(tx *dcrutil.Tx, includePrevRegularTxns bool) (*UtxoViewpoint, error) { 891 b.chainLock.RLock() 892 defer b.chainLock.RUnlock() 893 894 // The genesis block does not have any spendable transactions, so there 895 // can't possibly be any details about it. This is also necessary 896 // because the code below requires the parent block and the genesis 897 // block doesn't have one. 898 tip := b.bestChain.Tip() 899 view := NewUtxoViewpoint() 900 view.SetBestHash(&tip.hash) 901 if tip.height == 0 { 902 return view, nil 903 } 904 905 // Disconnect the transactions in the regular tree of the parent block if 906 // the caller requests it. In order to avoid the overhead of repeated 907 // lookups, only create a view with the changes once and cache it. 908 if !includePrevRegularTxns { 909 b.disapprovedViewLock.Lock() 910 if b.disapprovedView == nil || *b.disapprovedView.BestHash() != 911 tip.hash { 912 913 // Grab the parent of the current block. 914 parent, err := b.fetchMainChainBlockByNode(tip.parent) 915 if err != nil { 916 b.disapprovedViewLock.Unlock() 917 return nil, err 918 } 919 920 // Disconnect the transactions in the regular tree of the parent 921 // block. 922 err = view.disconnectDisapprovedBlock(b.db, parent) 923 if err != nil { 924 b.disapprovedViewLock.Unlock() 925 return nil, err 926 } 927 928 // Clone the view so the caller can safely mutate it. 929 b.disapprovedView = view.clone() 930 } else { 931 // Clone the view so the caller can safely mutate it. 932 view = b.disapprovedView.clone() 933 } 934 b.disapprovedViewLock.Unlock() 935 } 936 937 // Create a set of needed transactions based on those referenced by the 938 // inputs of the passed transaction. Also, add the passed transaction 939 // itself as a way for the caller to detect duplicates that are not fully 940 // spent. 941 filteredSet := make(viewFilteredSet) 942 filteredSet.add(view, tx.Hash()) 943 msgTx := tx.MsgTx() 944 if !standalone.IsCoinBaseTx(msgTx) { 945 isVote := stake.IsSSGen(msgTx) 946 for txInIdx, txIn := range msgTx.TxIn { 947 // Ignore stakebase since it has no input. 948 if isVote && txInIdx == 0 { 949 continue 950 } 951 952 filteredSet.add(view, &txIn.PreviousOutPoint.Hash) 953 } 954 } 955 956 err := view.fetchUtxosMain(b.db, filteredSet) 957 return view, err 958 } 959 960 // FetchUtxoEntry loads and returns the unspent transaction output entry for the 961 // passed hash from the point of view of the end of the main chain. 962 // 963 // NOTE: Requesting a hash for which there is no data will NOT return an error. 964 // Instead both the entry and the error will be nil. This is done to allow 965 // pruning of fully spent transactions. In practice this means the caller must 966 // check if the returned entry is nil before invoking methods on it. 967 // 968 // This function is safe for concurrent access however the returned entry (if 969 // any) is NOT. 970 func (b *BlockChain) FetchUtxoEntry(txHash *chainhash.Hash) (*UtxoEntry, error) { 971 b.chainLock.RLock() 972 defer b.chainLock.RUnlock() 973 974 var entry *UtxoEntry 975 err := b.db.View(func(dbTx database.Tx) error { 976 var err error 977 entry, err = dbFetchUtxoEntry(dbTx, txHash) 978 return err 979 }) 980 if err != nil { 981 return nil, err 982 } 983 984 return entry, nil 985 }