Example #1
0
// 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
}
Example #2
0
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
}
Example #3
0
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
}