decred.org/dcrwallet/v3@v3.1.0/wallet/udb/txmined.go (about) 1 // Copyright (c) 2013-2015 The btcsuite developers 2 // Copyright (c) 2015-2021 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 udb 7 8 import ( 9 "bytes" 10 "crypto/rand" 11 "encoding/binary" 12 "fmt" 13 "math/bits" 14 "time" 15 16 "decred.org/dcrwallet/v3/errors" 17 "decred.org/dcrwallet/v3/internal/compat" 18 "decred.org/dcrwallet/v3/wallet/txauthor" 19 "decred.org/dcrwallet/v3/wallet/txrules" 20 "decred.org/dcrwallet/v3/wallet/txsizes" 21 "decred.org/dcrwallet/v3/wallet/walletdb" 22 "github.com/decred/dcrd/blockchain/stake/v5" 23 "github.com/decred/dcrd/chaincfg/chainhash" 24 "github.com/decred/dcrd/chaincfg/v3" 25 "github.com/decred/dcrd/crypto/ripemd160" 26 "github.com/decred/dcrd/dcrutil/v4" 27 gcs2 "github.com/decred/dcrd/gcs/v4" 28 "github.com/decred/dcrd/gcs/v4/blockcf2" 29 "github.com/decred/dcrd/txscript/v4" 30 "github.com/decred/dcrd/txscript/v4/stdaddr" 31 "github.com/decred/dcrd/txscript/v4/stdscript" 32 "github.com/decred/dcrd/wire" 33 ) 34 35 const ( 36 opNonstake = txscript.OP_NOP10 37 38 // The assumed output script version is defined to assist with refactoring 39 // to use actual script versions. 40 scriptVersionAssumed = 0 41 ) 42 43 // Block contains the minimum amount of data to uniquely identify any block on 44 // either the best or side chain. 45 type Block struct { 46 Hash chainhash.Hash 47 Height int32 48 } 49 50 // BlockMeta contains the unique identification for a block and any metadata 51 // pertaining to the block. At the moment, this additional metadata only 52 // includes the block time from the block header. 53 type BlockMeta struct { 54 Block 55 Time time.Time 56 VoteBits uint16 57 } 58 59 // blockRecord is an in-memory representation of the block record saved in the 60 // database. 61 type blockRecord struct { 62 Block 63 Time time.Time 64 VoteBits uint16 65 transactions []chainhash.Hash 66 } 67 68 // incidence records the block hash and blockchain height of a mined transaction. 69 // Since a transaction hash alone is not enough to uniquely identify a mined 70 // transaction (duplicate transaction hashes are allowed), the incidence is used 71 // instead. 72 type incidence struct { 73 txHash chainhash.Hash 74 block Block 75 } 76 77 // indexedIncidence records the transaction incidence and an input or output 78 // index. 79 type indexedIncidence struct { 80 incidence 81 index uint32 82 } 83 84 // credit describes a transaction output which was or is spendable by wallet. 85 type credit struct { 86 outPoint wire.OutPoint 87 block Block 88 amount dcrutil.Amount 89 change bool 90 spentBy indexedIncidence // Index == ^uint32(0) if unspent 91 opCode uint8 92 isCoinbase bool 93 hasExpiry bool 94 } 95 96 // TxRecord represents a transaction managed by the Store. 97 type TxRecord struct { 98 MsgTx wire.MsgTx 99 Hash chainhash.Hash 100 Received time.Time 101 SerializedTx []byte // Optional: may be nil 102 TxType stake.TxType 103 Unpublished bool 104 } 105 106 // NewTxRecord creates a new transaction record that may be inserted into the 107 // store. It uses memoization to save the transaction hash and the serialized 108 // transaction. 109 func NewTxRecord(serializedTx []byte, received time.Time) (*TxRecord, error) { 110 rec := &TxRecord{ 111 Received: received, 112 SerializedTx: serializedTx, 113 } 114 err := rec.MsgTx.Deserialize(bytes.NewReader(serializedTx)) 115 if err != nil { 116 return nil, err 117 } 118 rec.TxType = stake.DetermineTxType(&rec.MsgTx) 119 hash := rec.MsgTx.TxHash() 120 copy(rec.Hash[:], hash[:]) 121 return rec, nil 122 } 123 124 // NewTxRecordFromMsgTx creates a new transaction record that may be inserted 125 // into the store. 126 func NewTxRecordFromMsgTx(msgTx *wire.MsgTx, received time.Time) (*TxRecord, error) { 127 var buf bytes.Buffer 128 buf.Grow(msgTx.SerializeSize()) 129 err := msgTx.Serialize(&buf) 130 if err != nil { 131 return nil, err 132 } 133 rec := &TxRecord{ 134 MsgTx: *msgTx, 135 Received: received, 136 SerializedTx: buf.Bytes(), 137 } 138 rec.TxType = stake.DetermineTxType(&rec.MsgTx) 139 hash := rec.MsgTx.TxHash() 140 copy(rec.Hash[:], hash[:]) 141 return rec, nil 142 } 143 144 // MultisigOut represents a spendable multisignature outpoint contain 145 // a script hash. 146 type MultisigOut struct { 147 OutPoint *wire.OutPoint 148 Tree int8 149 ScriptHash [ripemd160.Size]byte 150 M uint8 151 N uint8 152 TxHash chainhash.Hash 153 BlockHash chainhash.Hash 154 BlockHeight uint32 155 Amount dcrutil.Amount 156 Spent bool 157 SpentBy chainhash.Hash 158 SpentByIndex uint32 159 } 160 161 // Credit is the type representing a transaction output which was spent or 162 // is still spendable by wallet. A UTXO is an unspent Credit, but not all 163 // Credits are UTXOs. 164 type Credit struct { 165 wire.OutPoint 166 BlockMeta 167 Amount dcrutil.Amount 168 PkScript []byte // TODO: script version 169 Received time.Time 170 FromCoinBase bool 171 HasExpiry bool 172 } 173 174 // Store implements a transaction store for storing and managing wallet 175 // transactions. 176 type Store struct { 177 chainParams *chaincfg.Params 178 acctLookupFunc func(walletdb.ReadBucket, stdaddr.Address) (uint32, error) 179 manager *Manager 180 } 181 182 // MainChainTip returns the hash and height of the currently marked tip-most 183 // block of the main chain. 184 func (s *Store) MainChainTip(dbtx walletdb.ReadTx) (chainhash.Hash, int32) { 185 ns := dbtx.ReadBucket(wtxmgrBucketKey) 186 var hash chainhash.Hash 187 tipHash := ns.Get(rootTipBlock) 188 copy(hash[:], tipHash) 189 190 header := ns.NestedReadBucket(bucketHeaders).Get(hash[:]) 191 height := extractBlockHeaderHeight(header) 192 193 return hash, height 194 } 195 196 // ExtendMainChain inserts a block header and compact filter into the database. 197 // It must connect to the existing tip block. 198 // 199 // If the block is already inserted and part of the main chain, an errors.Exist 200 // error is returned. 201 // 202 // The main chain tip may not be extended unless compact filters have been saved 203 // for all existing main chain blocks. 204 func (s *Store) ExtendMainChain(ns walletdb.ReadWriteBucket, header *wire.BlockHeader, f *gcs2.FilterV2) error { 205 height := int32(header.Height) 206 if height < 1 { 207 return errors.E(errors.Invalid, "block 0 cannot be added") 208 } 209 v := ns.Get(rootHaveCFilters) 210 if len(v) != 1 || v[0] != 1 { 211 return errors.E(errors.Invalid, "main chain may not be extended without first saving all previous cfilters") 212 } 213 214 headerBucket := ns.NestedReadWriteBucket(bucketHeaders) 215 216 blockHash := header.BlockHash() 217 218 currentTipHash := ns.Get(rootTipBlock) 219 if !bytes.Equal(header.PrevBlock[:], currentTipHash) { 220 // Return a special error if it is a duplicate of an existing block in 221 // the main chain (NOT the headers bucket, since headers are never 222 // pruned). 223 _, v := existsBlockRecord(ns, height) 224 if v != nil && bytes.Equal(extractRawBlockRecordHash(v), blockHash[:]) { 225 return errors.E(errors.Exist, "block already recorded in main chain") 226 } 227 return errors.E(errors.Invalid, "not direct child of current tip") 228 } 229 // Also check that the height is one more than the current height 230 // recorded by the current tip. 231 currentTipHeader := headerBucket.Get(currentTipHash) 232 currentTipHeight := extractBlockHeaderHeight(currentTipHeader) 233 if currentTipHeight+1 != height { 234 return errors.E(errors.Invalid, "invalid height for next block") 235 } 236 237 var err error 238 if approvesParent(header.VoteBits) { 239 err = stakeValidate(ns, currentTipHeight) 240 } else { 241 err = stakeInvalidate(ns, currentTipHeight) 242 } 243 if err != nil { 244 return err 245 } 246 247 // Add the header 248 var headerBuffer bytes.Buffer 249 err = header.Serialize(&headerBuffer) 250 if err != nil { 251 return err 252 } 253 err = headerBucket.Put(blockHash[:], headerBuffer.Bytes()) 254 if err != nil { 255 return errors.E(errors.IO, err) 256 } 257 258 // Update the tip block 259 err = ns.Put(rootTipBlock, blockHash[:]) 260 if err != nil { 261 return errors.E(errors.IO, err) 262 } 263 264 // Add an empty block record 265 blockKey := keyBlockRecord(height) 266 blockVal := valueBlockRecordEmptyFromHeader(blockHash[:], headerBuffer.Bytes()) 267 err = putRawBlockRecord(ns, blockKey, blockVal) 268 if err != nil { 269 return err 270 } 271 272 // Save the compact filter. 273 bcf2Key := blockcf2.Key(&header.MerkleRoot) 274 return putRawCFilter(ns, blockHash[:], valueRawCFilter2(bcf2Key, f.Bytes())) 275 } 276 277 // ProcessedTxsBlockMarker returns the hash of the block which records the last 278 // block after the genesis block which has been recorded as being processed for 279 // relevant transactions. 280 func (s *Store) ProcessedTxsBlockMarker(dbtx walletdb.ReadTx) *chainhash.Hash { 281 ns := dbtx.ReadBucket(wtxmgrBucketKey) 282 var h chainhash.Hash 283 copy(h[:], ns.Get(rootLastTxsBlock)) 284 return &h 285 } 286 287 // UpdateProcessedTxsBlockMarker updates the hash of the block recording the 288 // final block since the genesis block for which all transactions have been 289 // processed. Hash must describe a main chain block. This does not modify the 290 // database if hash has a lower block height than the main chain fork point of 291 // the existing marker. 292 func (s *Store) UpdateProcessedTxsBlockMarker(dbtx walletdb.ReadWriteTx, hash *chainhash.Hash) error { 293 ns := dbtx.ReadWriteBucket(wtxmgrBucketKey) 294 prev := s.ProcessedTxsBlockMarker(dbtx) 295 for { 296 mainChain, _ := s.BlockInMainChain(dbtx, prev) 297 if mainChain { 298 break 299 } 300 h, err := s.GetBlockHeader(dbtx, prev) 301 if err != nil { 302 return err 303 } 304 prev = &h.PrevBlock 305 } 306 prevHeader, err := s.GetBlockHeader(dbtx, prev) 307 if err != nil { 308 return err 309 } 310 if mainChain, _ := s.BlockInMainChain(dbtx, hash); !mainChain { 311 return errors.E(errors.Invalid, errors.Errorf("%v is not a main chain block", hash)) 312 } 313 header, err := s.GetBlockHeader(dbtx, hash) 314 if err != nil { 315 return err 316 } 317 if header.Height > prevHeader.Height { 318 err := ns.Put(rootLastTxsBlock, hash[:]) 319 if err != nil { 320 return errors.E(errors.IO, err) 321 } 322 } 323 return nil 324 } 325 326 // IsMissingMainChainCFilters returns whether all compact filters for main chain 327 // blocks have been recorded to the database after the upgrade which began to 328 // require them to extend the main chain. If compact filters are missing, they 329 // must be added using InsertMissingCFilters. 330 func (s *Store) IsMissingMainChainCFilters(dbtx walletdb.ReadTx) bool { 331 v := dbtx.ReadBucket(wtxmgrBucketKey).Get(rootHaveCFilters) 332 return len(v) != 1 || v[0] == 0 333 } 334 335 // MissingCFiltersHeight returns the first main chain block height 336 // with a missing cfilter. Errors with NotExist when all main chain 337 // blocks record cfilters. 338 func (s *Store) MissingCFiltersHeight(dbtx walletdb.ReadTx) (int32, error) { 339 ns := dbtx.ReadBucket(wtxmgrBucketKey) 340 c := ns.NestedReadBucket(bucketBlocks).ReadCursor() 341 defer c.Close() 342 for k, v := c.First(); k != nil; k, v = c.Next() { 343 hash := extractRawBlockRecordHash(v) 344 _, _, err := fetchRawCFilter2(ns, hash) 345 if errors.Is(err, errors.NotExist) { 346 height := int32(byteOrder.Uint32(k)) 347 return height, nil 348 } 349 } 350 return 0, errors.E(errors.NotExist) 351 } 352 353 // InsertMissingCFilters records compact filters for each main chain block 354 // specified by blockHashes. This is used to add the additional required 355 // cfilters after upgrading a database to version TODO as recording cfilters 356 // becomes a required part of extending the main chain. This method may be 357 // called incrementally to record all main chain block cfilters. When all 358 // cfilters of the main chain are recorded, extending the main chain becomes 359 // possible again. 360 func (s *Store) InsertMissingCFilters(dbtx walletdb.ReadWriteTx, blockHashes []*chainhash.Hash, filters []*gcs2.FilterV2) error { 361 ns := dbtx.ReadWriteBucket(wtxmgrBucketKey) 362 v := ns.Get(rootHaveCFilters) 363 if len(v) == 1 && v[0] != 0 { 364 return errors.E(errors.Invalid, "all cfilters for main chain blocks are already recorded") 365 } 366 367 if len(blockHashes) != len(filters) { 368 return errors.E(errors.Invalid, "slices must have equal len") 369 } 370 if len(blockHashes) == 0 { 371 return nil 372 } 373 374 for i, blockHash := range blockHashes { 375 // Ensure that blockHashes are ordered and that all previous cfilters in the 376 // main chain are known. 377 ok := i == 0 && *blockHash == s.chainParams.GenesisHash 378 var bcf2Key [gcs2.KeySize]byte 379 if !ok { 380 header := existsBlockHeader(ns, blockHash[:]) 381 if header == nil { 382 return errors.E(errors.NotExist, errors.Errorf("missing header for block %v", blockHash)) 383 } 384 parentHash := extractBlockHeaderParentHash(header) 385 merkleRoot := extractBlockHeaderMerkleRoot(header) 386 merkleRootHash, err := chainhash.NewHash(merkleRoot) 387 if err != nil { 388 return errors.E(errors.Invalid, errors.Errorf("invalid stored header %v", blockHash)) 389 } 390 bcf2Key = blockcf2.Key(merkleRootHash) 391 if i == 0 { 392 _, _, err := fetchRawCFilter2(ns, parentHash) 393 ok = err == nil 394 } else { 395 ok = bytes.Equal(parentHash, blockHashes[i-1][:]) 396 } 397 } 398 if !ok { 399 return errors.E(errors.Invalid, "block hashes are not ordered or previous cfilters are missing") 400 } 401 402 // Record cfilter for this block 403 err := putRawCFilter(ns, blockHash[:], valueRawCFilter2(bcf2Key, filters[i].Bytes())) 404 if err != nil { 405 return err 406 } 407 } 408 409 // Mark all main chain cfilters as saved if the last block hash is the main 410 // chain tip. 411 tip, _ := s.MainChainTip(dbtx) 412 if bytes.Equal(tip[:], blockHashes[len(blockHashes)-1][:]) { 413 err := ns.Put(rootHaveCFilters, []byte{1}) 414 if err != nil { 415 return errors.E(errors.IO, err) 416 } 417 } 418 419 return nil 420 } 421 422 // BlockCFilter is a compact filter for a Decred block. 423 type BlockCFilter struct { 424 BlockHash chainhash.Hash 425 FilterV2 *gcs2.FilterV2 426 Key [gcs2.KeySize]byte 427 } 428 429 // GetMainChainCFilters returns compact filters from the main chain, starting at 430 // startHash, copying as many as possible into the storage slice and returning a 431 // subslice for the total number of results. If the start hash is not in the 432 // main chain, this function errors. If inclusive is true, the startHash is 433 // included in the results, otherwise only blocks after the startHash are 434 // included. 435 func (s *Store) GetMainChainCFilters(dbtx walletdb.ReadTx, startHash *chainhash.Hash, inclusive bool, storage []*BlockCFilter) ([]*BlockCFilter, error) { 436 ns := dbtx.ReadBucket(wtxmgrBucketKey) 437 header := ns.NestedReadBucket(bucketHeaders).Get(startHash[:]) 438 if header == nil { 439 return nil, errors.E(errors.NotExist, errors.Errorf("starting block %v not found", startHash)) 440 } 441 height := extractBlockHeaderHeight(header) 442 if !inclusive { 443 height++ 444 } 445 446 blockRecords := ns.NestedReadBucket(bucketBlocks) 447 448 storageUsed := 0 449 for storageUsed < len(storage) { 450 v := blockRecords.Get(keyBlockRecord(height)) 451 if v == nil { 452 break 453 } 454 blockHash := extractRawBlockRecordHash(v) 455 bcf2Key, rawFilter, err := fetchRawCFilter2(ns, blockHash) 456 if err != nil { 457 return nil, err 458 } 459 fCopy := make([]byte, len(rawFilter)) 460 copy(fCopy, rawFilter) 461 f, err := gcs2.FromBytesV2(blockcf2.B, blockcf2.M, fCopy) 462 if err != nil { 463 return nil, err 464 } 465 bf := &BlockCFilter{FilterV2: f, Key: bcf2Key} 466 copy(bf.BlockHash[:], blockHash) 467 storage[storageUsed] = bf 468 469 height++ 470 storageUsed++ 471 } 472 return storage[:storageUsed], nil 473 } 474 475 func extractBlockHeaderParentHash(header []byte) []byte { 476 const parentOffset = 4 477 return header[parentOffset : parentOffset+chainhash.HashSize] 478 } 479 480 func extractBlockHeaderMerkleRoot(header []byte) []byte { 481 const merkleRootOffset = 36 482 return header[merkleRootOffset : merkleRootOffset+chainhash.HashSize] 483 } 484 485 // ExtractBlockHeaderParentHash subslices the header to return the bytes of the 486 // parent block's hash. Must only be called on known good input. 487 // 488 // TODO: This really should not be exported by this package. 489 func ExtractBlockHeaderParentHash(header []byte) []byte { 490 return extractBlockHeaderParentHash(header) 491 } 492 493 func extractBlockHeaderVoteBits(header []byte) uint16 { 494 const voteBitsOffset = 100 495 return binary.LittleEndian.Uint16(header[voteBitsOffset:]) 496 } 497 498 func extractBlockHeaderHeight(header []byte) int32 { 499 const heightOffset = 128 500 return int32(binary.LittleEndian.Uint32(header[heightOffset:])) 501 } 502 503 // ExtractBlockHeaderHeight returns the height field that is encoded in the 504 // header. Must only be called on known good input. 505 // 506 // TODO: This really should not be exported by this package. 507 func ExtractBlockHeaderHeight(header []byte) int32 { 508 return extractBlockHeaderHeight(header) 509 } 510 511 func extractBlockHeaderUnixTime(header []byte) uint32 { 512 const timestampOffset = 136 513 return binary.LittleEndian.Uint32(header[timestampOffset:]) 514 } 515 516 // ExtractBlockHeaderTime returns the unix timestamp that is encoded in the 517 // header. Must only be called on known good input. Header timestamps are only 518 // 4 bytes and this value is actually limited to a maximum unix time of 2^32-1. 519 // 520 // TODO: This really should not be exported by this package. 521 func ExtractBlockHeaderTime(header []byte) int64 { 522 return int64(extractBlockHeaderUnixTime(header)) 523 } 524 525 func blockMetaFromHeader(blockHash *chainhash.Hash, header []byte) BlockMeta { 526 return BlockMeta{ 527 Block: Block{ 528 Hash: *blockHash, 529 Height: extractBlockHeaderHeight(header), 530 }, 531 Time: time.Unix(int64(extractBlockHeaderUnixTime(header)), 0), 532 VoteBits: extractBlockHeaderVoteBits(header), 533 } 534 } 535 536 // RawBlockHeader is a 180 byte block header (always true for version 0 blocks). 537 type RawBlockHeader [180]byte 538 539 // Height extracts the height encoded in a block header. 540 func (h *RawBlockHeader) Height() int32 { 541 return extractBlockHeaderHeight(h[:]) 542 } 543 544 // BlockHeaderData contains the block hashes and serialized blocks headers that 545 // are inserted into the database. At time of writing this only supports wire 546 // protocol version 0 blocks and changes will need to be made if the block 547 // header size changes. 548 type BlockHeaderData struct { 549 BlockHash chainhash.Hash 550 SerializedHeader RawBlockHeader 551 } 552 553 // stakeValidate validates regular tree transactions from the main chain block 554 // at some height. This does not perform any changes when the block is not 555 // marked invalid. When currently invalidated, the invalidated byte is set to 0 556 // to mark the block as stake validated and the mined balance is incremented for 557 // all credits of this block. 558 // 559 // Stake validation or invalidation should only occur for the block at height 560 // tip-1. 561 func stakeValidate(ns walletdb.ReadWriteBucket, height int32) error { 562 k, v := existsBlockRecord(ns, height) 563 if v == nil { 564 return errors.E(errors.IO, errors.Errorf("missing block record for height %v", height)) 565 } 566 if !extractRawBlockRecordStakeInvalid(v) { 567 return nil 568 } 569 570 minedBalance, err := fetchMinedBalance(ns) 571 if err != nil { 572 return err 573 } 574 575 var blockRec blockRecord 576 err = readRawBlockRecord(k, v, &blockRec) 577 if err != nil { 578 return err 579 } 580 581 // Rewrite the block record, marking the regular tree as stake validated. 582 err = putRawBlockRecord(ns, k, valueBlockRecordStakeValidated(v)) 583 if err != nil { 584 return err 585 } 586 587 for i := range blockRec.transactions { 588 txHash := &blockRec.transactions[i] 589 590 _, txv := existsTxRecord(ns, txHash, &blockRec.Block) 591 if txv == nil { 592 return errors.E(errors.IO, errors.Errorf("missing transaction record for tx %v block %v", 593 txHash, &blockRec.Block.Hash)) 594 } 595 var txRec TxRecord 596 err = readRawTxRecord(txHash, txv, &txRec) 597 if err != nil { 598 return err 599 } 600 601 // Only regular tree transactions must be considered. 602 if txRec.TxType != stake.TxTypeRegular { 603 continue 604 } 605 606 // Move all credits from this tx to the non-invalidated credits bucket. 607 // Add an unspent output for each validated credit. 608 // The mined balance is incremented for all moved credit outputs. 609 creditOutPoint := wire.OutPoint{Hash: txRec.Hash} // Index set in loop 610 for i, output := range txRec.MsgTx.TxOut { 611 k, v := existsInvalidatedCredit(ns, &txRec.Hash, uint32(i), &blockRec.Block) 612 if v == nil { 613 continue 614 } 615 616 err = ns.NestedReadWriteBucket(bucketStakeInvalidatedCredits). 617 Delete(k) 618 if err != nil { 619 return errors.E(errors.IO, err) 620 } 621 err = putRawCredit(ns, k, v) 622 if err != nil { 623 return err 624 } 625 626 creditOutPoint.Index = uint32(i) 627 err = putUnspent(ns, &creditOutPoint, &blockRec.Block) 628 if err != nil { 629 return err 630 } 631 632 minedBalance += dcrutil.Amount(output.Value) 633 } 634 635 // Move all debits from this tx to the non-invalidated debits bucket, 636 // and spend any previous credits spent by the stake validated tx. 637 // Remove utxos for all spent previous credits. The mined balance is 638 // decremented for all previous credits that are no longer spendable. 639 debitIncidence := indexedIncidence{ 640 incidence: incidence{txHash: txRec.Hash, block: blockRec.Block}, 641 // index set for each rec input below. 642 } 643 for i := range txRec.MsgTx.TxIn { 644 debKey, credKey, err := existsInvalidatedDebit(ns, &txRec.Hash, uint32(i), 645 &blockRec.Block) 646 if err != nil { 647 return err 648 } 649 if debKey == nil { 650 continue 651 } 652 debitIncidence.index = uint32(i) 653 654 debVal := ns.NestedReadBucket(bucketStakeInvalidatedDebits). 655 Get(debKey) 656 debitAmount := extractRawDebitAmount(debVal) 657 658 _, err = spendCredit(ns, credKey, &debitIncidence) 659 if err != nil { 660 return err 661 } 662 663 prevOut := &txRec.MsgTx.TxIn[i].PreviousOutPoint 664 unspentKey := canonicalOutPoint(&prevOut.Hash, prevOut.Index) 665 err = deleteRawUnspent(ns, unspentKey) 666 if err != nil { 667 return err 668 } 669 670 err = ns.NestedReadWriteBucket(bucketStakeInvalidatedDebits). 671 Delete(debKey) 672 if err != nil { 673 return errors.E(errors.IO, err) 674 } 675 err = putRawDebit(ns, debKey, debVal) 676 if err != nil { 677 return err 678 } 679 680 minedBalance -= debitAmount 681 } 682 } 683 684 return putMinedBalance(ns, minedBalance) 685 } 686 687 // stakeInvalidate invalidates regular tree transactions from the main chain 688 // block at some height. This does not perform any changes when the block is 689 // not marked invalid. When not marked invalid, the invalidated byte is set to 690 // 1 to mark the block as stake invalidated and the mined balance is decremented 691 // for all credits. 692 // 693 // Stake validation or invalidation should only occur for the block at height 694 // tip-1. 695 // 696 // See stakeValidate (which performs the reverse operation) for more details. 697 func stakeInvalidate(ns walletdb.ReadWriteBucket, height int32) error { 698 k, v := existsBlockRecord(ns, height) 699 if v == nil { 700 return errors.E(errors.IO, errors.Errorf("missing block record for height %v", height)) 701 } 702 if extractRawBlockRecordStakeInvalid(v) { 703 return nil 704 } 705 706 minedBalance, err := fetchMinedBalance(ns) 707 if err != nil { 708 return err 709 } 710 711 var blockRec blockRecord 712 err = readRawBlockRecord(k, v, &blockRec) 713 if err != nil { 714 return err 715 } 716 717 // Rewrite the block record, marking the regular tree as stake invalidated. 718 err = putRawBlockRecord(ns, k, valueBlockRecordStakeInvalidated(v)) 719 if err != nil { 720 return err 721 } 722 723 for i := range blockRec.transactions { 724 txHash := &blockRec.transactions[i] 725 726 _, txv := existsTxRecord(ns, txHash, &blockRec.Block) 727 if txv == nil { 728 return errors.E(errors.IO, errors.Errorf("missing transaction record for tx %v block %v", 729 txHash, &blockRec.Block.Hash)) 730 } 731 var txRec TxRecord 732 err = readRawTxRecord(txHash, txv, &txRec) 733 if err != nil { 734 return err 735 } 736 737 // Only regular tree transactions must be considered. 738 if txRec.TxType != stake.TxTypeRegular { 739 continue 740 } 741 742 // Move all credits from this tx to the invalidated credits bucket. 743 // Remove the unspent output for each invalidated credit. 744 // The mined balance is decremented for all moved credit outputs. 745 for i, output := range txRec.MsgTx.TxOut { 746 k, v := existsCredit(ns, &txRec.Hash, uint32(i), &blockRec.Block) 747 if v == nil { 748 continue 749 } 750 751 err = deleteRawCredit(ns, k) 752 if err != nil { 753 return err 754 } 755 err = ns.NestedReadWriteBucket(bucketStakeInvalidatedCredits). 756 Put(k, v) 757 if err != nil { 758 return errors.E(errors.IO, err) 759 } 760 761 unspentKey := canonicalOutPoint(txHash, uint32(i)) 762 err = deleteRawUnspent(ns, unspentKey) 763 if err != nil { 764 return err 765 } 766 767 minedBalance -= dcrutil.Amount(output.Value) 768 } 769 770 // Move all debits from this tx to the invalidated debits bucket, and 771 // unspend any credit spents by the stake invalidated tx. The mined 772 // balance is incremented for all previous credits that are spendable 773 // again. 774 for i := range txRec.MsgTx.TxIn { 775 debKey, credKey, err := existsDebit(ns, &txRec.Hash, uint32(i), 776 &blockRec.Block) 777 if err != nil { 778 return err 779 } 780 if debKey == nil { 781 continue 782 } 783 784 debVal := ns.NestedReadBucket(bucketDebits).Get(debKey) 785 debitAmount := extractRawDebitAmount(debVal) 786 787 _, err = unspendRawCredit(ns, credKey) 788 if err != nil { 789 return err 790 } 791 792 err = deleteRawDebit(ns, debKey) 793 if err != nil { 794 return err 795 } 796 err = ns.NestedReadWriteBucket(bucketStakeInvalidatedDebits). 797 Put(debKey, debVal) 798 if err != nil { 799 return errors.E(errors.IO, err) 800 } 801 802 prevOut := &txRec.MsgTx.TxIn[i].PreviousOutPoint 803 unspentKey := canonicalOutPoint(&prevOut.Hash, prevOut.Index) 804 unspentVal := extractRawDebitUnspentValue(debVal) 805 err = putRawUnspent(ns, unspentKey, unspentVal) 806 if err != nil { 807 return err 808 } 809 810 minedBalance += debitAmount 811 } 812 } 813 814 return putMinedBalance(ns, minedBalance) 815 } 816 817 // GetMainChainBlockHashForHeight returns the block hash of the block on the 818 // main chain at a given height. 819 func (s *Store) GetMainChainBlockHashForHeight(ns walletdb.ReadBucket, height int32) (chainhash.Hash, error) { 820 _, v := existsBlockRecord(ns, height) 821 if v == nil { 822 err := errors.E(errors.NotExist, errors.Errorf("no block at height %v in main chain", height)) 823 return chainhash.Hash{}, err 824 } 825 var hash chainhash.Hash 826 copy(hash[:], extractRawBlockRecordHash(v)) 827 return hash, nil 828 } 829 830 // GetSerializedBlockHeader returns the bytes of the serialized header for the 831 // block specified by its hash. These bytes are a copy of the value returned 832 // from the DB and are usable outside of the transaction. 833 func (s *Store) GetSerializedBlockHeader(ns walletdb.ReadBucket, blockHash *chainhash.Hash) ([]byte, error) { 834 return fetchRawBlockHeader(ns, keyBlockHeader(blockHash)) 835 } 836 837 // GetBlockHeader returns the block header for the block specified by its hash. 838 func (s *Store) GetBlockHeader(dbtx walletdb.ReadTx, blockHash *chainhash.Hash) (*wire.BlockHeader, error) { 839 ns := dbtx.ReadBucket(wtxmgrBucketKey) 840 serialized, err := fetchRawBlockHeader(ns, keyBlockHeader(blockHash)) 841 if err != nil { 842 return nil, err 843 } 844 header := new(wire.BlockHeader) 845 err = header.Deserialize(bytes.NewReader(serialized)) 846 if err != nil { 847 return nil, errors.E(errors.IO, err) 848 } 849 return header, nil 850 } 851 852 // BlockInMainChain returns whether a block identified by its hash is in the 853 // current main chain and if so, whether it has been stake invalidated by the 854 // next main chain block. 855 func (s *Store) BlockInMainChain(dbtx walletdb.ReadTx, blockHash *chainhash.Hash) (inMainChain bool, invalidated bool) { 856 ns := dbtx.ReadBucket(wtxmgrBucketKey) 857 header := existsBlockHeader(ns, keyBlockHeader(blockHash)) 858 if header == nil { 859 return false, false 860 } 861 862 _, v := existsBlockRecord(ns, extractBlockHeaderHeight(header)) 863 if v == nil { 864 return false, false 865 } 866 if !bytes.Equal(extractRawBlockRecordHash(v), blockHash[:]) { 867 return false, false 868 } 869 return true, extractRawBlockRecordStakeInvalid(v) 870 } 871 872 // GetBlockMetaForHash returns the BlockMeta for a block specified by its hash. 873 // 874 // TODO: This is legacy code now that headers are saved. BlockMeta can be removed. 875 func (s *Store) GetBlockMetaForHash(ns walletdb.ReadBucket, blockHash *chainhash.Hash) (BlockMeta, error) { 876 header := ns.NestedReadBucket(bucketHeaders).Get(blockHash[:]) 877 if header == nil { 878 err := errors.E(errors.NotExist, errors.Errorf("block %v header not found", blockHash)) 879 return BlockMeta{}, err 880 } 881 return BlockMeta{ 882 Block: Block{ 883 Hash: *blockHash, 884 Height: extractBlockHeaderHeight(header), 885 }, 886 Time: time.Unix(int64(extractBlockHeaderUnixTime(header)), 0), 887 VoteBits: extractBlockHeaderVoteBits(header), 888 }, nil 889 } 890 891 // GetMainChainBlockHashes returns block hashes from the main chain, starting at 892 // startHash, copying as many as possible into the storage slice and returning a 893 // subslice for the total number of results. If the start hash is not in the 894 // main chain, this function errors. If inclusive is true, the startHash is 895 // included in the results, otherwise only blocks after the startHash are 896 // included. 897 func (s *Store) GetMainChainBlockHashes(ns walletdb.ReadBucket, startHash *chainhash.Hash, 898 inclusive bool, storage []chainhash.Hash) ([]chainhash.Hash, error) { 899 900 header := ns.NestedReadBucket(bucketHeaders).Get(startHash[:]) 901 if header == nil { 902 return nil, errors.E(errors.NotExist, errors.Errorf("starting block %v not found", startHash)) 903 } 904 height := extractBlockHeaderHeight(header) 905 906 // Check that the hash of the recorded main chain block at height is equal 907 // to startHash. 908 blockRecords := ns.NestedReadBucket(bucketBlocks) 909 blockVal := blockRecords.Get(keyBlockRecord(height)) 910 if !bytes.Equal(extractRawBlockRecordHash(blockVal), startHash[:]) { 911 return nil, errors.E(errors.Invalid, errors.Errorf("block %v not in main chain", startHash)) 912 } 913 914 if !inclusive { 915 height++ 916 } 917 918 i := 0 919 for i < len(storage) { 920 v := blockRecords.Get(keyBlockRecord(height)) 921 if v == nil { 922 break 923 } 924 copy(storage[i][:], extractRawBlockRecordHash(v)) 925 height++ 926 i++ 927 } 928 return storage[:i], nil 929 } 930 931 // fetchAccountForPkScript fetches an account for a given pkScript given a 932 // credit value, the script, and an account lookup function. It does this 933 // to maintain compatibility with older versions of the database. 934 func (s *Store) fetchAccountForPkScript(addrmgrNs walletdb.ReadBucket, 935 credVal []byte, unminedCredVal []byte, pkScript []byte) (uint32, error) { 936 937 // Attempt to get the account from the mined credit. If the account was 938 // never stored, we can ignore the error and fall through to do the lookup 939 // with the acctLookupFunc. 940 // 941 // TODO: upgrade the database to actually store the account for every credit 942 // to avoid this nonsensical error handling. The upgrade was not done 943 // correctly in the past and only began recording accounts for newly 944 // inserted credits without modifying existing ones. 945 if credVal != nil { 946 acct, err := fetchRawCreditAccount(credVal) 947 if err == nil { 948 return acct, nil 949 } 950 } 951 if unminedCredVal != nil { 952 acct, err := fetchRawUnminedCreditAccount(unminedCredVal) 953 if err == nil { 954 return acct, nil 955 } 956 } 957 958 // Neither credVal or unminedCredVal were passed, or if they were, they 959 // didn't have the account set. Figure out the account from the pkScript the 960 // expensive way. 961 _, addrs := stdscript.ExtractAddrs(scriptVersionAssumed, pkScript, s.chainParams) 962 if len(addrs) == 0 { 963 return 0, errors.New("no addresses decoded from pkScript") 964 } 965 966 // Only look at the first address returned. This does not handle 967 // multisignature or other custom pkScripts in the correct way, which 968 // requires multiple account tracking. 969 return s.acctLookupFunc(addrmgrNs, addrs[0]) 970 } 971 972 // moveMinedTx moves a transaction record from the unmined buckets to block 973 // buckets. 974 func (s *Store) moveMinedTx(ns walletdb.ReadWriteBucket, addrmgrNs walletdb.ReadBucket, rec *TxRecord, recKey, recVal []byte, block *BlockMeta) error { 975 log.Debugf("Marking unconfirmed transaction %v mined in block %d", 976 &rec.Hash, block.Height) 977 978 // Add transaction to block record. 979 blockKey, blockVal := existsBlockRecord(ns, block.Height) 980 blockVal, err := appendRawBlockRecord(blockVal, &rec.Hash) 981 if err != nil { 982 return err 983 } 984 err = putRawBlockRecord(ns, blockKey, blockVal) 985 if err != nil { 986 return err 987 } 988 989 err = putRawTxRecord(ns, recKey, recVal) 990 if err != nil { 991 return err 992 } 993 minedBalance, err := fetchMinedBalance(ns) 994 if err != nil { 995 return err 996 } 997 998 // For all transaction inputs, remove the previous output marker from the 999 // unmined inputs bucket. For any mined transactions with unspent credits 1000 // spent by this transaction, mark each spent, remove from the unspents map, 1001 // and insert a debit record for the spent credit. 1002 debitIncidence := indexedIncidence{ 1003 incidence: incidence{txHash: rec.Hash, block: block.Block}, 1004 // index set for each rec input below. 1005 } 1006 for i, input := range rec.MsgTx.TxIn { 1007 unspentKey, credKey := existsUnspent(ns, &input.PreviousOutPoint) 1008 1009 err = deleteRawUnminedInput(ns, unspentKey) 1010 if err != nil { 1011 return err 1012 } 1013 1014 if credKey == nil { 1015 continue 1016 } 1017 1018 debitIncidence.index = uint32(i) 1019 amt, err := spendCredit(ns, credKey, &debitIncidence) 1020 if err != nil { 1021 return err 1022 } 1023 1024 credVal := existsRawCredit(ns, credKey) 1025 if credVal == nil { 1026 return errors.E(errors.IO, errors.Errorf("missing credit "+ 1027 "%v, key %x, spent by %v", &input.PreviousOutPoint, credKey, &rec.Hash)) 1028 } 1029 creditOpCode := fetchRawCreditTagOpCode(credVal) 1030 1031 // Do not decrement ticket amounts. 1032 if !(creditOpCode == txscript.OP_SSTX) { 1033 minedBalance -= amt 1034 } 1035 err = deleteRawUnspent(ns, unspentKey) 1036 if err != nil { 1037 return err 1038 } 1039 1040 err = putDebit(ns, &rec.Hash, uint32(i), amt, &block.Block, credKey) 1041 if err != nil { 1042 return err 1043 } 1044 } 1045 1046 // For each output of the record that is marked as a credit, if the 1047 // output is marked as a credit by the unconfirmed store, remove the 1048 // marker and mark the output as a mined credit in the db. 1049 // 1050 // Moved credits are added as unspents, even if there is another 1051 // unconfirmed transaction which spends them. 1052 cred := credit{ 1053 outPoint: wire.OutPoint{Hash: rec.Hash}, 1054 block: block.Block, 1055 spentBy: indexedIncidence{index: ^uint32(0)}, 1056 } 1057 for i := uint32(0); i < uint32(len(rec.MsgTx.TxOut)); i++ { 1058 k := canonicalOutPoint(&rec.Hash, i) 1059 v := existsRawUnminedCredit(ns, k) 1060 if v == nil { 1061 continue 1062 } 1063 1064 // TODO: This should use the raw apis. The credit value (it.cv) 1065 // can be moved from unmined directly to the credits bucket. 1066 // The key needs a modification to include the block 1067 // height/hash. 1068 amount, change, err := fetchRawUnminedCreditAmountChange(v) 1069 if err != nil { 1070 return err 1071 } 1072 cred.outPoint.Index = i 1073 cred.amount = amount 1074 cred.change = change 1075 cred.opCode = fetchRawUnminedCreditTagOpCode(v) 1076 cred.isCoinbase = fetchRawUnminedCreditTagIsCoinbase(v) 1077 1078 // Legacy credit output values may be of the wrong 1079 // size. 1080 scrType := fetchRawUnminedCreditScriptType(v) 1081 scrPos := fetchRawUnminedCreditScriptOffset(v) 1082 scrLen := fetchRawUnminedCreditScriptLength(v) 1083 1084 // Grab the pkScript quickly. 1085 pkScript, err := fetchRawTxRecordPkScript(recKey, recVal, 1086 cred.outPoint.Index, scrPos, scrLen) 1087 if err != nil { 1088 return err 1089 } 1090 1091 acct, err := s.fetchAccountForPkScript(addrmgrNs, nil, v, pkScript) 1092 if err != nil { 1093 return err 1094 } 1095 1096 err = deleteRawUnminedCredit(ns, k) 1097 if err != nil { 1098 return err 1099 } 1100 err = putUnspentCredit(ns, &cred, scrType, scrPos, scrLen, acct, DBVersion) 1101 if err != nil { 1102 return err 1103 } 1104 err = putUnspent(ns, &cred.outPoint, &block.Block) 1105 if err != nil { 1106 return err 1107 } 1108 1109 // Do not increment ticket credits. 1110 if !(cred.opCode == txscript.OP_SSTX) { 1111 minedBalance += amount 1112 } 1113 } 1114 1115 err = putMinedBalance(ns, minedBalance) 1116 if err != nil { 1117 return err 1118 } 1119 1120 err = deleteUnpublished(ns, rec.Hash[:]) 1121 if err != nil { 1122 return err 1123 } 1124 1125 return deleteRawUnmined(ns, rec.Hash[:]) 1126 } 1127 1128 // InsertMinedTx inserts a new transaction record for a mined transaction into 1129 // the database. The block header must have been previously saved. If the 1130 // exact transaction is already saved as an unmined transaction, it is moved to 1131 // a block. Other unmined transactions which become double spends are removed. 1132 func (s *Store) InsertMinedTx(dbtx walletdb.ReadWriteTx, rec *TxRecord, blockHash *chainhash.Hash) error { 1133 ns := dbtx.ReadWriteBucket(wtxmgrBucketKey) 1134 addrmgrNs := dbtx.ReadWriteBucket(waddrmgrBucketKey) 1135 1136 // Ensure block is in the main chain before proceeding. 1137 blockHeader := existsBlockHeader(ns, blockHash[:]) 1138 if blockHeader == nil { 1139 return errors.E(errors.Invalid, "block header must be recorded") 1140 } 1141 height := extractBlockHeaderHeight(blockHeader) 1142 blockVal := ns.NestedReadBucket(bucketBlocks).Get(keyBlockRecord(height)) 1143 if !bytes.Equal(extractRawBlockRecordHash(blockVal), blockHash[:]) { 1144 return errors.E(errors.Invalid, "mined transactions must be added to main chain blocks") 1145 } 1146 1147 // Fetch the mined balance in case we need to update it. 1148 minedBalance, err := fetchMinedBalance(ns) 1149 if err != nil { 1150 return err 1151 } 1152 1153 // Add a debit record for each unspent credit spent by this tx. 1154 block := blockMetaFromHeader(blockHash, blockHeader) 1155 spender := indexedIncidence{ 1156 incidence: incidence{ 1157 txHash: rec.Hash, 1158 block: block.Block, 1159 }, 1160 // index set for each iteration below 1161 } 1162 txType := stake.DetermineTxType(&rec.MsgTx) 1163 1164 invalidated := false 1165 if txType == stake.TxTypeRegular { 1166 height := extractBlockHeaderHeight(blockHeader) 1167 _, rawBlockRecVal := existsBlockRecord(ns, height) 1168 invalidated = extractRawBlockRecordStakeInvalid(rawBlockRecVal) 1169 } 1170 1171 for i, input := range rec.MsgTx.TxIn { 1172 unspentKey, credKey := existsUnspent(ns, &input.PreviousOutPoint) 1173 if credKey == nil { 1174 // Debits for unmined transactions are not explicitly 1175 // tracked. Instead, all previous outputs spent by any 1176 // unmined transaction are added to a map for quick 1177 // lookups when it must be checked whether a mined 1178 // output is unspent or not. 1179 // 1180 // Tracking individual debits for unmined transactions 1181 // could be added later to simplify (and increase 1182 // performance of) determining some details that need 1183 // the previous outputs (e.g. determining a fee), but at 1184 // the moment that is not done (and a db lookup is used 1185 // for those cases instead). There is also a good 1186 // chance that all unmined transaction handling will 1187 // move entirely to the db rather than being handled in 1188 // memory for atomicity reasons, so the simplist 1189 // implementation is currently used. 1190 continue 1191 } 1192 1193 if invalidated { 1194 // Add an invalidated debit but do not spend the previous credit, 1195 // remove it from the utxo set, or decrement the mined balance. 1196 debKey := keyDebit(&rec.Hash, uint32(i), &block.Block) 1197 credVal := existsRawCredit(ns, credKey) 1198 credAmt, err := fetchRawCreditAmount(credVal) 1199 if err != nil { 1200 return err 1201 } 1202 debVal := valueDebit(credAmt, credKey) 1203 err = ns.NestedReadWriteBucket(bucketStakeInvalidatedDebits). 1204 Put(debKey, debVal) 1205 if err != nil { 1206 return errors.E(errors.IO, err) 1207 } 1208 } else { 1209 spender.index = uint32(i) 1210 amt, err := spendCredit(ns, credKey, &spender) 1211 if err != nil { 1212 return err 1213 } 1214 err = putDebit(ns, &rec.Hash, uint32(i), amt, &block.Block, 1215 credKey) 1216 if err != nil { 1217 return err 1218 } 1219 1220 // Don't decrement spent ticket amounts. 1221 isTicketInput := (txType == stake.TxTypeSSGen && i == 1) || 1222 (txType == stake.TxTypeSSRtx && i == 0) 1223 if !isTicketInput { 1224 minedBalance -= amt 1225 } 1226 1227 err = deleteRawUnspent(ns, unspentKey) 1228 if err != nil { 1229 return err 1230 } 1231 } 1232 } 1233 1234 // TODO only update if we actually modified the 1235 // mined balance. 1236 err = putMinedBalance(ns, minedBalance) 1237 if err != nil { 1238 return err 1239 } 1240 1241 // If a transaction record for this tx hash and block already exist, 1242 // there is nothing left to do. 1243 k, v := existsTxRecord(ns, &rec.Hash, &block.Block) 1244 if v != nil { 1245 return nil 1246 } 1247 1248 // If the transaction is a ticket purchase, record it in the ticket 1249 // purchases bucket. 1250 if txType == stake.TxTypeSStx { 1251 tk := rec.Hash[:] 1252 tv := existsRawTicketRecord(ns, tk) 1253 if tv == nil { 1254 tv = valueTicketRecord(-1) 1255 err := putRawTicketRecord(ns, tk, tv) 1256 if err != nil { 1257 return err 1258 } 1259 } 1260 } 1261 1262 // If the exact tx (not a double spend) is already included but 1263 // unconfirmed, move it to a block. 1264 v = existsRawUnmined(ns, rec.Hash[:]) 1265 if v != nil { 1266 if invalidated { 1267 panic(fmt.Sprintf("unimplemented: moveMinedTx called on a stake-invalidated tx: block %v height %v tx %v", &block.Hash, block.Height, &rec.Hash)) 1268 } 1269 return s.moveMinedTx(ns, addrmgrNs, rec, k, v, &block) 1270 } 1271 1272 // As there may be unconfirmed transactions that are invalidated by this 1273 // transaction (either being duplicates, or double spends), remove them 1274 // from the unconfirmed set. This also handles removing unconfirmed 1275 // transaction spend chains if any other unconfirmed transactions spend 1276 // outputs of the removed double spend. 1277 err = s.removeDoubleSpends(ns, rec) 1278 if err != nil { 1279 return err 1280 } 1281 1282 // Adding this transaction hash to the set of transactions from this block. 1283 blockKey, blockValue := existsBlockRecord(ns, block.Height) 1284 blockValue, err = appendRawBlockRecord(blockValue, &rec.Hash) 1285 if err != nil { 1286 return err 1287 } 1288 err = putRawBlockRecord(ns, blockKey, blockValue) 1289 if err != nil { 1290 return err 1291 } 1292 1293 return putTxRecord(ns, rec, &block.Block) 1294 } 1295 1296 // AddCredit marks a transaction record as containing a transaction output 1297 // spendable by wallet. The output is added unspent, and is marked spent 1298 // when a new transaction spending the output is inserted into the store. 1299 // 1300 // TODO(jrick): This should not be necessary. Instead, pass the indexes 1301 // that are known to contain credits when a transaction or merkleblock is 1302 // inserted into the store. 1303 func (s *Store) AddCredit(dbtx walletdb.ReadWriteTx, rec *TxRecord, block *BlockMeta, 1304 index uint32, change bool, account uint32) error { 1305 1306 ns := dbtx.ReadWriteBucket(wtxmgrBucketKey) 1307 1308 if int(index) >= len(rec.MsgTx.TxOut) { 1309 return errors.E(errors.Invalid, "transaction output index for credit does not exist") 1310 } 1311 1312 invalidated := false 1313 if rec.TxType == stake.TxTypeRegular && block != nil { 1314 blockHeader := existsBlockHeader(ns, block.Hash[:]) 1315 height := extractBlockHeaderHeight(blockHeader) 1316 _, rawBlockRecVal := existsBlockRecord(ns, height) 1317 invalidated = extractRawBlockRecordStakeInvalid(rawBlockRecVal) 1318 } 1319 if invalidated { 1320 // Write an invalidated credit. Do not create a utxo for the output, 1321 // and do not increment the mined balance. 1322 version := rec.MsgTx.TxOut[index].Version 1323 pkScript := rec.MsgTx.TxOut[index].PkScript 1324 k := keyCredit(&rec.Hash, index, &block.Block) 1325 cred := credit{ 1326 outPoint: wire.OutPoint{ 1327 Hash: rec.Hash, 1328 Index: index, 1329 }, 1330 block: block.Block, 1331 amount: dcrutil.Amount(rec.MsgTx.TxOut[index].Value), 1332 change: change, 1333 spentBy: indexedIncidence{index: ^uint32(0)}, 1334 opCode: getStakeOpCode(version, pkScript), 1335 isCoinbase: compat.IsEitherCoinBaseTx(&rec.MsgTx), 1336 hasExpiry: rec.MsgTx.Expiry != 0, 1337 } 1338 scTy := pkScriptType(version, pkScript) 1339 scLoc := uint32(rec.MsgTx.PkScriptLocs()[index]) 1340 v := valueUnspentCredit(&cred, scTy, scLoc, uint32(len(pkScript)), 1341 account, DBVersion) 1342 err := ns.NestedReadWriteBucket(bucketStakeInvalidatedCredits).Put(k, v) 1343 if err != nil { 1344 return errors.E(errors.IO, err) 1345 } 1346 return nil 1347 } 1348 1349 _, err := s.addCredit(ns, rec, block, index, change, account) 1350 return err 1351 } 1352 1353 // getStakeOpCode returns opNonstake for non-stake transactions, or the stake op 1354 // code tag for stake transactions. This excludes TADD. 1355 func getStakeOpCode(version uint16, pkScript []byte) uint8 { 1356 class := stdscript.DetermineScriptType(version, pkScript) 1357 switch class { 1358 case stdscript.STStakeSubmissionPubKeyHash, stdscript.STStakeSubmissionScriptHash: 1359 return txscript.OP_SSTX 1360 case stdscript.STStakeGenPubKeyHash, stdscript.STStakeGenScriptHash: 1361 return txscript.OP_SSGEN 1362 case stdscript.STStakeRevocationPubKeyHash, stdscript.STStakeRevocationScriptHash: 1363 return txscript.OP_SSRTX 1364 case stdscript.STStakeChangePubKeyHash, stdscript.STStakeChangeScriptHash: 1365 return txscript.OP_SSTXCHANGE 1366 case stdscript.STTreasuryGenPubKeyHash, stdscript.STTreasuryGenScriptHash: 1367 return txscript.OP_TGEN 1368 } 1369 1370 return opNonstake 1371 } 1372 1373 // pkScriptType determines the general type of pkScript for the purposes of 1374 // fast extraction of pkScript data from a raw transaction record. 1375 func pkScriptType(ver uint16, pkScript []byte) scriptType { 1376 class := stdscript.DetermineScriptType(ver, pkScript) 1377 switch class { 1378 case stdscript.STPubKeyHashEcdsaSecp256k1: 1379 return scriptTypeP2PKH 1380 case stdscript.STPubKeyEcdsaSecp256k1: 1381 return scriptTypeP2PK 1382 case stdscript.STScriptHash: 1383 return scriptTypeP2SH 1384 case stdscript.STPubKeyHashEd25519, stdscript.STPubKeyHashSchnorrSecp256k1: 1385 return scriptTypeP2PKHAlt 1386 case stdscript.STPubKeyEd25519, stdscript.STPubKeySchnorrSecp256k1: 1387 return scriptTypeP2PKAlt 1388 case stdscript.STStakeSubmissionPubKeyHash, stdscript.STStakeGenPubKeyHash, 1389 stdscript.STStakeRevocationPubKeyHash, stdscript.STStakeChangePubKeyHash, 1390 stdscript.STTreasuryGenPubKeyHash: 1391 return scriptTypeSP2PKH 1392 case stdscript.STStakeSubmissionScriptHash, stdscript.STStakeGenScriptHash, 1393 stdscript.STStakeRevocationScriptHash, stdscript.STStakeChangeScriptHash, 1394 stdscript.STTreasuryGenScriptHash: 1395 return scriptTypeSP2SH 1396 } 1397 1398 return scriptTypeUnspecified 1399 } 1400 1401 func (s *Store) addCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, 1402 index uint32, change bool, account uint32) (bool, error) { 1403 1404 scriptVersion, pkScript := rec.MsgTx.TxOut[index].Version, rec.MsgTx.TxOut[index].PkScript 1405 opCode := getStakeOpCode(scriptVersion, pkScript) 1406 isCoinbase := compat.IsEitherCoinBaseTx(&rec.MsgTx) 1407 hasExpiry := rec.MsgTx.Expiry != wire.NoExpiryValue 1408 1409 if block == nil { 1410 // Unmined tx must have been already added for the credit to be added. 1411 if existsRawUnmined(ns, rec.Hash[:]) == nil { 1412 panic("attempted to add credit for unmined tx, but unmined tx with same hash does not exist") 1413 } 1414 1415 k := canonicalOutPoint(&rec.Hash, index) 1416 if existsRawUnminedCredit(ns, k) != nil { 1417 return false, nil 1418 } 1419 scrType := pkScriptType(scriptVersion, pkScript) 1420 pkScrLocs := rec.MsgTx.PkScriptLocs() 1421 scrLoc := pkScrLocs[index] 1422 scrLen := len(pkScript) 1423 1424 v := valueUnminedCredit(dcrutil.Amount(rec.MsgTx.TxOut[index].Value), 1425 change, opCode, isCoinbase, hasExpiry, scrType, uint32(scrLoc), 1426 uint32(scrLen), account, DBVersion) 1427 return true, putRawUnminedCredit(ns, k, v) 1428 } 1429 1430 k, v := existsCredit(ns, &rec.Hash, index, &block.Block) 1431 if v != nil { 1432 return false, nil 1433 } 1434 1435 txOutAmt := dcrutil.Amount(rec.MsgTx.TxOut[index].Value) 1436 log.Debugf("Marking transaction %v output %d (%v) spendable", 1437 rec.Hash, index, txOutAmt) 1438 1439 cred := credit{ 1440 outPoint: wire.OutPoint{ 1441 Hash: rec.Hash, 1442 Index: index, 1443 }, 1444 block: block.Block, 1445 amount: txOutAmt, 1446 change: change, 1447 spentBy: indexedIncidence{index: ^uint32(0)}, 1448 opCode: opCode, 1449 isCoinbase: isCoinbase, 1450 hasExpiry: rec.MsgTx.Expiry != wire.NoExpiryValue, 1451 } 1452 scrType := pkScriptType(scriptVersion, pkScript) 1453 pkScrLocs := rec.MsgTx.PkScriptLocs() 1454 scrLoc := pkScrLocs[index] 1455 scrLen := len(pkScript) 1456 1457 v = valueUnspentCredit(&cred, scrType, uint32(scrLoc), uint32(scrLen), 1458 account, DBVersion) 1459 err := putRawCredit(ns, k, v) 1460 if err != nil { 1461 return false, err 1462 } 1463 1464 minedBalance, err := fetchMinedBalance(ns) 1465 if err != nil { 1466 return false, err 1467 } 1468 // Update the balance so long as it's not a ticket output. 1469 if !(opCode == txscript.OP_SSTX) { 1470 err = putMinedBalance(ns, minedBalance+txOutAmt) 1471 if err != nil { 1472 return false, err 1473 } 1474 } 1475 1476 return true, putUnspent(ns, &cred.outPoint, &block.Block) 1477 } 1478 1479 // AddTicketCommitment adds the given output of a transaction as a ticket 1480 // commitment originating from a wallet account. 1481 // 1482 // The transaction record MUST correspond to a ticket (sstx) transaction, the 1483 // index MUST be from a commitment output and the account MUST be from a 1484 // wallet-controlled account, otherwise the database will be put in an undefined 1485 // state. 1486 func (s *Store) AddTicketCommitment(ns walletdb.ReadWriteBucket, rec *TxRecord, 1487 index, account uint32) error { 1488 1489 k := keyTicketCommitment(rec.Hash, index) 1490 v := existsRawTicketCommitment(ns, k) 1491 if v != nil { 1492 // If we have already recorded this ticket commitment, there's nothing 1493 // else to do. Note that this means unspent ticket commitment entries 1494 // are added to the index only once, at the very first time the ticket 1495 // commitment is seen. 1496 return nil 1497 } 1498 1499 if index >= uint32(len(rec.MsgTx.TxOut)) { 1500 return errors.E(errors.Invalid, "index should be of an existing output") 1501 } 1502 1503 if index%2 != 1 { 1504 return errors.E(errors.Invalid, "index should be of a ticket commitment") 1505 } 1506 1507 if rec.TxType != stake.TxTypeSStx { 1508 return errors.E(errors.Invalid, "transaction record should be of a ticket") 1509 } 1510 1511 txOut := rec.MsgTx.TxOut[index] 1512 txOutAmt, err := stake.AmountFromSStxPkScrCommitment(txOut.PkScript) 1513 if err != nil { 1514 return err 1515 } 1516 1517 log.Debugf("Accounting for ticket commitment %v:%d (%v) from the wallet", 1518 rec.Hash, index, txOutAmt) 1519 1520 v = valueTicketCommitment(txOutAmt, account) 1521 err = putRawTicketCommitment(ns, k, v) 1522 if err != nil { 1523 return err 1524 } 1525 1526 v = valueUnspentTicketCommitment(false) 1527 return putRawUnspentTicketCommitment(ns, k, v) 1528 } 1529 1530 // originalTicketInfo returns the transaction hash and output count for the 1531 // ticket spent by the given transaction. 1532 // 1533 // spenderType MUST be either TxTypeSSGen or TxTypeSSRtx and MUST by the type of 1534 // the provided spenderTx, otherwise the result is undefined. 1535 func originalTicketInfo(spenderType stake.TxType, spenderTx *wire.MsgTx) (chainhash.Hash, uint32) { 1536 if spenderType == stake.TxTypeSSGen { 1537 // Votes have an additional input (stakebase) and two additional outputs 1538 // (previous block hash and vote bits) so account for those. 1539 return spenderTx.TxIn[1].PreviousOutPoint.Hash, 1540 uint32((len(spenderTx.TxOut)-2)*2 + 1) 1541 } 1542 1543 return spenderTx.TxIn[0].PreviousOutPoint.Hash, 1544 uint32(len(spenderTx.TxOut)*2 + 1) 1545 } 1546 1547 // removeUnspentTicketCommitments deletes any outstanding commitments that are 1548 // controlled by this wallet and have been redeemed by the given transaction. 1549 // 1550 // rec MUST be either a vote or revocation transaction, otherwise the results 1551 // are undefined. 1552 func (s *Store) removeUnspentTicketCommitments(ns walletdb.ReadWriteBucket, 1553 txType stake.TxType, tx *wire.MsgTx) error { 1554 1555 ticketHash, ticketOutCount := originalTicketInfo(txType, tx) 1556 for i := uint32(1); i < ticketOutCount; i += 2 { 1557 k := keyTicketCommitment(ticketHash, i) 1558 if existsRawTicketCommitment(ns, k) == nil { 1559 // This commitment was not tracked by the wallet, so ignore it. 1560 continue 1561 } 1562 1563 log.Debugf("Removing unspent ticket commitment %v:%d from the wallet", 1564 ticketHash, i) 1565 1566 err := deleteRawUnspentTicketCommitment(ns, k) 1567 if err != nil { 1568 return err 1569 } 1570 } 1571 1572 return nil 1573 } 1574 1575 // replaceTicketCommitmentUnminedSpent replaces the unminedSpent flag of all 1576 // unspent commitments spent by the given transaction to the given value. 1577 // 1578 // txType MUST be either a TxTypeSSGen or TxTypeSSRtx and MUST be the type for 1579 // the corresponding tx parameter, otherwise the result is undefined. 1580 func (s *Store) replaceTicketCommitmentUnminedSpent(ns walletdb.ReadWriteBucket, 1581 txType stake.TxType, tx *wire.MsgTx, value bool) error { 1582 1583 ticketHash, ticketOutCount := originalTicketInfo(txType, tx) 1584 1585 // Loop over the indices of possible ticket commitments, checking if they 1586 // are controlled by the wallet. If they are, replace the corresponding 1587 // unminedSpent flag. 1588 for i := uint32(1); i < ticketOutCount; i += 2 { 1589 k := keyTicketCommitment(ticketHash, i) 1590 if existsRawTicketCommitment(ns, k) == nil { 1591 // This commitment was not tracked by the wallet, so ignore it. 1592 continue 1593 } 1594 1595 log.Debugf("Marking ticket commitment %v:%d unmined spent as %v", 1596 ticketHash, i, value) 1597 v := valueUnspentTicketCommitment(value) 1598 err := putRawUnspentTicketCommitment(ns, k, v) 1599 if err != nil { 1600 return err 1601 } 1602 } 1603 1604 return nil 1605 } 1606 1607 // RedeemTicketCommitments redeems the commitments of the given vote or 1608 // revocation transaction by marking the commitments unminedSpent or removing 1609 // them altogether. 1610 // 1611 // rec MUST be either a vote (ssgen) or revocation (ssrtx) or this method fails. 1612 func (s *Store) RedeemTicketCommitments(ns walletdb.ReadWriteBucket, rec *TxRecord, 1613 block *BlockMeta) error { 1614 1615 if (rec.TxType != stake.TxTypeSSGen) && (rec.TxType != stake.TxTypeSSRtx) { 1616 return errors.E(errors.Invalid, "rec must be a vote or revocation tx") 1617 } 1618 1619 if block == nil { 1620 // When the tx is unmined, we only mark the commitment as spent by an 1621 // unmined tx. 1622 return s.replaceTicketCommitmentUnminedSpent(ns, rec.TxType, 1623 &rec.MsgTx, true) 1624 } 1625 1626 // When the tx is mined we completely remove the commitment from the unspent 1627 // commitments index. 1628 return s.removeUnspentTicketCommitments(ns, rec.TxType, &rec.MsgTx) 1629 } 1630 1631 // copied from txscript/v1 1632 func getScriptHashFromP2SHScript(pkScript []byte) ([]byte, error) { 1633 // Scan through the opcodes until the first HASH160 opcode is found. 1634 const scriptVersion = 0 1635 tokenizer := txscript.MakeScriptTokenizer(scriptVersion, pkScript) 1636 for tokenizer.Next() { 1637 if tokenizer.Opcode() == txscript.OP_HASH160 { 1638 break 1639 } 1640 } 1641 1642 // Attempt to extract the script hash if the script is not already fully 1643 // parsed and didn't already fail during parsing above. 1644 // 1645 // Note that this means a script without a data push for the script hash or 1646 // where OP_HASH160 wasn't found will result in nil data being returned. 1647 // This was done to preserve existing behavior, although it is questionable 1648 // since a p2sh script has a specific form and if there is no push here, 1649 // it's not really a p2sh script and thus should probably have returned an 1650 // appropriate error for calling the function incorrectly. 1651 if tokenizer.Next() { 1652 return tokenizer.Data(), nil 1653 } 1654 return nil, tokenizer.Err() 1655 } 1656 1657 // AddMultisigOut adds a P2SH multisignature spendable output into the 1658 // transaction manager. In the event that the output already existed but 1659 // was not mined, the output is updated so its value reflects the block 1660 // it was included in. 1661 func (s *Store) AddMultisigOut(dbtx walletdb.ReadWriteTx, rec *TxRecord, block *BlockMeta, index uint32) error { 1662 ns := dbtx.ReadWriteBucket(wtxmgrBucketKey) 1663 addrmgrNs := dbtx.ReadWriteBucket(waddrmgrBucketKey) 1664 1665 if int(index) >= len(rec.MsgTx.TxOut) { 1666 return errors.E(errors.Invalid, "transaction output does not exist") 1667 } 1668 1669 empty := &chainhash.Hash{} 1670 1671 // Check to see if the output already exists and is now being 1672 // mined into a block. If it does, update the record and return. 1673 key := keyMultisigOut(rec.Hash, index) 1674 val := existsMultisigOutCopy(ns, key) 1675 if val != nil && block != nil { 1676 blockHashV, _ := fetchMultisigOutMined(val) 1677 if blockHashV.IsEqual(empty) { 1678 setMultisigOutMined(val, block.Block.Hash, 1679 uint32(block.Block.Height)) 1680 return putMultisigOutRawValues(ns, key, val) 1681 } 1682 return errors.E(errors.Invalid, "multisig credit is mined") 1683 } 1684 // The multisignature output already exists in the database 1685 // as an unmined, unspent output and something is trying to 1686 // add it in duplicate. Return. 1687 if val != nil && block == nil { 1688 blockHashV, _ := fetchMultisigOutMined(val) 1689 if blockHashV.IsEqual(empty) { 1690 return nil 1691 } 1692 } 1693 1694 // Dummy block for created transactions. 1695 if block == nil { 1696 block = &BlockMeta{Block{*empty, 0}, 1697 rec.Received, 1698 0} 1699 } 1700 1701 // Otherwise create a full record and insert it. 1702 version := rec.MsgTx.TxOut[index].Version 1703 p2shScript := rec.MsgTx.TxOut[index].PkScript 1704 class := stdscript.DetermineScriptType(version, p2shScript) 1705 tree := wire.TxTreeRegular 1706 class, isStakeType := txrules.StakeSubScriptType(class) 1707 if isStakeType { 1708 tree = wire.TxTreeStake 1709 } 1710 if class != stdscript.STScriptHash { 1711 return errors.E(errors.Invalid, "multisig output must be P2SH") 1712 } 1713 scriptHash, err := getScriptHashFromP2SHScript(p2shScript) 1714 if err != nil { 1715 return err 1716 } 1717 multisigScript, err := s.manager.redeemScriptForHash160(addrmgrNs, scriptHash) 1718 if err != nil { 1719 return err 1720 } 1721 if version != 0 { 1722 return errors.E(errors.IO, "only version 0 scripts are supported") 1723 } 1724 multisigDetails := stdscript.ExtractMultiSigScriptDetailsV0(multisigScript, false) 1725 if !multisigDetails.Valid { 1726 return errors.E(errors.IO, "invalid m-of-n multisig script") 1727 } 1728 var p2shScriptHash [ripemd160.Size]byte 1729 copy(p2shScriptHash[:], scriptHash) 1730 val = valueMultisigOut(p2shScriptHash, 1731 uint8(multisigDetails.RequiredSigs), 1732 uint8(multisigDetails.NumPubKeys), 1733 false, 1734 tree, 1735 block.Block.Hash, 1736 uint32(block.Block.Height), 1737 dcrutil.Amount(rec.MsgTx.TxOut[index].Value), 1738 *empty, // Unspent 1739 0xFFFFFFFF, // Unspent 1740 rec.Hash) 1741 1742 // Write the output, and insert the unspent key. 1743 err = putMultisigOutRawValues(ns, key, val) 1744 if err != nil { 1745 return err 1746 } 1747 return putMultisigOutUS(ns, key) 1748 } 1749 1750 // SpendMultisigOut spends a multisignature output by making it spent in 1751 // the general bucket and removing it from the unspent bucket. 1752 func (s *Store) SpendMultisigOut(ns walletdb.ReadWriteBucket, op *wire.OutPoint, spendHash chainhash.Hash, spendIndex uint32) error { 1753 // Mark the output spent. 1754 key := keyMultisigOut(op.Hash, op.Index) 1755 val := existsMultisigOutCopy(ns, key) 1756 if val == nil { 1757 return errors.E(errors.NotExist, errors.Errorf("no multisig output for outpoint %v", op)) 1758 } 1759 // Attempting to double spend an outpoint is an error. 1760 if fetchMultisigOutSpent(val) { 1761 _, foundSpendHash, foundSpendIndex := fetchMultisigOutSpentVerbose(val) 1762 // It's not technically an error to try to respend 1763 // the output with exactly the same transaction. 1764 // However, there's no need to set it again. Just return. 1765 if spendHash == foundSpendHash && foundSpendIndex == spendIndex { 1766 return nil 1767 } 1768 return errors.E(errors.DoubleSpend, errors.Errorf("outpoint %v spent by %v", op, &foundSpendHash)) 1769 } 1770 setMultisigOutSpent(val, spendHash, spendIndex) 1771 1772 // Check to see that it's in the unspent bucket. 1773 existsUnspent := existsMultisigOutUS(ns, key) 1774 if !existsUnspent { 1775 return errors.E(errors.IO, "missing unspent multisig record") 1776 } 1777 1778 // Write the updated output, and delete the unspent key. 1779 err := putMultisigOutRawValues(ns, key, val) 1780 if err != nil { 1781 return err 1782 } 1783 return deleteMultisigOutUS(ns, key) 1784 } 1785 1786 func approvesParent(voteBits uint16) bool { 1787 return dcrutil.IsFlagSet16(voteBits, dcrutil.BlockValid) 1788 } 1789 1790 // Rollback removes all blocks at height onwards, moving any transactions within 1791 // each block to the unconfirmed pool. 1792 func (s *Store) Rollback(dbtx walletdb.ReadWriteTx, height int32) error { 1793 // Note: does not stake validate the parent block at height-1. Assumes the 1794 // rollback is being done to add more blocks starting at height, and stake 1795 // validation will occur when that block is attached. 1796 1797 ns := dbtx.ReadWriteBucket(wtxmgrBucketKey) 1798 addrmgrNs := dbtx.ReadWriteBucket(waddrmgrBucketKey) 1799 1800 if height == 0 { 1801 return errors.E(errors.Invalid, "cannot rollback the genesis block") 1802 } 1803 1804 minedBalance, err := fetchMinedBalance(ns) 1805 if err != nil { 1806 return err 1807 } 1808 1809 // Keep track of all credits that were removed from coinbase 1810 // transactions. After detaching all blocks, if any transaction record 1811 // exists in unmined that spends these outputs, remove them and their 1812 // spend chains. 1813 // 1814 // It is necessary to keep these in memory and fix the unmined 1815 // transactions later since blocks are removed in increasing order. 1816 var coinBaseCredits []wire.OutPoint 1817 1818 var heightsToRemove []int32 1819 1820 it := makeReverseBlockIterator(ns) 1821 defer it.close() 1822 for it.prev() { 1823 b := &it.elem 1824 if it.elem.Height < height { 1825 break 1826 } 1827 1828 heightsToRemove = append(heightsToRemove, it.elem.Height) 1829 1830 log.Debugf("Rolling back transactions from block %v height %d", 1831 b.Hash, b.Height) 1832 1833 // cache the values of removed credits so they can be inspected even 1834 // after removal from the db. 1835 removedCredits := make(map[string][]byte) 1836 1837 for i := range b.transactions { 1838 txHash := &b.transactions[i] 1839 1840 recKey := keyTxRecord(txHash, &b.Block) 1841 recVal := existsRawTxRecord(ns, recKey) 1842 var rec TxRecord 1843 err = readRawTxRecord(txHash, recVal, &rec) 1844 if err != nil { 1845 return err 1846 } 1847 1848 err = deleteTxRecord(ns, txHash, &b.Block) 1849 if err != nil { 1850 return err 1851 } 1852 1853 // Handle coinbase transactions specially since they are 1854 // not moved to the unconfirmed store. A coinbase cannot 1855 // contain any debits, but all credits should be removed 1856 // and the mined balance decremented. 1857 if compat.IsEitherCoinBaseTx(&rec.MsgTx) { 1858 for i, output := range rec.MsgTx.TxOut { 1859 k, v := existsCredit(ns, &rec.Hash, 1860 uint32(i), &b.Block) 1861 if v == nil { 1862 continue 1863 } 1864 1865 coinBaseCredits = append(coinBaseCredits, wire.OutPoint{ 1866 Hash: rec.Hash, 1867 Index: uint32(i), 1868 Tree: wire.TxTreeRegular, 1869 }) 1870 1871 outPointKey := canonicalOutPoint(&rec.Hash, uint32(i)) 1872 credKey := existsRawUnspent(ns, outPointKey) 1873 if credKey != nil { 1874 minedBalance -= dcrutil.Amount(output.Value) 1875 err = deleteRawUnspent(ns, outPointKey) 1876 if err != nil { 1877 return err 1878 } 1879 } 1880 removedCredits[string(k)] = v 1881 err = deleteRawCredit(ns, k) 1882 if err != nil { 1883 return err 1884 } 1885 1886 // Check if this output is a multisignature 1887 // P2SH output. If it is, access the value 1888 // for the key and mark it unmined. 1889 msKey := keyMultisigOut(*txHash, uint32(i)) 1890 msVal := existsMultisigOutCopy(ns, msKey) 1891 if msVal != nil { 1892 setMultisigOutUnmined(msVal) 1893 err := putMultisigOutRawValues(ns, msKey, msVal) 1894 if err != nil { 1895 return err 1896 } 1897 } 1898 } 1899 1900 continue 1901 } 1902 1903 err = putRawUnmined(ns, txHash[:], recVal) 1904 if err != nil { 1905 return err 1906 } 1907 1908 txType := rec.TxType 1909 1910 // For each debit recorded for this transaction, mark 1911 // the credit it spends as unspent (as long as it still 1912 // exists) and delete the debit. The previous output is 1913 // recorded in the unconfirmed store for every previous 1914 // output, not just debits. 1915 for i, input := range rec.MsgTx.TxIn { 1916 // Skip stakebases. 1917 if i == 0 && txType == stake.TxTypeSSGen { 1918 continue 1919 } 1920 1921 prevOut := &input.PreviousOutPoint 1922 prevOutKey := canonicalOutPoint(&prevOut.Hash, 1923 prevOut.Index) 1924 err = putRawUnminedInput(ns, prevOutKey, rec.Hash[:]) 1925 if err != nil { 1926 return err 1927 } 1928 1929 // If this input is a debit, remove the debit 1930 // record and mark the credit that it spent as 1931 // unspent, incrementing the mined balance. 1932 debKey, credKey, err := existsDebit(ns, 1933 &rec.Hash, uint32(i), &b.Block) 1934 if err != nil { 1935 return err 1936 } 1937 if debKey == nil { 1938 continue 1939 } 1940 1941 // Store the credit OP code for later use. Since the credit may 1942 // already have been removed if it also appeared in this block, 1943 // a cache of removed credits is also checked. 1944 credVal := existsRawCredit(ns, credKey) 1945 if credVal == nil { 1946 credVal = removedCredits[string(credKey)] 1947 } 1948 if credVal == nil { 1949 return errors.E(errors.IO, errors.Errorf("missing credit "+ 1950 "%v, key %x, spent by %v", prevOut, credKey, &rec.Hash)) 1951 } 1952 creditOpCode := fetchRawCreditTagOpCode(credVal) 1953 1954 // unspendRawCredit does not error in case the no credit exists 1955 // for this key, but this behavior is correct. Since 1956 // transactions are removed in an unspecified order 1957 // (transactions in the blo ck record are not sorted by 1958 // appearance in the block), this credit may have already been 1959 // removed. 1960 var amt dcrutil.Amount 1961 amt, err = unspendRawCredit(ns, credKey) 1962 if err != nil { 1963 return err 1964 } 1965 1966 err = deleteRawDebit(ns, debKey) 1967 if err != nil { 1968 return err 1969 } 1970 1971 // If the credit was previously removed in the 1972 // rollback, the credit amount is zero. Only 1973 // mark the previously spent credit as unspent 1974 // if it still exists. 1975 if amt == 0 { 1976 continue 1977 } 1978 unspentVal, err := fetchRawCreditUnspentValue(credKey) 1979 if err != nil { 1980 return err 1981 } 1982 1983 // Ticket output spends are never decremented, so no need 1984 // to add them back. 1985 if !(creditOpCode == txscript.OP_SSTX) { 1986 minedBalance += amt 1987 } 1988 1989 err = putRawUnspent(ns, prevOutKey, unspentVal) 1990 if err != nil { 1991 return err 1992 } 1993 1994 // Check if this input uses a multisignature P2SH 1995 // output. If it did, mark the output unspent 1996 // and create an entry in the unspent bucket. 1997 msVal := existsMultisigOutCopy(ns, prevOutKey) 1998 if msVal != nil { 1999 setMultisigOutUnSpent(msVal) 2000 err := putMultisigOutRawValues(ns, prevOutKey, msVal) 2001 if err != nil { 2002 return err 2003 } 2004 err = putMultisigOutUS(ns, prevOutKey) 2005 if err != nil { 2006 return err 2007 } 2008 } 2009 } 2010 2011 // For each detached non-coinbase credit, move the 2012 // credit output to unmined. If the credit is marked 2013 // unspent, it is removed from the utxo set and the 2014 // mined balance is decremented. 2015 // 2016 // TODO: use a credit iterator 2017 for i, output := range rec.MsgTx.TxOut { 2018 k, v := existsCredit(ns, &rec.Hash, uint32(i), 2019 &b.Block) 2020 if v == nil { 2021 continue 2022 } 2023 vcopy := make([]byte, len(v)) 2024 copy(vcopy, v) 2025 removedCredits[string(k)] = vcopy 2026 2027 amt, change, err := fetchRawCreditAmountChange(v) 2028 if err != nil { 2029 return err 2030 } 2031 opCode := fetchRawCreditTagOpCode(v) 2032 isCoinbase := fetchRawCreditIsCoinbase(v) 2033 hasExpiry := fetchRawCreditHasExpiry(v, DBVersion) 2034 2035 scrType := pkScriptType(output.Version, output.PkScript) 2036 scrLoc := rec.MsgTx.PkScriptLocs()[i] 2037 scrLen := len(rec.MsgTx.TxOut[i].PkScript) 2038 2039 acct, err := s.fetchAccountForPkScript(addrmgrNs, v, nil, output.PkScript) 2040 if err != nil { 2041 return err 2042 } 2043 2044 outPointKey := canonicalOutPoint(&rec.Hash, uint32(i)) 2045 unminedCredVal := valueUnminedCredit(amt, change, opCode, 2046 isCoinbase, hasExpiry, scrType, uint32(scrLoc), uint32(scrLen), 2047 acct, DBVersion) 2048 err = putRawUnminedCredit(ns, outPointKey, unminedCredVal) 2049 if err != nil { 2050 return err 2051 } 2052 2053 err = deleteRawCredit(ns, k) 2054 if err != nil { 2055 return err 2056 } 2057 2058 credKey := existsRawUnspent(ns, outPointKey) 2059 if credKey != nil { 2060 // Ticket amounts were never added, so ignore them when 2061 // correcting the balance. 2062 isTicketOutput := (txType == stake.TxTypeSStx && i == 0) 2063 if !isTicketOutput { 2064 minedBalance -= dcrutil.Amount(output.Value) 2065 } 2066 err = deleteRawUnspent(ns, outPointKey) 2067 if err != nil { 2068 return err 2069 } 2070 } 2071 2072 // Check if this output is a multisignature 2073 // P2SH output. If it is, access the value 2074 // for the key and mark it unmined. 2075 msKey := keyMultisigOut(*txHash, uint32(i)) 2076 msVal := existsMultisigOutCopy(ns, msKey) 2077 if msVal != nil { 2078 setMultisigOutUnmined(msVal) 2079 err := putMultisigOutRawValues(ns, msKey, msVal) 2080 if err != nil { 2081 return err 2082 } 2083 } 2084 } 2085 2086 // When rolling back votes and revocations, return unspent status 2087 // for tracked commitments. 2088 if (rec.TxType == stake.TxTypeSSGen) || (rec.TxType == stake.TxTypeSSRtx) { 2089 err = s.replaceTicketCommitmentUnminedSpent(ns, rec.TxType, &rec.MsgTx, true) 2090 if err != nil { 2091 return err 2092 } 2093 } 2094 } 2095 2096 // reposition cursor before deleting this k/v pair and advancing to the 2097 // previous. 2098 it.reposition(it.elem.Height) 2099 2100 // Avoid cursor deletion until bolt issue #620 is resolved. 2101 // err = it.delete() 2102 // if err != nil { 2103 // return err 2104 // } 2105 } 2106 if it.err != nil { 2107 return it.err 2108 } 2109 2110 // Delete the block records outside of the iteration since cursor deletion 2111 // is broken. 2112 for _, h := range heightsToRemove { 2113 err = deleteBlockRecord(ns, h) 2114 if err != nil { 2115 return err 2116 } 2117 } 2118 2119 for _, op := range coinBaseCredits { 2120 opKey := canonicalOutPoint(&op.Hash, op.Index) 2121 unminedKey := existsRawUnminedInput(ns, opKey) 2122 if unminedKey != nil { 2123 unminedVal := existsRawUnmined(ns, unminedKey) 2124 var unminedRec TxRecord 2125 copy(unminedRec.Hash[:], unminedKey) // Silly but need an array 2126 err = readRawTxRecord(&unminedRec.Hash, unminedVal, &unminedRec) 2127 if err != nil { 2128 return err 2129 } 2130 2131 log.Debugf("Transaction %v spends a removed coinbase "+ 2132 "output -- removing as well", unminedRec.Hash) 2133 err = s.RemoveUnconfirmed(ns, &unminedRec.MsgTx, &unminedRec.Hash) 2134 if err != nil { 2135 return err 2136 } 2137 } 2138 } 2139 2140 err = putMinedBalance(ns, minedBalance) 2141 if err != nil { 2142 return err 2143 } 2144 2145 // Mark block hash for height-1 as the new main chain tip. 2146 _, newTipBlockRecord := existsBlockRecord(ns, height-1) 2147 newTipHash := extractRawBlockRecordHash(newTipBlockRecord) 2148 err = ns.Put(rootTipBlock, newTipHash) 2149 if err != nil { 2150 return errors.E(errors.IO, err) 2151 } 2152 2153 return nil 2154 } 2155 2156 // outputCreditInfo fetches information about a credit from the database, 2157 // fills out a credit struct, and returns it. 2158 func (s *Store) outputCreditInfo(ns walletdb.ReadBucket, op wire.OutPoint, block *Block) (*Credit, error) { 2159 // It has to exists as a credit or an unmined credit. 2160 // Look both of these up. If it doesn't, throw an 2161 // error. Check unmined first, then mined. 2162 var minedCredV []byte 2163 unminedCredV := existsRawUnminedCredit(ns, 2164 canonicalOutPoint(&op.Hash, op.Index)) 2165 if unminedCredV == nil { 2166 if block != nil { 2167 credK := keyCredit(&op.Hash, op.Index, block) 2168 minedCredV = existsRawCredit(ns, credK) 2169 } 2170 } 2171 if minedCredV == nil && unminedCredV == nil { 2172 return nil, errors.E(errors.IO, errors.Errorf("missing credit for outpoint %v", &op)) 2173 } 2174 2175 // Error for DB inconsistency if we find any. 2176 if minedCredV != nil && unminedCredV != nil { 2177 return nil, errors.E(errors.IO, errors.Errorf("inconsistency: credit %v is marked mined and unmined", &op)) 2178 } 2179 2180 var amt dcrutil.Amount 2181 var opCode uint8 2182 var isCoinbase bool 2183 var hasExpiry bool 2184 var mined bool 2185 var blockTime time.Time 2186 var pkScript []byte 2187 var receiveTime time.Time 2188 2189 if unminedCredV != nil { 2190 var err error 2191 amt, err = fetchRawUnminedCreditAmount(unminedCredV) 2192 if err != nil { 2193 return nil, err 2194 } 2195 2196 opCode = fetchRawUnminedCreditTagOpCode(unminedCredV) 2197 hasExpiry = fetchRawCreditHasExpiry(unminedCredV, DBVersion) 2198 2199 v := existsRawUnmined(ns, op.Hash[:]) 2200 received, err := fetchRawUnminedReceiveTime(v) 2201 if err != nil { 2202 return nil, err 2203 } 2204 receiveTime = time.Unix(received, 0) 2205 2206 var tx wire.MsgTx 2207 err = tx.Deserialize(bytes.NewReader(extractRawUnminedTx(v))) 2208 if err != nil { 2209 return nil, errors.E(errors.IO, err) 2210 } 2211 if op.Index >= uint32(len(tx.TxOut)) { 2212 return nil, errors.E(errors.IO, errors.Errorf("no output %d for tx %v", op.Index, &op.Hash)) 2213 } 2214 pkScript = tx.TxOut[op.Index].PkScript 2215 } else { 2216 mined = true 2217 2218 var err error 2219 amt, err = fetchRawCreditAmount(minedCredV) 2220 if err != nil { 2221 return nil, err 2222 } 2223 2224 opCode = fetchRawCreditTagOpCode(minedCredV) 2225 isCoinbase = fetchRawCreditIsCoinbase(minedCredV) 2226 hasExpiry = fetchRawCreditHasExpiry(minedCredV, DBVersion) 2227 2228 scrLoc := fetchRawCreditScriptOffset(minedCredV) 2229 scrLen := fetchRawCreditScriptLength(minedCredV) 2230 2231 recK, recV := existsTxRecord(ns, &op.Hash, block) 2232 receiveTime = fetchRawTxRecordReceived(recV) 2233 pkScript, err = fetchRawTxRecordPkScript(recK, recV, op.Index, 2234 scrLoc, scrLen) 2235 if err != nil { 2236 return nil, err 2237 } 2238 } 2239 2240 op.Tree = wire.TxTreeRegular 2241 if opCode != opNonstake { 2242 op.Tree = wire.TxTreeStake 2243 } 2244 2245 c := &Credit{ 2246 OutPoint: op, 2247 BlockMeta: BlockMeta{ 2248 Block: Block{Height: -1}, 2249 Time: blockTime, 2250 }, 2251 Amount: amt, 2252 PkScript: pkScript, 2253 Received: receiveTime, 2254 FromCoinBase: isCoinbase, 2255 HasExpiry: hasExpiry, 2256 } 2257 if mined { 2258 c.BlockMeta.Block = *block 2259 } 2260 return c, nil 2261 } 2262 2263 func randUint32() uint32 { 2264 b := make([]byte, 4) 2265 rand.Read(b[:]) 2266 return binary.LittleEndian.Uint32(b) 2267 } 2268 2269 func randUint32n(n uint32) uint32 { 2270 if n < 2 { 2271 return 0 2272 } 2273 n-- 2274 mask := ^uint32(0) >> bits.LeadingZeros32(n) 2275 for { 2276 v := randUint32() & mask 2277 if v <= n { 2278 return v 2279 } 2280 } 2281 } 2282 2283 func shuffle(n int, swap func(i, j int)) { 2284 if n < 0 { 2285 panic("shuffle: negative n") 2286 } 2287 if int64(n) >= 1<<32 { 2288 panic("shuffle: large n") 2289 } 2290 2291 // Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle 2292 for i := uint32(0); i < uint32(n); i++ { 2293 j := randUint32n(uint32(n)-i) + i 2294 swap(int(i), int(j)) 2295 } 2296 } 2297 2298 // UnspentOutputCount returns the number of mined unspent Credits (including 2299 // those spent by unmined transactions). 2300 func (s *Store) UnspentOutputCount(dbtx walletdb.ReadTx) int { 2301 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2302 return ns.NestedReadBucket(bucketUnspent).KeyN() 2303 } 2304 2305 // randomUTXO returns a random key/value pair from the unspent bucket, ignoring 2306 // any keys that match the skip function. 2307 func (s *Store) randomUTXO(dbtx walletdb.ReadTx, skip func(k, v []byte) bool) (k, v []byte, err error) { 2308 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2309 2310 r := make([]byte, 33) 2311 _, err = rand.Read(r) 2312 if err != nil { 2313 return nil, nil, err 2314 } 2315 randKey := r[:32] 2316 prevFirst := r[32]&1 == 1 2317 2318 c := ns.NestedReadBucket(bucketUnspent).ReadCursor() 2319 k, v = c.Seek(randKey) 2320 iter := c.Next 2321 if prevFirst { 2322 iter = c.Prev 2323 k, v = iter() 2324 } 2325 2326 var keys [][]byte 2327 for ; k != nil; k, v = iter() { 2328 if len(keys) > 0 && !bytes.Equal(keys[0][:32], k[:32]) { 2329 break 2330 } 2331 if skip(k, v) { 2332 continue 2333 } 2334 keys = append(keys, append(make([]byte, 0, 36), k...)) 2335 } 2336 // Pick random output when at least one random transaction was found. 2337 if len(keys) > 0 { 2338 k, v = c.Seek(keys[randUint32n(uint32(len(keys)))]) 2339 c.Close() 2340 return k, v, nil 2341 } 2342 2343 // Search the opposite direction from the random seek key. 2344 if prevFirst { 2345 k, v = c.Seek(randKey) 2346 iter = c.Next 2347 } else { 2348 c.Seek(randKey) 2349 iter = c.Prev 2350 k, v = iter() 2351 } 2352 for ; k != nil; k, v = iter() { 2353 if len(keys) > 0 && !bytes.Equal(keys[0][:32], k[:32]) { 2354 break 2355 } 2356 if skip(k, v) { 2357 continue 2358 } 2359 keys = append(keys, append(make([]byte, 0, 36), k...)) 2360 } 2361 if len(keys) > 0 { 2362 k, v = c.Seek(keys[randUint32n(uint32(len(keys)))]) 2363 c.Close() 2364 return k, v, err 2365 } 2366 2367 c.Close() 2368 return nil, nil, nil 2369 } 2370 2371 // RandomUTXO returns a random unspent Credit, or nil if none matching are 2372 // found. 2373 // 2374 // As an optimization to avoid reading all unspent outputs, this method is 2375 // limited only to mined outputs, and minConf may not be zero. 2376 func (s *Store) RandomUTXO(dbtx walletdb.ReadTx, minConf, syncHeight int32) (*Credit, error) { 2377 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2378 2379 if minConf == 0 { 2380 return nil, errors.E(errors.Invalid, 2381 "optimized random utxo selection not possible with minConf=0") 2382 } 2383 2384 skip := func(k, v []byte) bool { 2385 if existsRawUnminedInput(ns, k) != nil { 2386 // Output is spent by an unmined transaction. 2387 return true 2388 } 2389 var block Block 2390 err := readUnspentBlock(v, &block) 2391 if err != nil || !confirmed(minConf, block.Height, syncHeight) { 2392 return true 2393 } 2394 return false 2395 } 2396 k, v, err := s.randomUTXO(dbtx, skip) 2397 if k == nil || err != nil { 2398 return nil, err 2399 } 2400 var op wire.OutPoint 2401 var block Block 2402 err = readCanonicalOutPoint(k, &op) 2403 if err != nil { 2404 return nil, err 2405 } 2406 err = readUnspentBlock(v, &block) 2407 if err != nil { 2408 return nil, err 2409 } 2410 return s.outputCreditInfo(ns, op, &block) 2411 } 2412 2413 // UnspentOutputs returns all unspent received transaction outputs. 2414 // The order is undefined. 2415 func (s *Store) UnspentOutputs(dbtx walletdb.ReadTx) ([]*Credit, error) { 2416 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2417 var unspent []*Credit 2418 2419 var op wire.OutPoint 2420 var block Block 2421 c := ns.NestedReadBucket(bucketUnspent).ReadCursor() 2422 for k, v := c.First(); k != nil; k, v = c.Next() { 2423 err := readCanonicalOutPoint(k, &op) 2424 if err != nil { 2425 c.Close() 2426 return nil, err 2427 } 2428 if existsRawUnminedInput(ns, k) != nil { 2429 // Output is spent by an unmined transaction. 2430 // Skip this k/v pair. 2431 continue 2432 } 2433 2434 err = readUnspentBlock(v, &block) 2435 if err != nil { 2436 c.Close() 2437 return nil, err 2438 } 2439 2440 cred, err := s.outputCreditInfo(ns, op, &block) 2441 if err != nil { 2442 c.Close() 2443 return nil, err 2444 } 2445 2446 unspent = append(unspent, cred) 2447 } 2448 c.Close() 2449 2450 c = ns.NestedReadBucket(bucketUnminedCredits).ReadCursor() 2451 for k, _ := c.First(); k != nil; k, _ = c.Next() { 2452 if existsRawUnminedInput(ns, k) != nil { 2453 // Output is spent by an unmined transaction. 2454 // Skip to next unmined credit. 2455 continue 2456 } 2457 2458 // Skip outputs from unpublished transactions. 2459 txHash := k[:32] 2460 if existsUnpublished(ns, txHash) { 2461 continue 2462 } 2463 2464 err := readCanonicalOutPoint(k, &op) 2465 if err != nil { 2466 c.Close() 2467 return nil, err 2468 } 2469 2470 cred, err := s.outputCreditInfo(ns, op, nil) 2471 if err != nil { 2472 c.Close() 2473 return nil, err 2474 } 2475 2476 unspent = append(unspent, cred) 2477 } 2478 c.Close() 2479 2480 log.Tracef("%v many utxos found in database", len(unspent)) 2481 2482 return unspent, nil 2483 } 2484 2485 // UnspentOutput returns details for an unspent received transaction output. 2486 // Returns error NotExist if the specified outpoint cannot be found or has been 2487 // spent by a mined transaction. Mined transactions that are spent by a mempool 2488 // transaction are not affected by this. 2489 func (s *Store) UnspentOutput(ns walletdb.ReadBucket, op wire.OutPoint, includeMempool bool) (*Credit, error) { 2490 k := canonicalOutPoint(&op.Hash, op.Index) 2491 // Check if unspent output is in mempool (if includeMempool == true). 2492 if includeMempool && existsRawUnminedCredit(ns, k) != nil { 2493 return s.outputCreditInfo(ns, op, nil) 2494 } 2495 // Check for unspent output in bucket for mined unspents. 2496 if v := ns.NestedReadBucket(bucketUnspent).Get(k); v != nil { 2497 var block Block 2498 err := readUnspentBlock(v, &block) 2499 if err != nil { 2500 return nil, err 2501 } 2502 return s.outputCreditInfo(ns, op, &block) 2503 } 2504 return nil, errors.E(errors.NotExist, errors.Errorf("no unspent output %v", op)) 2505 } 2506 2507 // ForEachUnspentOutpoint calls f on each UTXO outpoint. 2508 // The order is undefined. 2509 func (s *Store) ForEachUnspentOutpoint(dbtx walletdb.ReadTx, f func(*wire.OutPoint) error) error { 2510 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2511 c := ns.NestedReadBucket(bucketUnspent).ReadCursor() 2512 defer func() { 2513 c.Close() 2514 }() 2515 for k, v := c.First(); k != nil; k, v = c.Next() { 2516 var op wire.OutPoint 2517 err := readCanonicalOutPoint(k, &op) 2518 if err != nil { 2519 return err 2520 } 2521 if existsRawUnminedInput(ns, k) != nil { 2522 // Output is spent by an unmined transaction. 2523 // Skip this k/v pair. 2524 continue 2525 } 2526 2527 block := new(Block) 2528 err = readUnspentBlock(v, block) 2529 if err != nil { 2530 return err 2531 } 2532 2533 kC := keyCredit(&op.Hash, op.Index, block) 2534 vC := existsRawCredit(ns, kC) 2535 opCode := fetchRawCreditTagOpCode(vC) 2536 op.Tree = wire.TxTreeRegular 2537 if opCode != opNonstake { 2538 op.Tree = wire.TxTreeStake 2539 } 2540 2541 if err := f(&op); err != nil { 2542 return err 2543 } 2544 } 2545 2546 c.Close() 2547 c = ns.NestedReadBucket(bucketUnminedCredits).ReadCursor() 2548 for k, v := c.First(); k != nil; k, v = c.Next() { 2549 if existsRawUnminedInput(ns, k) != nil { 2550 // Output is spent by an unmined transaction. 2551 // Skip to next unmined credit. 2552 continue 2553 } 2554 2555 // Skip outputs from unpublished transactions. 2556 txHash := k[:32] 2557 if existsUnpublished(ns, txHash) { 2558 continue 2559 } 2560 2561 var op wire.OutPoint 2562 err := readCanonicalOutPoint(k, &op) 2563 if err != nil { 2564 return err 2565 } 2566 2567 opCode := fetchRawUnminedCreditTagOpCode(v) 2568 op.Tree = wire.TxTreeRegular 2569 if opCode != opNonstake { 2570 op.Tree = wire.TxTreeStake 2571 } 2572 2573 if err := f(&op); err != nil { 2574 return err 2575 } 2576 } 2577 2578 return nil 2579 } 2580 2581 // IsUnspentOutpoint returns whether the outpoint is recorded as a wallet UTXO. 2582 func (s *Store) IsUnspentOutpoint(dbtx walletdb.ReadTx, op *wire.OutPoint) bool { 2583 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2584 2585 // Outputs from unpublished transactions are not UTXOs yet. 2586 if existsUnpublished(ns, op.Hash[:]) { 2587 return false 2588 } 2589 2590 k := canonicalOutPoint(&op.Hash, op.Index) 2591 if v := ns.NestedReadBucket(bucketUnspent); v != nil { 2592 // Output is mined and not spent by any other mined tx, but may be spent 2593 // by an unmined transaction. 2594 return existsRawUnminedInput(ns, k) == nil 2595 } 2596 if v := existsRawUnminedCredit(ns, k); v != nil { 2597 // Output is in an unmined transaction, but may be spent by another 2598 // unmined transaction. 2599 return existsRawUnminedInput(ns, k) == nil 2600 } 2601 return false 2602 } 2603 2604 // UnspentTickets returns all unspent tickets that are known for this wallet. 2605 // Tickets that have been spent by an unmined vote that is not a vote on the tip 2606 // block are also considered unspent and are returned. The order of the hashes 2607 // is undefined. 2608 func (s *Store) UnspentTickets(dbtx walletdb.ReadTx, syncHeight int32, includeImmature bool) ([]chainhash.Hash, error) { 2609 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2610 tipBlock, _ := s.MainChainTip(dbtx) 2611 var tickets []chainhash.Hash 2612 c := ns.NestedReadBucket(bucketTickets).ReadCursor() 2613 defer c.Close() 2614 var hash chainhash.Hash 2615 for ticketHash, _ := c.First(); ticketHash != nil; ticketHash, _ = c.Next() { 2616 copy(hash[:], ticketHash) 2617 2618 // Skip over tickets that are spent by votes or revocations. As long as 2619 // the ticket is relevant to the wallet, output zero is recorded as a 2620 // credit. Use the credit's spent tracking to determine if the ticket 2621 // is spent or not. 2622 opKey := canonicalOutPoint(&hash, 0) 2623 if existsRawUnspent(ns, opKey) == nil { 2624 // No unspent record indicates the output was spent by a mined 2625 // transaction. 2626 continue 2627 } 2628 if spenderHash := existsRawUnminedInput(ns, opKey); spenderHash != nil { 2629 // A non-nil record for the outpoint indicates that there exists an 2630 // unmined transaction that spends the output. Determine if the 2631 // spender is a vote, and append the hash if the vote is not for the 2632 // tip block height. Otherwise continue to the next ticket. 2633 serializedSpender := extractRawUnminedTx(existsRawUnmined(ns, spenderHash)) 2634 if serializedSpender == nil { 2635 continue 2636 } 2637 var spender wire.MsgTx 2638 err := spender.Deserialize(bytes.NewReader(serializedSpender)) 2639 if err != nil { 2640 return nil, errors.E(errors.IO, err) 2641 } 2642 if stake.IsSSGen(&spender) { 2643 voteBlock, _ := stake.SSGenBlockVotedOn(&spender) 2644 if voteBlock != tipBlock { 2645 goto Include 2646 } 2647 } 2648 2649 continue 2650 } 2651 2652 // When configured to exclude immature tickets, skip the transaction if 2653 // is unmined or has not reached ticket maturity yet. 2654 if !includeImmature { 2655 txRecKey, _ := latestTxRecord(ns, ticketHash) 2656 if txRecKey == nil { 2657 continue 2658 } 2659 var height int32 2660 err := readRawTxRecordBlockHeight(txRecKey, &height) 2661 if err != nil { 2662 return nil, err 2663 } 2664 if !ticketMatured(s.chainParams, height, syncHeight) { 2665 continue 2666 } 2667 } 2668 2669 Include: 2670 tickets = append(tickets, hash) 2671 } 2672 return tickets, nil 2673 } 2674 2675 // OwnTicket returns whether ticketHash is the hash of a ticket purchase 2676 // transaction managed by the wallet. 2677 func (s *Store) OwnTicket(dbtx walletdb.ReadTx, ticketHash *chainhash.Hash) bool { 2678 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2679 v := existsRawTicketRecord(ns, ticketHash[:]) 2680 return v != nil 2681 } 2682 2683 // Ticket embeds a TxRecord for a ticket purchase transaction, the block it is 2684 // mined in (if any), and the transaction hash of the vote or revocation 2685 // transaction that spends the ticket (if any). 2686 type Ticket struct { 2687 TxRecord 2688 Block Block // Height -1 if unmined 2689 SpenderHash chainhash.Hash // Zero value if unspent 2690 } 2691 2692 // TicketIterator is used to iterate over all ticket purchase transactions. 2693 type TicketIterator struct { 2694 Ticket 2695 ns walletdb.ReadBucket 2696 c walletdb.ReadCursor 2697 ck, cv []byte 2698 err error 2699 } 2700 2701 // IterateTickets returns an object used to iterate over all ticket purchase 2702 // transactions. 2703 func (s *Store) IterateTickets(dbtx walletdb.ReadTx) *TicketIterator { 2704 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2705 c := ns.NestedReadBucket(bucketTickets).ReadCursor() 2706 ck, cv := c.First() 2707 return &TicketIterator{ns: ns, c: c, ck: ck, cv: cv} 2708 } 2709 2710 // Next reads the next Ticket from the database, writing it to the iterator's 2711 // embedded Ticket member. Returns false after all tickets have been iterated 2712 // over or an error occurs. 2713 func (it *TicketIterator) Next() bool { 2714 if it.err != nil { 2715 return false 2716 } 2717 2718 // Some tickets may be recorded in the tickets bucket but the transaction 2719 // records for them are missing because they were double spent and removed. 2720 // Add a label here so that the code below can branch back here to skip to 2721 // the next ticket. This could also be implemented using a for loop, but at 2722 // the loss of an indent. 2723 CheckNext: 2724 2725 // The cursor value will be nil when all items in the bucket have been 2726 // iterated over. 2727 if it.cv == nil { 2728 return false 2729 } 2730 2731 // Determine whether there is a mined transaction record for the ticket 2732 // purchase, an unmined transaction record, or no recorded transaction at 2733 // all. 2734 var ticketHash chainhash.Hash 2735 copy(ticketHash[:], it.ck) 2736 if k, v := latestTxRecord(it.ns, it.ck); v != nil { 2737 // Ticket is recorded mined 2738 err := readRawTxRecordBlock(k, &it.Block) 2739 if err != nil { 2740 it.err = err 2741 return false 2742 } 2743 err = readRawTxRecord(&ticketHash, v, &it.TxRecord) 2744 if err != nil { 2745 it.err = err 2746 return false 2747 } 2748 2749 // Check if the ticket is spent or not. Look up the credit for output 0 2750 // and check if either a debit is recorded or the output is spent by an 2751 // unmined transaction. 2752 _, credVal := existsCredit(it.ns, &ticketHash, 0, &it.Block) 2753 if credVal != nil { 2754 if extractRawCreditIsSpent(credVal) { 2755 debKey := extractRawCreditSpenderDebitKey(credVal) 2756 debHash := extractRawDebitHash(debKey) 2757 copy(it.SpenderHash[:], debHash) 2758 } else { 2759 it.SpenderHash = chainhash.Hash{} 2760 } 2761 } else { 2762 opKey := canonicalOutPoint(&ticketHash, 0) 2763 spenderVal := existsRawUnminedInput(it.ns, opKey) 2764 if spenderVal != nil { 2765 copy(it.SpenderHash[:], spenderVal) 2766 } else { 2767 it.SpenderHash = chainhash.Hash{} 2768 } 2769 } 2770 } else if v := existsRawUnmined(it.ns, ticketHash[:]); v != nil { 2771 // Ticket is recorded unmined 2772 it.Block = Block{Height: -1} 2773 // Unmined tickets cannot be spent 2774 it.SpenderHash = chainhash.Hash{} 2775 err := readRawTxRecord(&ticketHash, v, &it.TxRecord) 2776 if err != nil { 2777 it.err = err 2778 return false 2779 } 2780 } else { 2781 // Transaction was removed, skip to next 2782 it.ck, it.cv = it.c.Next() 2783 goto CheckNext 2784 } 2785 2786 // Advance the cursor to the next item before returning. Next expects the 2787 // cursor key and value to be set to the next item to read. 2788 it.ck, it.cv = it.c.Next() 2789 2790 return true 2791 } 2792 2793 // Err returns the final error state of the iterator. It should be checked 2794 // after iteration completes when Next returns false. 2795 func (it *TicketIterator) Err() error { return it.err } 2796 2797 func (it *TicketIterator) Close() { 2798 if it.c != nil { 2799 it.c.Close() 2800 } 2801 } 2802 2803 // MultisigCredit is a redeemable P2SH multisignature credit. 2804 type MultisigCredit struct { 2805 OutPoint *wire.OutPoint 2806 ScriptHash [ripemd160.Size]byte 2807 MSScript []byte 2808 M uint8 2809 N uint8 2810 Amount dcrutil.Amount 2811 } 2812 2813 // GetMultisigOutput takes an outpoint and returns multisignature 2814 // credit data stored about it. 2815 func (s *Store) GetMultisigOutput(ns walletdb.ReadBucket, op *wire.OutPoint) (*MultisigOut, error) { 2816 key := canonicalOutPoint(&op.Hash, op.Index) 2817 val := existsMultisigOutCopy(ns, key) 2818 if val == nil { 2819 return nil, errors.E(errors.NotExist, errors.Errorf("no multisig output for outpoint %v", op)) 2820 } 2821 2822 return fetchMultisigOut(key, val) 2823 } 2824 2825 // UnspentMultisigCreditsForAddress returns all unspent multisignature P2SH 2826 // credits in the wallet for some specified address. 2827 func (s *Store) UnspentMultisigCreditsForAddress(dbtx walletdb.ReadTx, addr stdaddr.Address) ([]*MultisigCredit, error) { 2828 ns := dbtx.ReadBucket(wtxmgrBucketKey) 2829 addrmgrNs := dbtx.ReadBucket(waddrmgrBucketKey) 2830 2831 p2shAddr, ok := addr.(*stdaddr.AddressScriptHashV0) 2832 if !ok { 2833 return nil, errors.E(errors.Invalid, "address must be P2SH") 2834 } 2835 addrScrHash := p2shAddr.Hash160() 2836 2837 var mscs []*MultisigCredit 2838 c := ns.NestedReadBucket(bucketMultisigUsp).ReadCursor() 2839 defer c.Close() 2840 for k, _ := c.First(); k != nil; k, _ = c.Next() { 2841 val := existsMultisigOutCopy(ns, k) 2842 if val == nil { 2843 return nil, errors.E(errors.IO, "missing multisig credit") 2844 } 2845 2846 // Skip everything that's unrelated to the address 2847 // we're concerned about. 2848 scriptHash := fetchMultisigOutScrHash(val) 2849 if scriptHash != *addrScrHash { 2850 continue 2851 } 2852 2853 var op wire.OutPoint 2854 err := readCanonicalOutPoint(k, &op) 2855 if err != nil { 2856 return nil, err 2857 } 2858 2859 multisigScript, err := s.manager.redeemScriptForHash160(addrmgrNs, scriptHash[:]) 2860 if err != nil { 2861 return nil, err 2862 } 2863 m, n := fetchMultisigOutMN(val) 2864 amount := fetchMultisigOutAmount(val) 2865 op.Tree = fetchMultisigOutTree(val) 2866 2867 msc := &MultisigCredit{ 2868 &op, 2869 scriptHash, 2870 multisigScript, 2871 m, 2872 n, 2873 amount, 2874 } 2875 mscs = append(mscs, msc) 2876 } 2877 2878 return mscs, nil 2879 } 2880 2881 type minimalCredit struct { 2882 txRecordKey []byte 2883 index uint32 2884 Amount int64 2885 tree int8 2886 unmined bool 2887 } 2888 2889 // byUtxoAmount defines the methods needed to satisify sort.Interface to 2890 // sort a slice of Utxos by their amount. 2891 type byUtxoAmount []*minimalCredit 2892 2893 func (u byUtxoAmount) Len() int { return len(u) } 2894 func (u byUtxoAmount) Less(i, j int) bool { return u[i].Amount < u[j].Amount } 2895 func (u byUtxoAmount) Swap(i, j int) { u[i], u[j] = u[j], u[i] } 2896 2897 // confirmed checks whether a transaction at height txHeight has met minConf 2898 // confirmations for a blockchain at height curHeight. 2899 func confirmed(minConf, txHeight, curHeight int32) bool { 2900 return confirms(txHeight, curHeight) >= minConf 2901 } 2902 2903 // confirms returns the number of confirmations for a transaction in a block at 2904 // height txHeight (or -1 for an unconfirmed tx) given the chain height 2905 // curHeight. 2906 func confirms(txHeight, curHeight int32) int32 { 2907 switch { 2908 case txHeight == -1, txHeight > curHeight: 2909 return 0 2910 default: 2911 return curHeight - txHeight + 1 2912 } 2913 } 2914 2915 // coinbaseMatured returns whether a transaction mined at txHeight has 2916 // reached coinbase maturity in a chain with tip height curHeight. 2917 func coinbaseMatured(params *chaincfg.Params, txHeight, curHeight int32) bool { 2918 return txHeight >= 0 && curHeight-txHeight+1 > int32(params.CoinbaseMaturity) 2919 } 2920 2921 // ticketChangeMatured returns whether a ticket change mined at 2922 // txHeight has reached ticket maturity in a chain with a tip height 2923 // curHeight. 2924 func ticketChangeMatured(params *chaincfg.Params, txHeight, curHeight int32) bool { 2925 return txHeight >= 0 && curHeight-txHeight+1 > int32(params.SStxChangeMaturity) 2926 } 2927 2928 // ticketMatured returns whether a ticket mined at txHeight has 2929 // reached ticket maturity in a chain with a tip height curHeight. 2930 func ticketMatured(params *chaincfg.Params, txHeight, curHeight int32) bool { 2931 // dcrd has an off-by-one in the calculation of the ticket 2932 // maturity, which results in maturity being one block higher 2933 // than the params would indicate. 2934 return txHeight >= 0 && curHeight-txHeight > int32(params.TicketMaturity) 2935 } 2936 2937 func (s *Store) fastCreditPkScriptLookup(ns walletdb.ReadBucket, credKey []byte, unminedCredKey []byte) ([]byte, error) { 2938 // It has to exists as a credit or an unmined credit. 2939 // Look both of these up. If it doesn't, throw an 2940 // error. Check unmined first, then mined. 2941 var minedCredV []byte 2942 unminedCredV := existsRawUnminedCredit(ns, unminedCredKey) 2943 if unminedCredV == nil { 2944 minedCredV = existsRawCredit(ns, credKey) 2945 } 2946 if minedCredV == nil && unminedCredV == nil { 2947 return nil, errors.E(errors.IO, "missing mined and unmined credit") 2948 } 2949 2950 if unminedCredV != nil { // unmined 2951 var op wire.OutPoint 2952 err := readCanonicalOutPoint(unminedCredKey, &op) 2953 if err != nil { 2954 return nil, err 2955 } 2956 k := op.Hash[:] 2957 v := existsRawUnmined(ns, k) 2958 var tx wire.MsgTx 2959 err = tx.Deserialize(bytes.NewReader(extractRawUnminedTx(v))) 2960 if err != nil { 2961 return nil, errors.E(errors.IO, err) 2962 } 2963 if op.Index >= uint32(len(tx.TxOut)) { 2964 return nil, errors.E(errors.IO, errors.Errorf("no output %d for tx %v", op.Index, &op.Hash)) 2965 } 2966 return tx.TxOut[op.Index].PkScript, nil 2967 } 2968 2969 scrLoc := fetchRawCreditScriptOffset(minedCredV) 2970 scrLen := fetchRawCreditScriptLength(minedCredV) 2971 2972 k := extractRawCreditTxRecordKey(credKey) 2973 v := existsRawTxRecord(ns, k) 2974 idx := extractRawCreditIndex(credKey) 2975 return fetchRawTxRecordPkScript(k, v, idx, scrLoc, scrLen) 2976 } 2977 2978 // minimalCreditToCredit looks up a minimal credit's data and prepares a Credit 2979 // from this data. 2980 func (s *Store) minimalCreditToCredit(ns walletdb.ReadBucket, mc *minimalCredit) (*Credit, error) { 2981 var cred *Credit 2982 2983 switch mc.unmined { 2984 case false: // Mined transactions. 2985 opHash, err := chainhash.NewHash(mc.txRecordKey[0:32]) 2986 if err != nil { 2987 return nil, err 2988 } 2989 2990 var block Block 2991 err = readUnspentBlock(mc.txRecordKey[32:68], &block) 2992 if err != nil { 2993 return nil, err 2994 } 2995 2996 var op wire.OutPoint 2997 op.Hash = *opHash 2998 op.Index = mc.index 2999 3000 cred, err = s.outputCreditInfo(ns, op, &block) 3001 if err != nil { 3002 return nil, err 3003 } 3004 3005 case true: // Unmined transactions. 3006 opHash, err := chainhash.NewHash(mc.txRecordKey[0:32]) 3007 if err != nil { 3008 return nil, err 3009 } 3010 3011 var op wire.OutPoint 3012 op.Hash = *opHash 3013 op.Index = mc.index 3014 3015 cred, err = s.outputCreditInfo(ns, op, nil) 3016 if err != nil { 3017 return nil, err 3018 } 3019 } 3020 3021 return cred, nil 3022 } 3023 3024 // InputSource provides a method (SelectInputs) to incrementally select unspent 3025 // outputs to use as transaction inputs. 3026 type InputSource struct { 3027 source func(dcrutil.Amount) (*txauthor.InputDetail, error) 3028 } 3029 3030 // SelectInputs selects transaction inputs to redeem unspent outputs stored in 3031 // the database. It may be called multiple times with increasing target amounts 3032 // to return additional inputs for a higher target amount. It returns the total 3033 // input amount referenced by the previous transaction outputs, a slice of 3034 // transaction inputs referencing these outputs, and a slice of previous output 3035 // scripts from each previous output referenced by the corresponding input. 3036 func (s *InputSource) SelectInputs(target dcrutil.Amount) (*txauthor.InputDetail, error) { 3037 return s.source(target) 3038 } 3039 3040 // MakeInputSource creates an InputSource to redeem unspent outputs from an 3041 // account. The minConf and syncHeight parameters are used to filter outputs 3042 // based on some spendable policy. An ignore func is called to determine whether 3043 // an output must be excluded from the source, and may be nil to ignore nothing. 3044 func (s *Store) MakeInputSource(dbtx walletdb.ReadTx, account uint32, minConf, 3045 syncHeight int32, ignore func(*wire.OutPoint) bool) InputSource { 3046 3047 ns := dbtx.ReadBucket(wtxmgrBucketKey) 3048 addrmgrNs := dbtx.ReadBucket(waddrmgrBucketKey) 3049 3050 // Cursors to iterate over the (mined) unspent and unmined credit 3051 // buckets. These are closed over by the returned input source and 3052 // reused across multiple calls. 3053 // 3054 // These cursors are initialized to nil and are set to a valid cursor 3055 // when first needed. This is done since cursors are not positioned 3056 // when created, and positioning a cursor also returns a key/value pair. 3057 // The simplest way to handle this is to branch to either cursor.First 3058 // or cursor.Next depending on whether the cursor has already been 3059 // created or not. 3060 var bucketUnspentCursor, bucketUnminedCreditsCursor walletdb.ReadCursor 3061 3062 defer func() { 3063 if bucketUnspentCursor != nil { 3064 bucketUnspentCursor.Close() 3065 bucketUnspentCursor = nil 3066 } 3067 3068 if bucketUnminedCreditsCursor != nil { 3069 bucketUnminedCreditsCursor.Close() 3070 bucketUnminedCreditsCursor = nil 3071 } 3072 }() 3073 3074 type remainingKey struct { 3075 k []byte 3076 unmined bool 3077 } 3078 3079 // Current inputs and their total value. These are closed over by the 3080 // returned input source and reused across multiple calls. 3081 var ( 3082 currentTotal dcrutil.Amount 3083 currentInputs []*wire.TxIn 3084 currentScripts [][]byte 3085 redeemScriptSizes []int 3086 seen = make(map[string]struct{}) // random unspent bucket keys 3087 numUnspent = ns.NestedReadBucket(bucketUnspent).KeyN() 3088 randTries int 3089 remainingKeys []remainingKey 3090 ) 3091 3092 if minConf != 0 { 3093 log.Debugf("Unspent bucket k/v count: %v", numUnspent) 3094 } 3095 3096 skip := func(k, v []byte) bool { 3097 if existsRawUnminedInput(ns, k) != nil { 3098 // Output is spent by an unmined transaction. 3099 // Skip to next unmined credit. 3100 return true 3101 } 3102 var block Block 3103 err := readUnspentBlock(v, &block) 3104 if err != nil || !confirmed(minConf, block.Height, syncHeight) { 3105 return true 3106 } 3107 if _, ok := seen[string(k)]; ok { 3108 // already found by the random search 3109 return true 3110 } 3111 return false 3112 } 3113 3114 f := func(target dcrutil.Amount) (*txauthor.InputDetail, error) { 3115 for currentTotal < target || target == 0 { 3116 var k, v []byte 3117 var err error 3118 if minConf != 0 && target != 0 && randTries < numUnspent/2 { 3119 randTries++ 3120 k, v, err = s.randomUTXO(dbtx, skip) 3121 if k != nil { 3122 seen[string(k)] = struct{}{} 3123 } 3124 } else if remainingKeys == nil { 3125 if randTries > 0 { 3126 log.Debugf("Abandoned random UTXO selection "+ 3127 "attempts after %v tries", randTries) 3128 } 3129 // All remaining keys not discovered by the 3130 // random search (if any was performed) are read 3131 // into memory and shuffled, and then iterated 3132 // over. 3133 remainingKeys = make([]remainingKey, 0) 3134 b := ns.NestedReadBucket(bucketUnspent) 3135 err = b.ForEach(func(k, v []byte) error { 3136 if skip(k, v) { 3137 return nil 3138 } 3139 kcopy := make([]byte, len(k)) 3140 copy(kcopy, k) 3141 remainingKeys = append(remainingKeys, remainingKey{ 3142 k: kcopy, 3143 }) 3144 return nil 3145 }) 3146 if err != nil { 3147 return nil, err 3148 } 3149 if minConf == 0 { 3150 b = ns.NestedReadBucket(bucketUnminedCredits) 3151 err = b.ForEach(func(k, v []byte) error { 3152 if _, ok := seen[string(k)]; ok { 3153 return nil 3154 } 3155 // Skip unmined outputs from unpublished transactions. 3156 if txHash := k[:32]; existsUnpublished(ns, txHash) { 3157 return nil 3158 } 3159 // Skip ticket outputs, as only SSGen can spend these. 3160 opcode := fetchRawUnminedCreditTagOpCode(v) 3161 if opcode == txscript.OP_SSTX { 3162 return nil 3163 } 3164 // Skip outputs that are not mature. 3165 switch opcode { 3166 case txscript.OP_SSGEN, txscript.OP_SSTXCHANGE, txscript.OP_SSRTX, 3167 txscript.OP_TADD, txscript.OP_TGEN: 3168 return nil 3169 } 3170 3171 kcopy := make([]byte, len(k)) 3172 copy(kcopy, k) 3173 remainingKeys = append(remainingKeys, remainingKey{ 3174 k: kcopy, 3175 unmined: true, 3176 }) 3177 return nil 3178 }) 3179 if err != nil { 3180 return nil, err 3181 } 3182 } 3183 shuffle(len(remainingKeys), func(i, j int) { 3184 remainingKeys[i], remainingKeys[j] = remainingKeys[j], remainingKeys[i] 3185 }) 3186 } 3187 3188 var unmined bool 3189 if k == nil { 3190 if len(remainingKeys) == 0 { 3191 // No more UTXOs available. 3192 break 3193 } 3194 next := remainingKeys[0] 3195 remainingKeys = remainingKeys[1:] 3196 k, unmined = next.k, next.unmined 3197 } 3198 if !unmined { 3199 v = ns.NestedReadBucket(bucketUnspent).Get(k) 3200 } else { 3201 v = ns.NestedReadBucket(bucketUnminedCredits).Get(k) 3202 } 3203 3204 tree := wire.TxTreeRegular 3205 var op wire.OutPoint 3206 var amt dcrutil.Amount 3207 var pkScript []byte 3208 3209 if !unmined { 3210 cKey := make([]byte, 72) 3211 copy(cKey[0:32], k[0:32]) // Tx hash 3212 copy(cKey[32:36], v[0:4]) // Block height 3213 copy(cKey[36:68], v[4:36]) // Block hash 3214 copy(cKey[68:72], k[32:36]) // Output index 3215 3216 cVal := existsRawCredit(ns, cKey) 3217 3218 // Check the account first. 3219 pkScript, err = s.fastCreditPkScriptLookup(ns, cKey, nil) 3220 if err != nil { 3221 return nil, err 3222 } 3223 thisAcct, err := s.fetchAccountForPkScript(addrmgrNs, cVal, nil, pkScript) 3224 if err != nil { 3225 return nil, err 3226 } 3227 if account != thisAcct { 3228 continue 3229 } 3230 3231 var spent bool 3232 amt, spent, err = fetchRawCreditAmountSpent(cVal) 3233 if err != nil { 3234 return nil, err 3235 } 3236 3237 // This should never happen since this is already in bucket 3238 // unspent, but let's be careful anyway. 3239 if spent { 3240 continue 3241 } 3242 3243 // Skip zero value outputs. 3244 if amt == 0 { 3245 continue 3246 } 3247 3248 // Skip ticket outputs, as only SSGen can spend these. 3249 opcode := fetchRawCreditTagOpCode(cVal) 3250 if opcode == txscript.OP_SSTX { 3251 continue 3252 } 3253 3254 // Only include this output if it meets the required number of 3255 // confirmations. Coinbase transactions must have have reached 3256 // maturity before their outputs may be spent. 3257 txHeight := extractRawCreditHeight(cKey) 3258 if !confirmed(minConf, txHeight, syncHeight) { 3259 continue 3260 } 3261 3262 // Skip outputs that are not mature. 3263 if opcode == opNonstake && fetchRawCreditIsCoinbase(cVal) { 3264 if !coinbaseMatured(s.chainParams, txHeight, syncHeight) { 3265 continue 3266 } 3267 } 3268 switch opcode { 3269 case txscript.OP_SSGEN, txscript.OP_SSRTX, txscript.OP_TADD, 3270 txscript.OP_TGEN: 3271 if !coinbaseMatured(s.chainParams, txHeight, syncHeight) { 3272 continue 3273 } 3274 } 3275 if opcode == txscript.OP_SSTXCHANGE { 3276 if !ticketChangeMatured(s.chainParams, txHeight, syncHeight) { 3277 continue 3278 } 3279 } 3280 3281 // Determine the txtree for the outpoint by whether or not it's 3282 // using stake tagged outputs. 3283 if opcode != opNonstake { 3284 tree = wire.TxTreeStake 3285 } 3286 3287 err = readCanonicalOutPoint(k, &op) 3288 if err != nil { 3289 return nil, err 3290 } 3291 op.Tree = tree 3292 3293 } else { 3294 // Check the account first. 3295 pkScript, err = s.fastCreditPkScriptLookup(ns, nil, k) 3296 if err != nil { 3297 return nil, err 3298 } 3299 thisAcct, err := s.fetchAccountForPkScript(addrmgrNs, nil, v, pkScript) 3300 if err != nil { 3301 return nil, err 3302 } 3303 if account != thisAcct { 3304 continue 3305 } 3306 3307 amt, err = fetchRawUnminedCreditAmount(v) 3308 if err != nil { 3309 return nil, err 3310 } 3311 3312 // Determine the txtree for the outpoint by whether or not it's 3313 // using stake tagged outputs. 3314 tree = wire.TxTreeRegular 3315 opcode := fetchRawUnminedCreditTagOpCode(v) 3316 if opcode != opNonstake { 3317 tree = wire.TxTreeStake 3318 } 3319 3320 err = readCanonicalOutPoint(k, &op) 3321 if err != nil { 3322 return nil, err 3323 } 3324 op.Tree = tree 3325 } 3326 3327 if ignore != nil && ignore(&op) { 3328 continue 3329 } 3330 3331 input := wire.NewTxIn(&op, int64(amt), nil) 3332 3333 // Unspent credits are currently expected to be either P2PKH or 3334 // P2PK, P2PKH/P2SH nested in a revocation/stakechange/vote output. 3335 // Ignore stake P2SH since it can pay to any script, which the 3336 // wallet may not recognize. 3337 var scriptSize int 3338 scriptClass := stdscript.DetermineScriptType(scriptVersionAssumed, pkScript) 3339 scriptSubClass, _ := txrules.StakeSubScriptType(scriptClass) 3340 switch scriptSubClass { 3341 case stdscript.STPubKeyHashEcdsaSecp256k1: 3342 scriptSize = txsizes.RedeemP2PKHSigScriptSize 3343 case stdscript.STPubKeyEcdsaSecp256k1: 3344 scriptSize = txsizes.RedeemP2PKSigScriptSize 3345 default: 3346 log.Errorf("unexpected script class for credit: %v", scriptClass) 3347 continue 3348 } 3349 3350 currentTotal += amt 3351 currentInputs = append(currentInputs, input) 3352 currentScripts = append(currentScripts, pkScript) 3353 redeemScriptSizes = append(redeemScriptSizes, scriptSize) 3354 } 3355 3356 inputDetail := &txauthor.InputDetail{ 3357 Amount: currentTotal, 3358 Inputs: currentInputs, 3359 Scripts: currentScripts, 3360 RedeemScriptSizes: redeemScriptSizes, 3361 } 3362 3363 return inputDetail, nil 3364 } 3365 3366 return InputSource{source: f} 3367 } 3368 3369 // balanceFullScan does a fullscan of the UTXO set to get the current balance. 3370 // It is less efficient than the other balance functions, but works fine for 3371 // accounts. 3372 func (s *Store) balanceFullScan(dbtx walletdb.ReadTx, minConf int32, syncHeight int32) (map[uint32]*Balances, error) { 3373 ns := dbtx.ReadBucket(wtxmgrBucketKey) 3374 addrmgrNs := dbtx.ReadBucket(waddrmgrBucketKey) 3375 3376 accountBalances := make(map[uint32]*Balances) 3377 c := ns.NestedReadBucket(bucketUnspent).ReadCursor() 3378 for k, v := c.First(); k != nil; k, v = c.Next() { 3379 if existsRawUnminedInput(ns, k) != nil { 3380 // Output is spent by an unmined transaction. 3381 // Skip to next unmined credit. 3382 continue 3383 } 3384 3385 cKey := make([]byte, 72) 3386 copy(cKey[0:32], k[0:32]) // Tx hash 3387 copy(cKey[32:36], v[0:4]) // Block height 3388 copy(cKey[36:68], v[4:36]) // Block hash 3389 copy(cKey[68:72], k[32:36]) // Output index 3390 3391 cVal := existsRawCredit(ns, cKey) 3392 if cVal == nil { 3393 c.Close() 3394 return nil, errors.E(errors.IO, "missing credit for unspent output") 3395 } 3396 3397 // Check the account first. 3398 pkScript, err := s.fastCreditPkScriptLookup(ns, cKey, nil) 3399 if err != nil { 3400 c.Close() 3401 return nil, err 3402 } 3403 thisAcct, err := s.fetchAccountForPkScript(addrmgrNs, cVal, nil, pkScript) 3404 if err != nil { 3405 c.Close() 3406 return nil, err 3407 } 3408 3409 utxoAmt, err := fetchRawCreditAmount(cVal) 3410 if err != nil { 3411 c.Close() 3412 return nil, err 3413 } 3414 3415 height := extractRawCreditHeight(cKey) 3416 opcode := fetchRawCreditTagOpCode(cVal) 3417 3418 ab, ok := accountBalances[thisAcct] 3419 if !ok { 3420 ab = &Balances{ 3421 Account: thisAcct, 3422 } 3423 accountBalances[thisAcct] = ab 3424 } 3425 3426 switch opcode { 3427 case txscript.OP_TGEN: 3428 // Or add another type of balance? 3429 fallthrough 3430 case opNonstake: 3431 isConfirmed := confirmed(minConf, height, syncHeight) 3432 creditFromCoinbase := fetchRawCreditIsCoinbase(cVal) 3433 matureCoinbase := (creditFromCoinbase && 3434 coinbaseMatured(s.chainParams, height, syncHeight)) 3435 3436 if (isConfirmed && !creditFromCoinbase) || 3437 matureCoinbase { 3438 ab.Spendable += utxoAmt 3439 } else if creditFromCoinbase && !matureCoinbase { 3440 ab.ImmatureCoinbaseRewards += utxoAmt 3441 } 3442 3443 ab.Total += utxoAmt 3444 case txscript.OP_SSTX: 3445 ab.VotingAuthority += utxoAmt 3446 case txscript.OP_SSGEN: 3447 fallthrough 3448 case txscript.OP_SSRTX: 3449 if coinbaseMatured(s.chainParams, height, syncHeight) { 3450 ab.Spendable += utxoAmt 3451 } else { 3452 ab.ImmatureStakeGeneration += utxoAmt 3453 } 3454 3455 ab.Total += utxoAmt 3456 case txscript.OP_SSTXCHANGE: 3457 if ticketChangeMatured(s.chainParams, height, syncHeight) { 3458 ab.Spendable += utxoAmt 3459 } 3460 3461 ab.Total += utxoAmt 3462 default: 3463 log.Warnf("Unhandled opcode: %v", opcode) 3464 } 3465 } 3466 3467 c.Close() 3468 3469 // Unconfirmed transaction output handling. 3470 c = ns.NestedReadBucket(bucketUnminedCredits).ReadCursor() 3471 defer c.Close() 3472 for k, v := c.First(); k != nil; k, v = c.Next() { 3473 // Make sure this output was not spent by an unmined transaction. 3474 // If it was, skip this credit. 3475 if existsRawUnminedInput(ns, k) != nil { 3476 continue 3477 } 3478 3479 // Check the account first. 3480 pkScript, err := s.fastCreditPkScriptLookup(ns, nil, k) 3481 if err != nil { 3482 return nil, err 3483 } 3484 thisAcct, err := s.fetchAccountForPkScript(addrmgrNs, nil, v, pkScript) 3485 if err != nil { 3486 return nil, err 3487 } 3488 3489 utxoAmt, err := fetchRawUnminedCreditAmount(v) 3490 if err != nil { 3491 return nil, err 3492 } 3493 3494 ab, ok := accountBalances[thisAcct] 3495 if !ok { 3496 ab = &Balances{ 3497 Account: thisAcct, 3498 } 3499 accountBalances[thisAcct] = ab 3500 } 3501 // Skip ticket outputs, as only SSGen can spend these. 3502 opcode := fetchRawUnminedCreditTagOpCode(v) 3503 3504 txHash := k[:32] 3505 unpublished := existsUnpublished(ns, txHash) 3506 3507 switch opcode { 3508 case opNonstake: 3509 if minConf == 0 && !unpublished { 3510 ab.Spendable += utxoAmt 3511 } else if !fetchRawCreditIsCoinbase(v) { 3512 ab.Unconfirmed += utxoAmt 3513 } 3514 ab.Total += utxoAmt 3515 case txscript.OP_SSTX: 3516 ab.VotingAuthority += utxoAmt 3517 case txscript.OP_SSGEN: 3518 fallthrough 3519 case txscript.OP_SSRTX: 3520 ab.ImmatureStakeGeneration += utxoAmt 3521 ab.Total += utxoAmt 3522 case txscript.OP_SSTXCHANGE: 3523 ab.Total += utxoAmt 3524 continue 3525 case txscript.OP_TGEN: 3526 // Only consider mined tspends for simpler balance 3527 // accounting. 3528 default: 3529 log.Warnf("Unhandled unconfirmed opcode %v: %v", opcode, v) 3530 } 3531 } 3532 3533 // Account for ticket commitments by iterating over the unspent commitments 3534 // index. 3535 it := makeUnspentTicketCommitsIterator(ns) 3536 for it.next() { 3537 if it.err != nil { 3538 return nil, it.err 3539 } 3540 3541 if it.unminedSpent { 3542 // Some unmined tx is redeeming this commitment, so ignore it for 3543 // balance purposes. 3544 continue 3545 } 3546 3547 ab, ok := accountBalances[it.account] 3548 if !ok { 3549 ab = &Balances{ 3550 Account: it.account, 3551 } 3552 accountBalances[it.account] = ab 3553 } 3554 3555 ab.LockedByTickets += it.amount 3556 ab.Total += it.amount 3557 } 3558 it.close() 3559 3560 return accountBalances, nil 3561 } 3562 3563 // Balances is an convenience type. 3564 type Balances = struct { 3565 Account uint32 3566 ImmatureCoinbaseRewards dcrutil.Amount 3567 ImmatureStakeGeneration dcrutil.Amount 3568 LockedByTickets dcrutil.Amount 3569 Spendable dcrutil.Amount 3570 Total dcrutil.Amount 3571 VotingAuthority dcrutil.Amount 3572 Unconfirmed dcrutil.Amount 3573 } 3574 3575 // AccountBalance returns a Balances struct for some given account at 3576 // syncHeight block height with all UTXOS that have minConf manyn confirms. 3577 func (s *Store) AccountBalance(dbtx walletdb.ReadTx, minConf int32, account uint32) (Balances, error) { 3578 balances, err := s.AccountBalances(dbtx, minConf) 3579 if err != nil { 3580 return Balances{}, err 3581 } 3582 3583 balance, ok := balances[account] 3584 if !ok { 3585 // No balance for the account was found so must be zero. 3586 return Balances{ 3587 Account: account, 3588 }, nil 3589 } 3590 3591 return *balance, nil 3592 } 3593 3594 // AccountBalances returns a map of all account balances at syncHeight block 3595 // height with all UTXOs that have minConf many confirms. 3596 func (s *Store) AccountBalances(dbtx walletdb.ReadTx, minConf int32) (map[uint32]*Balances, error) { 3597 _, syncHeight := s.MainChainTip(dbtx) 3598 return s.balanceFullScan(dbtx, minConf, syncHeight) 3599 }