Exemple #1
0
// ProcessTransaction is the main workhorse for handling insertion of new
// free-standing transactions into a memory pool.  It includes functionality
// such as rejecting duplicate transactions, ensuring transactions follow all
// rules, orphan transaction handling, and insertion into the memory pool.
func (mp *txMemPool) ProcessTransaction(tx *btcwire.MsgTx) error {
	txHash, err := tx.TxSha()
	if err != nil {
		return err
	}
	log.Tracef("[TXMP] Processing transaction %v", txHash)

	// Potentially accept the transaction to the memory pool.
	var isOrphan bool
	err = mp.maybeAcceptTransaction(tx, &isOrphan)
	if err != nil {
		return err
	}

	if !isOrphan {
		// Accept any orphan transactions that depend on this
		// transaction (they are no longer orphans) and repeat for those
		// accepted transactions until there are no more.
		err = mp.processOrphans(&txHash)
		if err != nil {
			return err
		}
	} else {
		// When the transaction is an orphan (has inputs missing),
		// potentially add it to the orphan pool.
		err := mp.maybeAddOrphan(tx, &txHash)
		if err != nil {
			return err
		}
	}

	return nil
}
Exemple #2
0
// removeTransaction removes the passed transaction from the memory pool.
func (mp *txMemPool) removeTransaction(tx *btcwire.MsgTx) {
	mp.lock.Lock()
	defer mp.lock.Unlock()

	// Remove any transactions which rely on this one.
	txHash, _ := tx.TxSha()
	for i := uint32(0); i < uint32(len(tx.TxOut)); i++ {
		outpoint := btcwire.NewOutPoint(&txHash, i)
		if txRedeemer, exists := mp.outpoints[*outpoint]; exists {
			mp.lock.Unlock()
			mp.removeTransaction(txRedeemer)
			mp.lock.Lock()
		}
	}

	// Remove the transaction and mark the referenced outpoints as unspent
	// by the pool.
	if tx, exists := mp.pool[txHash]; exists {
		for _, txIn := range tx.TxIn {
			delete(mp.outpoints, txIn.PreviousOutpoint)
		}
		delete(mp.pool, txHash)
	}

}
Exemple #3
0
// handleTxMsg is invoked when a peer receives a tx bitcoin message.  It blocks
// until the bitcoin transaction has been fully processed.  Unlock the block
// handler this does not serialize all transactions through a single thread
// transactions don't rely on the previous one in a linear fashion like blocks.
func (p *peer) handleTxMsg(msg *btcwire.MsgTx) {
	// Add the transaction to the known inventory for the peer.
	hash, err := msg.TxSha()
	if err != nil {
		log.Errorf("Unable to get transaction hash: %v", err)
		return
	}
	iv := btcwire.NewInvVect(btcwire.InvVect_Tx, &hash)
	p.addKnownInventory(iv)

	// Process the transaction.
	err = p.server.txMemPool.ProcessTransaction(msg)
	if err != nil {
		// When the error is a rule error, it means the transaction was
		// simply rejected as opposed to something actually going wrong,
		// so log it as such.  Otherwise, something really did go wrong,
		// so log it as an actual error.
		if _, ok := err.(TxRuleError); ok {
			log.Infof("Rejected transaction %v: %v", hash, err)
		} else {
			log.Errorf("Failed to process transaction %v: %v", hash, err)
		}
		return
	}
}
Exemple #4
0
// Creates a new bulletin from the containing Tx, supplied author and optional blockhash
// by unpacking txOuts that are considered data. It ignores extra junk behind the protobuffer.
// NewBulletin also asserts aspects of valid bulletins by throwing errors when msg len
// is zero or board len is greater than MaxBoardLen.
func NewBulletin(tx *btcwire.MsgTx, blkhash *btcwire.ShaHash, net *btcnet.Params) (*Bulletin, error) {
	wireBltn := &wirebulletin.WireBulletin{}

	author, err := getAuthor(tx, net)
	if err != nil {
		return nil, err
	}

	// TODO. scrutinize.

	// Bootleg solution, but if unmarshal fails slice txout and try again until we can try no more or it fails
	for j := len(tx.TxOut); j > 1; j-- {
		rel_txouts := tx.TxOut[:j] // slice off change txouts
		bytes, err := extractData(rel_txouts)
		if err != nil {
			continue
		}

		err = proto.Unmarshal(bytes, wireBltn)
		if err != nil {
			continue
		} else {
			// No errors, we found a good decode
			break
		}
	}
	if err != nil {
		return nil, err
	}

	board := wireBltn.GetBoard()
	// assert that the length of the board is within its max size!
	if len(board) > MaxBoardLen {
		return nil, ErrMaxBoardLen
	}

	msg := wireBltn.GetMessage()
	// assert that the bulletin has a non zero message length.
	if len(msg) < 1 {
		return nil, ErrNoMsg
	}

	// TODO assert that msg and board are valid UTF-8 strings.
	hash, _ := tx.TxSha()

	bltn := &Bulletin{
		Txid:      &hash,
		Block:     blkhash,
		Author:    author,
		Board:     board,
		Message:   msg,
		Timestamp: time.Unix(wireBltn.GetTimestamp(), 0),
	}

	return bltn, nil
}
Exemple #5
0
// handleDecodeRawTransaction handles decoderawtransaction commands.
func handleDecodeRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
	c := cmd.(*btcjson.DecodeRawTransactionCmd)

	// Deserialize the transaction.
	hexStr := c.HexTx
	if len(hexStr)%2 != 0 {
		hexStr = "0" + hexStr
	}
	serializedTx, err := hex.DecodeString(hexStr)
	if err != nil {
		return nil, btcjson.Error{
			Code: btcjson.ErrInvalidParameter.Code,
			Message: fmt.Sprintf("argument must be hexadecimal "+
				"string (not %q)", hexStr),
		}
	}
	var mtx btcwire.MsgTx
	err = mtx.Deserialize(bytes.NewBuffer(serializedTx))
	if err != nil {
		return nil, btcjson.Error{
			Code:    btcjson.ErrDeserialization.Code,
			Message: "TX decode failed",
		}
	}
	txSha, _ := mtx.TxSha()

	vin, err := createVinList(&mtx)
	if err != nil {
		return nil, err
	}
	vout, err := createVoutList(&mtx, s.server.btcnet)
	if err != nil {
		return nil, err
	}

	// Create and return the result.
	txReply := btcjson.TxRawDecodeResult{
		Txid:     txSha.String(),
		Version:  mtx.Version,
		Locktime: mtx.LockTime,
		Vin:      vin,
		Vout:     vout,
	}
	return txReply, nil
}
Exemple #6
0
// maybeAcceptTransaction is the main workhorse for handling insertion of new
// free-standing transactions into a memory pool.  It includes functionality
// such as rejecting duplicate transactions, ensuring transactions follow all
// rules, orphan transaction handling, and insertion into the memory pool.
func (mp *txMemPool) maybeAcceptTransaction(tx *btcwire.MsgTx, isOrphan *bool) error {
	*isOrphan = false
	txHash, err := tx.TxSha()
	if err != nil {
		return err
	}

	// Don't accept the transaction if it already exists in the pool.  This
	// applies to orphan transactions as well.  This check is intended to
	// be a quick check to weed out duplicates.  It is more expensive to
	// detect a duplicate transaction in the main chain, so that is done
	// later.
	if mp.isTransactionInPool(&txHash) {
		str := fmt.Sprintf("already have transaction %v", txHash)
		return TxRuleError(str)
	}

	// Perform preliminary sanity checks on the transaction.  This makes
	// use of btcchain which contains the invariant rules for what
	// transactions are allowed into blocks.
	err = btcchain.CheckTransactionSanity(tx)
	if err != nil {
		if _, ok := err.(btcchain.RuleError); ok {
			return TxRuleError(err.Error())
		}
		return err
	}

	// A standalone transaction must not be a coinbase transaction.
	if btcchain.IsCoinBase(tx) {
		str := fmt.Sprintf("transaction %v is an individual coinbase",
			txHash)
		return TxRuleError(str)
	}

	// Don't accept transactions with a lock time after the maximum int32
	// value for now.  This is an artifact of older bitcoind clients which
	// treated this field as an int32 and would treat anything larger
	// incorrectly (as negative).
	if tx.LockTime > math.MaxInt32 {
		str := fmt.Sprintf("transaction %v is has a lock time after "+
			"2038 which is not accepted yet", txHash)
		return TxRuleError(str)
	}

	// Get the current height of the main chain.  A standalone transaction
	// will be mined into the next block at best, so
	_, curHeight, err := mp.server.db.NewestSha()
	if err != nil {
		return err
	}
	nextBlockHeight := curHeight + 1

	// Don't allow non-standard transactions on the main network.
	if activeNetParams.btcnet == btcwire.MainNet {
		err := checkTransactionStandard(tx, nextBlockHeight)
		if err != nil {
			str := fmt.Sprintf("transaction %v is not a standard "+
				"transaction: %v", txHash, err)
			return TxRuleError(str)
		}
	}

	// The transaction may not use any of the same outputs as other
	// transactions already in the pool as that would ultimately result in a
	// double spend.  This check is intended to be quick and therefore only
	// detects double spends within the transaction pool itself.  The
	// transaction could still be double spending coins from the main chain
	// at this point.  There is a more in-depth check that happens later
	// after fetching the referenced transaction inputs from the main chain
	// which examines the actual spend data and prevents double spends.
	err = mp.checkPoolDoubleSpend(tx)
	if err != nil {
		return err
	}

	// Fetch all of the transactions referenced by the inputs to this
	// transaction.  This function also attempts to fetch the transaction
	// itself to be used for detecting a duplicate transaction without
	// needing to do a separate lookup.
	txStore, err := mp.fetchInputTransactions(tx)
	if err != nil {
		return err
	}

	// Don't allow the transaction if it exists in the main chain and is not
	// not already fully spent.
	if txD, exists := txStore[txHash]; exists && txD.Err == nil {
		for _, isOutputSpent := range txD.Spent {
			if !isOutputSpent {
				str := fmt.Sprintf("transaction already exists")
				return TxRuleError(str)
			}
		}
	}
	delete(txStore, txHash)

	// Transaction is an orphan if any of the inputs don't exist.
	for _, txD := range txStore {
		if txD.Err == btcdb.TxShaMissing {
			*isOrphan = true
			return nil
		}
	}

	// Perform several checks on the transaction inputs using the invariant
	// rules in btcchain for what transactions are allowed into blocks.
	// Also returns the fees associated with the transaction which will be
	// used later.
	txFee, err := btcchain.CheckTransactionInputs(tx, nextBlockHeight, txStore)
	if err != nil {
		return err
	}

	// Don't allow transactions with non-standard inputs on the main
	// network.
	if activeNetParams.btcnet == btcwire.MainNet {
		err := checkInputsStandard(tx)
		if err != nil {
			str := fmt.Sprintf("transaction %v has a non-standard "+
				"input: %v", txHash, err)
			return TxRuleError(str)
		}
	}

	// Note: if you modify this code to accept non-standard transactions,
	// you should add code here to check that the transaction does a
	// reasonable number of ECDSA signature verifications.

	// TODO(davec): Don't allow the transaction if the transation fee
	// would be too low to get into an empty block.
	_ = txFee

	// Verify crypto signatures for each input and reject the transaction if
	// any don't verify.
	err = btcchain.ValidateTransactionScripts(tx, &txHash, time.Now(), txStore)
	if err != nil {
		return err
	}

	// TODO(davec): Rate-limit free transactions

	// Add to transaction pool.
	mp.addTransaction(tx, &txHash)

	mp.lock.RLock()
	log.Debugf("[TXMP] Accepted transaction %v (pool size: %v)", txHash,
		len(mp.pool))
	mp.lock.RUnlock()

	// TODO(davec): Notifications

	// Generate the inventory vector and relay it.
	iv := btcwire.NewInvVect(btcwire.InvVect_Tx, &txHash)
	mp.server.RelayInventory(iv)

	return nil
}