Exemplo n.º 1
0
func (c *blockTxCollection) txRecordForInserts(tx *btcutil.Tx) *txRecord {
	if i, ok := c.txIndexes[tx.Index()]; ok {
		return c.txs[i]
	}

	log.Infof("Inserting transaction %v from block %d", tx.Sha(), c.Height)
	record := &txRecord{tx: tx}

	// If this new transaction record cannot be appended to the end of the
	// txs slice (which would disobey ordering transactions by their block
	// index), reslice and update the block's map of block indexes to txs
	// slice indexes.
	if len(c.txs) > 0 && c.txs[len(c.txs)-1].Tx().Index() > tx.Index() {
		i := uint32(len(c.txs))
		for i != 0 && c.txs[i-1].Tx().Index() >= tx.Index() {
			i--
		}
		detached := c.txs[i:]
		c.txs = append(c.txs[:i], record)
		c.txIndexes[tx.Index()] = i
		for i, r := range detached {
			newIndex := uint32(i + len(c.txs))
			c.txIndexes[r.Tx().Index()] = newIndex
		}
		c.txs = append(c.txs, detached...)
	} else {
		c.txIndexes[tx.Index()] = uint32(len(c.txs))
		c.txs = append(c.txs, record)
	}
	return record
}
Exemplo n.º 2
0
// NotifyForTxOuts iterates through all outputs of a tx, performing any
// necessary notifications for wallets.  If a non-nil block is passed,
// additional block information is passed with the notifications.
func (s *rpcServer) NotifyForTxOuts(tx *btcutil.Tx, block *btcutil.Block) {
	// Nothing to do if nobody is listening for transaction notifications.
	if len(s.ws.txNotifications) == 0 {
		return
	}

	for i, txout := range tx.MsgTx().TxOut {
		_, addrs, _, err := btcscript.ExtractPkScriptAddrs(
			txout.PkScript, s.server.btcnet)
		if err != nil {
			continue
		}

		for _, addr := range addrs {
			// Only support pay-to-pubkey-hash right now.
			if _, ok := addr.(*btcutil.AddressPubKeyHash); !ok {
				continue
			}

			encodedAddr := addr.EncodeAddress()
			if idlist, ok := s.ws.txNotifications[encodedAddr]; ok {
				for e := idlist.Front(); e != nil; e = e.Next() {
					n := e.Value.(ntfnChan)

					ntfn := &btcws.ProcessedTxNtfn{
						Receiver:   encodedAddr,
						TxID:       tx.Sha().String(),
						TxOutIndex: uint32(i),
						Amount:     txout.Value,
						PkScript:   hex.EncodeToString(txout.PkScript),
						// TODO(jrick): hardcoding unspent is WRONG and needs
						// to be either calculated from other block txs, or dropped.
						Spent: false,
					}

					if block != nil {
						blkhash, err := block.Sha()
						if err != nil {
							rpcsLog.Error("Error getting block sha; dropping Tx notification")
							break
						}
						ntfn.BlockHeight = int32(block.Height())
						ntfn.BlockHash = blkhash.String()
						ntfn.BlockIndex = tx.Index()
						ntfn.BlockTime = block.MsgBlock().Header.Timestamp.Unix()
					} else {
						ntfn.BlockHeight = -1
						ntfn.BlockIndex = -1
					}

					n <- ntfn
				}
			}
		}
	}
}
Exemplo n.º 3
0
// InsertTx records a transaction as belonging to a wallet's transaction
// history.  If block is nil, the transaction is considered unspent, and the
// transaction's index must be unset.  Otherwise, the transaction index must be
// set if a non-nil block is set.
//
// The transaction record is returned.  Credits and debits may be added to the
// transaction by calling methods on the TxRecord.
func (s *Store) InsertTx(tx *btcutil.Tx, block *Block) (*TxRecord, error) {
	// The receive time will be the earlier of now and the block time
	// (if any).
	received := time.Now()

	// Verify that the index of the transaction within the block is
	// set if a block is set, and unset if there is no block.
	index := tx.Index()
	switch {
	case index == btcutil.TxIndexUnknown && block != nil:
		return nil, errors.New("transaction block index unset")
	case index != btcutil.TxIndexUnknown && block == nil:
		return nil, errors.New("transaction block index set")
	}

	// Simply create or return the transaction record if this transaction
	// is unconfirmed.
	if block == nil {
		r := s.unconfirmed.txRecordForInserts(tx)
		r.received = received
		return &TxRecord{BlockTxKey{BlockHeight: -1}, r, s}, nil
	}

	// Check if block records already exist for this tx.  If so,
	// we're done.
	key := BlockTxKey{index, block.Height}
	record, err := s.lookupBlockTx(key)
	switch err.(type) {
	case MissingValueError:
		// handle it later
	case nil:
		// Verify that the txs actually match.
		if *record.tx.Sha() != *tx.Sha() {
			return nil, ErrInconsistentStore
		}

		return &TxRecord{key, record, s}, nil
	}

	// If the exact tx (not a double spend) is already included but
	// unconfirmed, move it to a block.
	if r, ok := s.unconfirmed.txs[*tx.Sha()]; ok {
		r.Tx().SetIndex(tx.Index())
		if err := s.moveMinedTx(r, block); err != nil {
			return nil, err
		}
		return &TxRecord{key, r, s}, nil
	}

	// If this transaction is not already saved unconfirmed, remove all
	// unconfirmed transactions that are now invalidated due to being a
	// double spend.  This also handles killing unconfirmed transaction
	// spend chains if any other unconfirmed transactions spend outputs
	// of the removed double spend.
	if err := s.removeDoubleSpends(tx); err != nil {
		return nil, err
	}

	r := s.blockTxRecordForInserts(tx, block)
	if r.received.IsZero() {
		if !block.Time.IsZero() && block.Time.Before(received) {
			received = block.Time
		}
		r.received = received
	}
	return &TxRecord{key, r, s}, nil
}