decred.org/dcrwallet/v3@v3.1.0/wallet/udb/txquery.go (about) 1 // Copyright (c) 2015 The btcsuite developers 2 // Copyright (c) 2015-2017 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 "context" 11 12 "decred.org/dcrwallet/v3/errors" 13 "decred.org/dcrwallet/v3/wallet/walletdb" 14 "github.com/decred/dcrd/blockchain/stake/v5" 15 "github.com/decred/dcrd/chaincfg/chainhash" 16 "github.com/decred/dcrd/dcrutil/v4" 17 "github.com/decred/dcrd/wire" 18 ) 19 20 // CreditRecord contains metadata regarding a transaction credit for a known 21 // transaction. Further details may be looked up by indexing a wire.MsgTx.TxOut 22 // with the Index field. 23 type CreditRecord struct { 24 Index uint32 25 Amount dcrutil.Amount 26 Spent bool 27 Change bool 28 OpCode uint8 29 IsCoinbase bool 30 HasExpiry bool 31 } 32 33 // DebitRecord contains metadata regarding a transaction debit for a known 34 // transaction. Further details may be looked up by indexing a wire.MsgTx.TxIn 35 // with the Index field. 36 type DebitRecord struct { 37 Amount dcrutil.Amount 38 Index uint32 39 } 40 41 // TxDetails is intended to provide callers with access to rich details 42 // regarding a relevant transaction and which inputs and outputs are credit or 43 // debits. 44 type TxDetails struct { 45 TxRecord 46 Block BlockMeta 47 Credits []CreditRecord 48 Debits []DebitRecord 49 } 50 51 // Height returns the height of a transaction according to the BlockMeta. 52 func (t *TxDetails) Height() int32 { 53 return t.Block.Block.Height 54 } 55 56 // minedTxDetails fetches the TxDetails for the mined transaction with hash 57 // txHash and the passed tx record key and value. 58 func (s *Store) minedTxDetails(ns walletdb.ReadBucket, txHash *chainhash.Hash, recKey, recVal []byte) (*TxDetails, error) { 59 var details TxDetails 60 61 // Parse transaction record k/v, lookup the full block record for the 62 // block time, and read all matching credits, debits. 63 err := readRawTxRecord(txHash, recVal, &details.TxRecord) 64 if err != nil { 65 return nil, err 66 } 67 err = readRawTxRecordBlock(recKey, &details.Block.Block) 68 if err != nil { 69 return nil, err 70 } 71 details.Block.Time, err = fetchBlockTime(ns, details.Block.Height) 72 if err != nil { 73 return nil, err 74 } 75 76 credIter := makeReadCreditIterator(ns, recKey, DBVersion) 77 for credIter.next() { 78 if int(credIter.elem.Index) >= len(details.MsgTx.TxOut) { 79 credIter.close() 80 return nil, errors.E(errors.IO, "saved credit index exceeds number of outputs") 81 } 82 83 // The credit iterator does not record whether this credit was 84 // spent by an unmined transaction, so check that here. 85 if !credIter.elem.Spent { 86 k := canonicalOutPoint(txHash, credIter.elem.Index) 87 spent := existsRawUnminedInput(ns, k) != nil 88 credIter.elem.Spent = spent 89 } 90 details.Credits = append(details.Credits, credIter.elem) 91 } 92 credIter.close() 93 if credIter.err != nil { 94 return nil, credIter.err 95 } 96 97 debIter := makeReadDebitIterator(ns, recKey) 98 defer debIter.close() 99 for debIter.next() { 100 if int(debIter.elem.Index) >= len(details.MsgTx.TxIn) { 101 return nil, errors.E(errors.IO, "saved debit index exceeds number of inputs") 102 } 103 104 details.Debits = append(details.Debits, debIter.elem) 105 } 106 return &details, debIter.err 107 } 108 109 // unminedTxDetails fetches the TxDetails for the unmined transaction with the 110 // hash txHash and the passed unmined record value. 111 func (s *Store) unminedTxDetails(ns walletdb.ReadBucket, txHash *chainhash.Hash, v []byte) (*TxDetails, error) { 112 details := TxDetails{ 113 Block: BlockMeta{Block: Block{Height: -1}}, 114 } 115 err := readRawTxRecord(txHash, v, &details.TxRecord) 116 if err != nil { 117 return nil, err 118 } 119 120 it := makeReadUnminedCreditIterator(ns, txHash, DBVersion) 121 defer it.close() 122 for it.next() { 123 if int(it.elem.Index) >= len(details.MsgTx.TxOut) { 124 return nil, errors.E(errors.IO, errors.Errorf("credit output index %d does not exist for tx %v", it.elem.Index, txHash)) 125 } 126 127 // Set the Spent field since this is not done by the iterator. 128 it.elem.Spent = existsRawUnminedInput(ns, it.ck) != nil 129 details.Credits = append(details.Credits, it.elem) 130 } 131 if it.err != nil { 132 return nil, it.err 133 } 134 135 // Debit records are not saved for unmined transactions. Instead, they 136 // must be looked up for each transaction input manually. There are two 137 // kinds of previous credits that may be debited by an unmined 138 // transaction: mined unspent outputs (which remain marked unspent even 139 // when spent by an unmined transaction), and credits from other unmined 140 // transactions. Both situations must be considered. 141 for i, output := range details.MsgTx.TxIn { 142 opKey := canonicalOutPoint(&output.PreviousOutPoint.Hash, 143 output.PreviousOutPoint.Index) 144 credKey := existsRawUnspent(ns, opKey) 145 if credKey != nil { 146 v := existsRawCredit(ns, credKey) 147 amount, err := fetchRawCreditAmount(v) 148 if err != nil { 149 return nil, err 150 } 151 152 details.Debits = append(details.Debits, DebitRecord{ 153 Amount: amount, 154 Index: uint32(i), 155 }) 156 continue 157 } 158 159 v := existsRawUnminedCredit(ns, opKey) 160 if v == nil { 161 continue 162 } 163 164 amount, err := fetchRawCreditAmount(v) 165 if err != nil { 166 return nil, err 167 } 168 details.Debits = append(details.Debits, DebitRecord{ 169 Amount: amount, 170 Index: uint32(i), 171 }) 172 } 173 174 return &details, nil 175 } 176 177 // TxDetails looks up all recorded details regarding a transaction with some 178 // hash. In case of a hash collision, the most recent transaction with a 179 // matching hash is returned. 180 func (s *Store) TxDetails(ns walletdb.ReadBucket, txHash *chainhash.Hash) (*TxDetails, error) { 181 // First, check whether there exists an unmined transaction with this 182 // hash. Use it if found. 183 v := existsRawUnmined(ns, txHash[:]) 184 if v != nil { 185 return s.unminedTxDetails(ns, txHash, v) 186 } 187 188 // Otherwise, if there exists a mined transaction with this matching 189 // hash, skip over to the newest and begin fetching all details. 190 k, v := latestTxRecord(ns, txHash[:]) 191 if v == nil { 192 return nil, errors.E(errors.NotExist) 193 } 194 return s.minedTxDetails(ns, txHash, k, v) 195 } 196 197 // TicketDetails is intended to provide callers with access to rich details 198 // regarding a relevant transaction and which inputs and outputs are credit or 199 // debits. 200 type TicketDetails struct { 201 Ticket *TxDetails 202 Spender *TxDetails 203 } 204 205 // TicketDetails looks up all recorded details regarding a ticket with some 206 // hash. 207 // 208 // Not finding a ticket with this hash is not an error. In this case, 209 // a nil TicketDetiails is returned. 210 func (s *Store) TicketDetails(ns walletdb.ReadBucket, txDetails *TxDetails) (*TicketDetails, error) { 211 var ticketDetails = &TicketDetails{} 212 if !stake.IsSStx(&txDetails.MsgTx) { 213 return nil, nil 214 } 215 ticketDetails.Ticket = txDetails 216 var spenderHash = chainhash.Hash{} 217 // Check if the ticket is spent or not. Look up the credit for output 0 218 // and check if either a debit is recorded or the output is spent by an 219 // unmined transaction. 220 _, credVal := existsCredit(ns, &txDetails.Hash, 0, &txDetails.Block.Block) 221 if credVal != nil { 222 if extractRawCreditIsSpent(credVal) { 223 debKey := extractRawCreditSpenderDebitKey(credVal) 224 debHash := extractRawDebitHash(debKey) 225 copy(spenderHash[:], debHash) 226 } 227 } else { 228 opKey := canonicalOutPoint(&txDetails.Hash, 0) 229 spenderVal := existsRawUnminedInput(ns, opKey) 230 if spenderVal != nil { 231 copy(spenderHash[:], spenderVal) 232 } 233 } 234 spenderDetails, err := s.TxDetails(ns, &spenderHash) 235 if (err != nil) && (!errors.Is(err, errors.NotExist)) { 236 return nil, err 237 } 238 ticketDetails.Spender = spenderDetails 239 return ticketDetails, nil 240 } 241 242 // parseTx deserializes a transaction into a MsgTx using the readRawTxRecord 243 // method. 244 func (s *Store) parseTx(txHash chainhash.Hash, v []byte) (*wire.MsgTx, error) { 245 details := TxDetails{ 246 Block: BlockMeta{Block: Block{Height: -1}}, 247 } 248 err := readRawTxRecord(&txHash, v, &details.TxRecord) 249 if err != nil { 250 return nil, err 251 } 252 253 return &details.MsgTx, nil 254 } 255 256 // Tx looks up all the stored wire.MsgTx for a transaction with some 257 // hash. In case of a hash collision, the most recent transaction with a 258 // matching hash is returned. 259 func (s *Store) Tx(ns walletdb.ReadBucket, txHash *chainhash.Hash) (*wire.MsgTx, error) { 260 // First, check whether there exists an unmined transaction with this 261 // hash. Use it if found. 262 v := existsRawUnmined(ns, txHash[:]) 263 if v != nil { 264 return s.parseTx(*txHash, v) 265 } 266 267 // Otherwise, if there exists a mined transaction with this matching 268 // hash, skip over to the newest and begin fetching the msgTx. 269 _, v = latestTxRecord(ns, txHash[:]) 270 if v == nil { 271 return nil, errors.E(errors.NotExist, 272 errors.Errorf("tx %s not found", txHash.String())) 273 } 274 return s.parseTx(*txHash, v) 275 } 276 277 // ExistsTx checks to see if a transaction exists in the database. 278 func (s *Store) ExistsTx(ns walletdb.ReadBucket, txHash *chainhash.Hash) bool { 279 // First, check whether there exists an unmined transaction with this 280 // hash. Use it if found. 281 v := existsRawUnmined(ns, txHash[:]) 282 if v != nil { 283 return true 284 } 285 286 // Otherwise, if there exists a mined transaction with this matching 287 // hash, skip over to the newest and begin fetching the msgTx. 288 _, v = latestTxRecord(ns, txHash[:]) 289 return v != nil 290 } 291 292 // ExistsUTXO checks to see if op refers to an unspent transaction output or a 293 // credit spent by an unmined transaction. This check is sufficient to 294 // determine whether a transaction input is relevant to the wallet by spending a 295 // UTXO or conflicting with another mempool transaction that double spends the 296 // output. 297 func (s *Store) ExistsUTXO(dbtx walletdb.ReadTx, op *wire.OutPoint) bool { 298 ns := dbtx.ReadBucket(wtxmgrBucketKey) 299 k, v := existsUnspent(ns, op) 300 if v != nil { 301 return true 302 } 303 return existsRawUnminedCredit(ns, k) != nil 304 } 305 306 // UniqueTxDetails looks up all recorded details for a transaction recorded 307 // mined in some particular block, or an unmined transaction if block is nil. 308 // 309 // Not finding a transaction with this hash from this block is not an error. In 310 // this case, a nil TxDetails is returned. 311 func (s *Store) UniqueTxDetails(ns walletdb.ReadBucket, txHash *chainhash.Hash, 312 block *Block) (*TxDetails, error) { 313 314 if block == nil { 315 v := existsRawUnmined(ns, txHash[:]) 316 if v == nil { 317 return nil, nil 318 } 319 return s.unminedTxDetails(ns, txHash, v) 320 } 321 322 k, v := existsTxRecord(ns, txHash, block) 323 if v == nil { 324 return nil, nil 325 } 326 return s.minedTxDetails(ns, txHash, k, v) 327 } 328 329 // TxBlockHeight returns the block height of a mined transaction, or -1 for any 330 // unmined transactions. 331 func (s *Store) TxBlockHeight(dbtx walletdb.ReadTx, txHash *chainhash.Hash) (int32, error) { 332 ns := dbtx.ReadBucket(wtxmgrBucketKey) 333 v := existsRawUnmined(ns, txHash[:]) 334 if v != nil { 335 return -1, nil 336 } 337 k, _ := latestTxRecord(ns, txHash[:]) 338 if k == nil { 339 return 0, errors.E(errors.NotExist, errors.Errorf("no transaction %v", txHash)) 340 } 341 var height int32 342 err := readRawTxRecordBlockHeight(k, &height) 343 return height, err 344 } 345 346 // rangeUnminedTransactions executes the function f with TxDetails for every 347 // unmined transaction. f is not executed if no unmined transactions exist. 348 // Error returns from f (if any) are propigated to the caller. Returns true 349 // (signaling breaking out of a RangeTransactions) iff f executes and returns 350 // true. 351 func (s *Store) rangeUnminedTransactions(ctx context.Context, ns walletdb.ReadBucket, f func([]TxDetails) (bool, error)) (bool, error) { 352 var details []TxDetails 353 err := ns.NestedReadBucket(bucketUnmined).ForEach(func(k, v []byte) error { 354 if ctx.Err() != nil { 355 return ctx.Err() 356 } 357 if len(k) < 32 { 358 return errors.E(errors.IO, errors.Errorf("bad unmined tx key len %d", len(k))) 359 } 360 361 var txHash chainhash.Hash 362 copy(txHash[:], k) 363 detail, err := s.unminedTxDetails(ns, &txHash, v) 364 if err != nil { 365 return err 366 } 367 368 // Because the key was created while foreach-ing over the 369 // bucket, it should be impossible for unminedTxDetails to ever 370 // successfully return a nil details struct. 371 details = append(details, *detail) 372 return nil 373 }) 374 if err == nil && len(details) > 0 { 375 return f(details) 376 } 377 return false, err 378 } 379 380 // rangeBlockTransactions executes the function f with TxDetails for every block 381 // between heights begin and end (reverse order when end > begin) until f 382 // returns true, or the transactions from block is processed. Returns true iff 383 // f executes and returns true. 384 func (s *Store) rangeBlockTransactions(ctx context.Context, ns walletdb.ReadBucket, begin, end int32, 385 f func([]TxDetails) (bool, error)) (bool, error) { 386 387 // Mempool height is considered a high bound. 388 if begin < 0 { 389 begin = int32(^uint32(0) >> 1) 390 } 391 if end < 0 { 392 end = int32(^uint32(0) >> 1) 393 } 394 395 var blockIter blockIterator 396 var advance func(*blockIterator) bool 397 if begin < end { 398 // Iterate in forwards order 399 blockIter = makeReadBlockIterator(ns, begin) 400 defer blockIter.close() 401 advance = func(it *blockIterator) bool { 402 if !it.next() { 403 return false 404 } 405 return it.elem.Height <= end 406 } 407 } else { 408 // Iterate in backwards order, from begin -> end. 409 blockIter = makeReadBlockIterator(ns, begin) 410 defer blockIter.close() 411 advance = func(it *blockIterator) bool { 412 if !it.prev() { 413 return false 414 } 415 return end <= it.elem.Height 416 } 417 } 418 419 var details []TxDetails 420 for advance(&blockIter) { 421 if ctx.Err() != nil { 422 return false, ctx.Err() 423 } 424 425 block := &blockIter.elem 426 427 if cap(details) < len(block.transactions) { 428 details = make([]TxDetails, 0, len(block.transactions)) 429 } else { 430 details = details[:0] 431 } 432 433 for _, txHash := range block.transactions { 434 k := keyTxRecord(&txHash, &block.Block) 435 v := existsRawTxRecord(ns, k) 436 if v == nil { 437 return false, errors.E(errors.IO, errors.Errorf("missing transaction %v for block %v", txHash, block.Height)) 438 } 439 detail := TxDetails{ 440 Block: BlockMeta{ 441 Block: block.Block, 442 Time: block.Time, 443 }, 444 } 445 err := readRawTxRecord(&txHash, v, &detail.TxRecord) 446 if err != nil { 447 return false, err 448 } 449 450 credIter := makeReadCreditIterator(ns, k, DBVersion) 451 for credIter.next() { 452 if int(credIter.elem.Index) >= len(detail.MsgTx.TxOut) { 453 credIter.close() 454 return false, errors.E(errors.IO, "saved credit index exceeds number of outputs") 455 } 456 457 // The credit iterator does not record whether 458 // this credit was spent by an unmined 459 // transaction, so check that here. 460 if !credIter.elem.Spent { 461 k := canonicalOutPoint(&txHash, credIter.elem.Index) 462 spent := existsRawUnminedInput(ns, k) != nil 463 credIter.elem.Spent = spent 464 } 465 detail.Credits = append(detail.Credits, credIter.elem) 466 } 467 credIter.close() 468 if credIter.err != nil { 469 return false, credIter.err 470 } 471 472 debIter := makeReadDebitIterator(ns, k) 473 defer debIter.close() 474 for debIter.next() { 475 if int(debIter.elem.Index) >= len(detail.MsgTx.TxIn) { 476 return false, errors.E(errors.IO, "saved debit index exceeds number of inputs") 477 } 478 479 detail.Debits = append(detail.Debits, debIter.elem) 480 } 481 if debIter.err != nil { 482 return false, debIter.err 483 } 484 485 details = append(details, detail) 486 } 487 488 // Decred: Block records are saved even when no transactions are 489 // included. This is used to save the votebits from every 490 // block. This differs from btcwallet where every block must 491 // have one transaction. Since f may only be called when 492 // len(details) > 0, this must be explicitly tested. 493 if len(details) == 0 { 494 continue 495 } 496 brk, err := f(details) 497 if err != nil || brk { 498 return brk, err 499 } 500 } 501 return false, blockIter.err 502 } 503 504 // RangeTransactions runs the function f on all transaction details between 505 // blocks on the best chain over the height range [begin,end]. The special 506 // height -1 may be used to also include unmined transactions. If the end 507 // height comes before the begin height, blocks are iterated in reverse order 508 // and unmined transactions (if any) are processed first. 509 // 510 // The function f may return an error which, if non-nil, is propagated to the 511 // caller. Additionally, a boolean return value allows exiting the function 512 // early without reading any additional transactions early when true. 513 // 514 // All calls to f are guaranteed to be passed a slice with more than zero 515 // elements. The slice may be reused for multiple blocks, so it is not safe to 516 // use it after the loop iteration it was acquired. 517 func (s *Store) RangeTransactions(ctx context.Context, ns walletdb.ReadBucket, begin, end int32, 518 f func([]TxDetails) (bool, error)) error { 519 520 var addedUnmined bool 521 if begin < 0 { 522 brk, err := s.rangeUnminedTransactions(ctx, ns, f) 523 if err != nil || brk { 524 return err 525 } 526 addedUnmined = true 527 } 528 529 brk, err := s.rangeBlockTransactions(ctx, ns, begin, end, f) 530 if err == nil && !brk && !addedUnmined && end < 0 { 531 _, err = s.rangeUnminedTransactions(ctx, ns, f) 532 } 533 return err 534 } 535 536 // PreviousPkScripts returns a slice of previous output scripts for each credit 537 // output this transaction record debits from. 538 func (s *Store) PreviousPkScripts(ns walletdb.ReadBucket, rec *TxRecord, block *Block) ([][]byte, error) { 539 var pkScripts [][]byte 540 541 if block == nil { 542 for _, input := range rec.MsgTx.TxIn { 543 prevOut := &input.PreviousOutPoint 544 545 // Input may spend a previous unmined output, a 546 // mined output (which would still be marked 547 // unspent), or neither. 548 549 v := existsRawUnmined(ns, prevOut.Hash[:]) 550 if v != nil { 551 // Ensure a credit exists for this 552 // unmined transaction before including 553 // the output script. 554 k := canonicalOutPoint(&prevOut.Hash, prevOut.Index) 555 vUC := existsRawUnminedCredit(ns, k) 556 if vUC == nil { 557 continue 558 } 559 560 // If we encounter an error here, it likely means 561 // we have a legacy outpoint. Ignore the error and 562 // just let the scrPos be 0, which will trigger 563 // whole transaction deserialization to retrieve 564 // the script. 565 scrPos := fetchRawUnminedCreditScriptOffset(vUC) 566 scrLen := fetchRawUnminedCreditScriptLength(vUC) 567 568 pkScript, err := fetchRawTxRecordPkScript( 569 prevOut.Hash[:], v, prevOut.Index, scrPos, scrLen) 570 if err != nil { 571 return nil, err 572 } 573 pkScripts = append(pkScripts, pkScript) 574 continue 575 } 576 577 _, credKey := existsUnspent(ns, prevOut) 578 if credKey != nil { 579 credVal := existsRawCredit(ns, credKey) 580 if credVal == nil { 581 return nil, errors.E(errors.IO, errors.Errorf("missing credit value for key %x", credKey)) 582 } 583 584 // Legacy outputs in the credit bucket may be of the 585 // wrong size. 586 scrPos := fetchRawCreditScriptOffset(credVal) 587 scrLen := fetchRawCreditScriptLength(credVal) 588 589 k := extractRawCreditTxRecordKey(credKey) 590 v = existsRawTxRecord(ns, k) 591 pkScript, err := fetchRawTxRecordPkScript(k, v, 592 prevOut.Index, scrPos, scrLen) 593 if err != nil { 594 return nil, err 595 } 596 pkScripts = append(pkScripts, pkScript) 597 } 598 } 599 } 600 601 recKey := keyTxRecord(&rec.Hash, block) 602 it := makeReadDebitIterator(ns, recKey) 603 for it.next() { 604 credKey := extractRawDebitCreditKey(it.cv) 605 index := extractRawCreditIndex(credKey) 606 607 credVal := existsRawCredit(ns, credKey) 608 if credVal == nil { 609 return nil, errors.E(errors.IO, errors.Errorf("missing credit val for key %x", credKey)) 610 } 611 612 // Legacy credit output values may be of the wrong 613 // size. 614 scrPos := fetchRawCreditScriptOffset(credVal) 615 scrLen := fetchRawCreditScriptLength(credVal) 616 617 k := extractRawCreditTxRecordKey(credKey) 618 v := existsRawTxRecord(ns, k) 619 pkScript, err := fetchRawTxRecordPkScript(k, v, index, 620 scrPos, scrLen) 621 if err != nil { 622 return nil, err 623 } 624 pkScripts = append(pkScripts, pkScript) 625 } 626 if it.err != nil { 627 return nil, it.err 628 } 629 630 return pkScripts, nil 631 } 632 633 // Spender queries for the transaction and input index which spends a Credit. 634 // If the output is not a Credit, an error with code ErrInput is returned. If 635 // the output is unspent, the ErrNoExist code is used. 636 func (s *Store) Spender(dbtx walletdb.ReadTx, out *wire.OutPoint) (*wire.MsgTx, uint32, error) { 637 ns := dbtx.ReadBucket(wtxmgrBucketKey) 638 639 var spender wire.MsgTx 640 var spenderHash chainhash.Hash 641 var spenderIndex uint32 642 643 // Check mined txs 644 k, v := latestTxRecord(ns, out.Hash[:]) 645 if v != nil { 646 var block Block 647 err := readRawTxRecordBlock(k, &block) 648 if err != nil { 649 return nil, 0, err 650 } 651 k = keyCredit(&out.Hash, out.Index, &block) 652 v = existsRawCredit(ns, k) 653 if v == nil { 654 return nil, 0, errors.E(errors.Invalid, "output is not a credit") 655 } 656 if extractRawCreditIsSpent(v) { 657 // Credit exists and is spent by a mined transaction. 658 k = extractRawCreditSpenderDebitKey(v) 659 copy(spenderHash[:], extractRawDebitHash(k)) 660 spenderIndex = extractRawDebitInputIndex(k) 661 k = extractRawDebitTxRecordKey(k) 662 v = existsRawTxRecord(ns, k) 663 err = readRawTxRecordMsgTx(&spenderHash, v, &spender) 664 if err != nil { 665 return nil, 0, err 666 } 667 return &spender, spenderIndex, nil 668 } 669 // Credit is not spent by a mined transaction, but may still be spent by 670 // an unmined one. Check whether it is spent by an unmined tx, and 671 // record the spender hash if spent. 672 k = canonicalOutPoint(&out.Hash, out.Index) 673 v = existsRawUnminedInput(ns, k) 674 if v == nil { 675 return nil, 0, errors.E(errors.NotExist, "credit is unspent") 676 } 677 readRawUnminedInputSpenderHash(v, &spenderHash) 678 } 679 680 // If a spender exists at this point, it must be an unmined transaction. 681 // The spender hash will not yet be known if the credit is also unmined, or 682 // if there is no credit. 683 if spenderHash == (chainhash.Hash{}) { 684 k = canonicalOutPoint(&out.Hash, out.Index) 685 v = existsRawUnminedCredit(ns, k) 686 if v == nil { 687 return nil, 0, errors.E(errors.Invalid, "output is not a credit") 688 } 689 v = existsRawUnminedInput(ns, k) 690 if v == nil { 691 return nil, 0, errors.E(errors.NotExist, "credit is unspent") 692 } 693 readRawUnminedInputSpenderHash(v, &spenderHash) 694 } 695 696 // Credit is spent by an unmined transaction. Index is unknown so the 697 // spending tx must be searched for a matching previous outpoint. 698 v = existsRawUnmined(ns, spenderHash[:]) 699 if v == nil { 700 return nil, 0, errors.E(errors.NotExist, "missing unmined spending tx") 701 } 702 err := spender.Deserialize(bytes.NewReader(extractRawUnminedTx(v))) 703 if err != nil { 704 return nil, 0, errors.E(errors.Bug, err) 705 } 706 found := false 707 for i, in := range spender.TxIn { 708 // Compare outpoints without comparing tree. 709 if out.Hash == in.PreviousOutPoint.Hash && out.Index == in.PreviousOutPoint.Index { 710 spenderIndex = uint32(i) 711 found = true 712 break 713 } 714 } 715 if !found { 716 return nil, 0, errors.E(errors.NotExist, "recorded spending tx does not spend credit") 717 } 718 return &spender, spenderIndex, nil 719 } 720 721 // RangeBlocks execute function `f` for all blocks within the given range of 722 // blocks in the main chain. 723 func (s *Store) RangeBlocks(ns walletdb.ReadBucket, begin, end int32, 724 f func(*Block) (bool, error)) error { 725 726 // Same convention as rangeTransactions: -1 means the full range. 727 if begin < 0 { 728 begin = int32(^uint32(0) >> 1) 729 } 730 if end < 0 { 731 end = int32(^uint32(0) >> 1) 732 } 733 734 var blockIter blockIterator 735 var advance func(*blockIterator) bool 736 737 if begin < end { 738 // Iterate in forwards order 739 blockIter = makeReadBlockIterator(ns, begin) 740 defer blockIter.close() 741 advance = func(it *blockIterator) bool { 742 if !it.next() { 743 return false 744 } 745 return it.elem.Height <= end 746 } 747 } else { 748 // Iterate in backwards order, from begin -> end. 749 blockIter = makeReadBlockIterator(ns, begin) 750 defer blockIter.close() 751 advance = func(it *blockIterator) bool { 752 if !it.prev() { 753 return false 754 } 755 return end <= it.elem.Height 756 } 757 } 758 759 for advance(&blockIter) { 760 block := &blockIter.elem 761 762 brk, err := f(&block.Block) 763 if err != nil || brk { 764 return err 765 } 766 } 767 768 return nil 769 }