// findPreviousCredits searches for all unspent credits that make up the inputs // for tx. This lookup is very expensive and should be avoided at all costs. func (s *Store) findPreviousCredits(tx *btcutil.Tx) ([]*Credit, error) { unfound := make(map[btcwire.OutPoint]struct{}, len(tx.MsgTx().TxIn)) for _, txIn := range tx.MsgTx().TxIn { unfound[txIn.PreviousOutpoint] = struct{}{} } spent := make([]*Credit, 0, len(unfound)) done: for blockHeight := range s.unspent { b, err := s.lookupBlock(blockHeight) if err != nil { return nil, err } for blockIndex, txIdx := range b.unspent { if uint32(len(b.txs)) <= txIdx { return nil, MissingBlockTxError{ BlockIndex: blockIndex, BlockHeight: blockHeight, } } r := b.txs[txIdx] op := btcwire.OutPoint{Hash: *r.Tx().Sha()} for i, cred := range r.credits { if cred == nil || cred.spentBy != nil { continue } op.Index = uint32(i) if _, ok := unfound[op]; ok { key := BlockTxKey{blockIndex, b.Height} t := &TxRecord{key, r, s} c := &Credit{t, op.Index} spent = append(spent, c) delete(unfound, op) if len(unfound) == 0 { break done } } } } } return spent, nil }
func (u *unconfirmedStore) ReadFrom(r io.Reader) (int64, error) { var buf [4]byte uint32Bytes := buf[:4] // Read length (as a uint32) of transaction record key/value pairs, // followed by each transaction record. n, err := io.ReadFull(r, uint32Bytes) n64 := int64(n) if err != nil { return n64, err } txCount := byteOrder.Uint32(uint32Bytes) for i := uint32(0); i < txCount; i++ { t := &txRecord{} tmpn64, err := t.ReadFrom(r) n64 += tmpn64 if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } u.txs[*t.tx.Sha()] = t } // Read length (as a uint32) of key/value pairs in the // spentBlockOutPoints and spentBlockOutPointKeys maps, followed by the // outpoint, the block transaction lookup key, and the transaction hash // of the spending transaction record. n, err = io.ReadFull(r, uint32Bytes) n64 += int64(n) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } spentBlockOutPointCount := byteOrder.Uint32(uint32Bytes) for i := uint32(0); i < spentBlockOutPointCount; i++ { // Read outpoint hash and index (uint32). op := btcwire.OutPoint{} n, err := io.ReadFull(r, op.Hash[:]) n64 += int64(n) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } n, err = io.ReadFull(r, uint32Bytes) n64 += int64(n) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } op.Index = byteOrder.Uint32(uint32Bytes) // Read block transaction lookup key, and create the full block // output key from it and the previously-read outpoint index. opKey := BlockOutputKey{OutputIndex: op.Index} tmpn64, err := opKey.BlockTxKey.ReadFrom(r) n64 += tmpn64 if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } // Read transaction record hash and check that it was previously // read into the txs map. Use full record as the map value. var txHash btcwire.ShaHash n, err = io.ReadFull(r, txHash[:]) n64 += int64(n) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } t, ok := u.txs[txHash] if !ok { return n64, fmt.Errorf("missing unconfirmed "+ "transaction record for transaction %v", txHash) } u.spentBlockOutPoints[opKey] = t u.spentBlockOutPointKeys[op] = opKey } // Read length (as a uint32) of key/value pairs in the spentUnconfirmed // map, followed by the outpoint and hash of the transaction record. // Use this hash as the lookup key for the full transaction record // previously read into the txs map. n, err = io.ReadFull(r, uint32Bytes) n64 += int64(n) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } spentUnconfirmedCount := byteOrder.Uint32(uint32Bytes) for i := uint32(0); i < spentUnconfirmedCount; i++ { // Read outpoint hash and index (uint32). op := btcwire.OutPoint{} n, err := io.ReadFull(r, op.Hash[:]) n64 += int64(n) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } n, err = io.ReadFull(r, uint32Bytes) n64 += int64(n) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } op.Index = byteOrder.Uint32(uint32Bytes) // Read transaction record hash and check that it was previously // read into the txs map. Use full record as the map value. var txHash btcwire.ShaHash n, err = io.ReadFull(r, txHash[:]) n64 += int64(n) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n64, err } t, ok := u.txs[txHash] if !ok { return n64, fmt.Errorf("missing unconfirmed "+ "transaction record for transaction %v", txHash) } u.spentUnconfirmed[op] = t } // Recreate the previousOutpoints map. For each transaction record // saved in the txs map, map each previous outpoint to the record // itself. for _, t := range u.txs { for _, input := range t.tx.MsgTx().TxIn { u.previousOutpoints[input.PreviousOutpoint] = t } } return n64, nil }
func (s *Store) moveMinedTx(r *txRecord, block *Block) error { log.Infof("Marking unconfirmed transaction %v mined in block %d", r.tx.Sha(), block.Height) delete(s.unconfirmed.txs, *r.Tx().Sha()) // Find collection and insert records. Error out if there are records // saved for this block and index. key := BlockTxKey{r.Tx().Index(), block.Height} b := s.blockCollectionForInserts(block) txIndex := uint32(len(b.txs)) b.txIndexes[key.BlockIndex] = txIndex b.txs = append(b.txs, r) for _, input := range r.Tx().MsgTx().TxIn { delete(s.unconfirmed.previousOutpoints, input.PreviousOutpoint) // For all mined transactions with credits spent by this // transaction, remove them from the spentBlockOutPoints map // (signifying that there is no longer an unconfirmed // transaction which spending that credit), and update the // credit's spent by tracking with the block key of the // newly-mined transaction. prev, ok := s.unconfirmed.spentBlockOutPointKeys[input.PreviousOutpoint] if !ok { continue } delete(s.unconfirmed.spentBlockOutPointKeys, input.PreviousOutpoint) delete(s.unconfirmed.spentBlockOutPoints, prev) rr, err := s.lookupBlockTx(prev.BlockTxKey) if err != nil { return err } rr.credits[prev.OutputIndex].spentBy = &key // debits should already be non-nil r.debits.spends = append(r.debits.spends, prev) } // For each credit in r, if the credit is spent by another unconfirmed // transaction, move the spending transaction from spentUnconfirmed // (which signifies a transaction spending another unconfirmed tx) to // spentBlockTxs (which signifies an unconfirmed transaction spending a // confirmed tx) and modify the mined transaction's record to refer to // the credit being spent by an unconfirmed transaction. // // If the credit is not spent, modify the store's unspent bookkeeping // maps to include the credit and increment the amount deltas by the // credit's value. op := btcwire.OutPoint{Hash: *r.Tx().Sha()} for i, credit := range r.credits { if credit == nil { continue } op.Index = uint32(i) outputKey := BlockOutputKey{key, op.Index} if rr, ok := s.unconfirmed.spentUnconfirmed[op]; ok { delete(s.unconfirmed.spentUnconfirmed, op) s.unconfirmed.spentBlockOutPointKeys[op] = outputKey s.unconfirmed.spentBlockOutPoints[outputKey] = rr credit.spentBy = &BlockTxKey{BlockHeight: -1} } else if credit.spentBy == nil { // Mark outpoint unspent. s.unspent[op] = key // Increment spendable amount delta as a result of // moving this credit to this block. value := r.Tx().MsgTx().TxOut[i].Value b.amountDeltas.Spendable += btcutil.Amount(value) } } // If this moved transaction debits from any previous credits, decrement // the amount deltas by the total input amount. Because this // transaction was moved from the unconfirmed transaction set, this can // never be a coinbase. if r.debits != nil { b.amountDeltas.Spendable -= r.debits.amount } return nil }