Exemplo n.º 1
0
// countP2SHSigOps returns the number of signature operations for all input
// transactions which are of the pay-to-script-hash type.  This uses the
// precise, signature operation counting mechanism from btcscript which requires
// access to the input transaction scripts.
func countP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool, txStore TxStore) (int, error) {
	// Coinbase transactions have no interesting inputs.
	if isCoinBaseTx {
		return 0, nil
	}

	// Accumulate the number of signature operations in all transaction
	// inputs.
	msgTx := tx.MsgTx()
	totalSigOps := 0
	for _, txIn := range msgTx.TxIn {
		// Ensure the referenced input transaction is available.
		txInHash := &txIn.PreviousOutpoint.Hash
		originTx, exists := txStore[*txInHash]
		if !exists || originTx.Err != nil || originTx.Tx == nil {
			str := fmt.Sprintf("unable to find input transaction "+
				"%v referenced from transaction %v", txInHash,
				tx.Sha())
			return 0, RuleError(str)
		}
		originMsgTx := originTx.Tx.MsgTx()

		// Ensure the output index in the referenced transaction is
		// available.
		originTxIndex := txIn.PreviousOutpoint.Index
		if originTxIndex >= uint32(len(originMsgTx.TxOut)) {
			str := fmt.Sprintf("out of bounds input index %d in "+
				"transaction %v referenced from transaction %v",
				originTxIndex, txInHash, tx.Sha())
			return 0, RuleError(str)
		}

		// We're only interested in pay-to-script-hash types, so skip
		// this input if it's not one.
		pkScript := originMsgTx.TxOut[originTxIndex].PkScript
		if !btcscript.IsPayToScriptHash(pkScript) {
			continue
		}

		// Count the precise number of signature operations in the
		// referenced public key script.
		sigScript := txIn.SignatureScript
		numSigOps := btcscript.GetPreciseSigOpCount(sigScript, pkScript,
			true)

		// We could potentially overflow the accumulator so check for
		// overflow.
		lastSigOps := totalSigOps
		totalSigOps += numSigOps
		if totalSigOps < lastSigOps {
			str := fmt.Sprintf("the public key script from "+
				"output index %d in transaction %v contains "+
				"too many signature operations - overflow",
				originTxIndex, txInHash)
			return 0, RuleError(str)
		}
	}

	return totalSigOps, nil
}
Exemplo n.º 2
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.º 3
0
// matchTxAndUpdate returns true if the bloom filter matches data within the
// passed transaction, otherwise false is returned.  If the filter does match
// the passed transaction, it will also update the filter depending on the bloom
// update flags set via the loaded filter if needed.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) matchTxAndUpdate(tx *btcutil.Tx) bool {
	// Check if the filter matches the hash of the transaction.
	// This is useful for finding transactions when they appear in a block.
	matched := bf.matches(tx.Sha().Bytes())

	// Check if the filter matches any data elements in the public key
	// scripts of any of the outputs.  When it does, add the outpoint that
	// matched so transactions which spend from the matched transaction are
	// also included in the filter.  This removes the burden of updating the
	// filter for this scenario from the client.  It is also more efficient
	// on the network since it avoids the need for another filteradd message
	// from the client and avoids some potential races that could otherwise
	// occur.
	for i, txOut := range tx.MsgTx().TxOut {
		pushedData, err := btcscript.PushedData(txOut.PkScript)
		if err != nil {
			continue
		}

		for _, data := range pushedData {
			if !bf.matches(data) {
				continue
			}

			matched = true
			bf.maybeAddOutpoint(txOut.PkScript, tx.Sha(), uint32(i))
			break
		}
	}

	// Nothing more to do if a match has already been made.
	if matched {
		return true
	}

	// At this point, the transaction and none of the data elements in the
	// public key scripts of its outputs matched.

	// Check if the filter matches any outpoints this transaction spends or
	// any any data elements in the signature scripts of any of the inputs.
	for _, txin := range tx.MsgTx().TxIn {
		if bf.matchesOutPoint(&txin.PreviousOutpoint) {
			return true
		}

		pushedData, err := btcscript.PushedData(txin.SignatureScript)
		if err != nil {
			continue
		}
		for _, data := range pushedData {
			if bf.matches(data) {
				return true
			}
		}
	}

	return false
}
Exemplo n.º 4
0
// newBlockNotifyCheckTxOut is a helper function to iterate through
// each transaction output of a new block and perform any checks and
// notify listening frontends when necessary.
func (s *rpcServer) newBlockNotifyCheckTxOut(block *btcutil.Block, tx *btcutil.Tx, spent []bool) {
	for wltNtfn, cxt := range s.ws.requests.m {
		for i, txout := range tx.MsgTx().TxOut {
			_, txaddrhash, err := btcscript.ScriptToAddrHash(txout.PkScript)
			if err != nil {
				log.Debug("Error getting payment address from tx; dropping any Tx notifications.")
				break
			}
			for addr, id := range cxt.txRequests {
				if !bytes.Equal(addr[:], txaddrhash) {
					continue
				}
				blkhash, err := block.Sha()
				if err != nil {
					log.Error("Error getting block sha; dropping Tx notification.")
					break
				}
				txaddr, err := btcutil.EncodeAddress(txaddrhash, s.server.btcnet)
				if err != nil {
					log.Error("Error encoding address; dropping Tx notification.")
					break
				}
				reply := &btcjson.Reply{
					Result: struct {
						Sender    string `json:"sender"`
						Receiver  string `json:"receiver"`
						BlockHash string `json:"blockhash"`
						Height    int64  `json:"height"`
						TxHash    string `json:"txhash"`
						Index     uint32 `json:"index"`
						Amount    int64  `json:"amount"`
						PkScript  string `json:"pkscript"`
						Spent     bool   `json:"spent"`
					}{
						Sender:    "Unknown", // TODO(jrick)
						Receiver:  txaddr,
						BlockHash: blkhash.String(),
						Height:    block.Height(),
						TxHash:    tx.Sha().String(),
						Index:     uint32(i),
						Amount:    txout.Value,
						PkScript:  btcutil.Base58Encode(txout.PkScript),
						Spent:     spent[i],
					},
					Error: nil,
					Id:    &id,
				}
				replyBytes, err := json.Marshal(reply)
				if err != nil {
					log.Errorf("RPCS: Unable to marshal tx notification: %v", err)
					continue
				}
				wltNtfn <- replyBytes
			}
		}
	}
}
Exemplo n.º 5
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.º 6
0
func (r *txRecord) setDebitsSpends(spends []*BlockOutputKey, tx *btcutil.Tx) error {
	if r.debits.spends != nil {
		if *r.tx.Sha() == *tx.Sha() {
			return ErrDuplicateInsert
		}
		return ErrInconsistentStore
	}
	r.debits.spends = spends
	return nil
}
Exemplo n.º 7
0
// logSkippedDeps logs any dependencies which are also skipped as a result of
// skipping a transaction while generating a block template at the trace level.
func logSkippedDeps(tx *btcutil.Tx, deps *list.List) {
	if deps == nil {
		return
	}

	for e := deps.Front(); e != nil; e = e.Next() {
		item := e.Value.(*txPrioItem)
		minrLog.Tracef("Skipping tx %s since it depends on %s\n",
			item.tx.Sha(), tx.Sha())
	}
}
Exemplo n.º 8
0
func (u *unconfirmedStore) txRecordForInserts(tx *btcutil.Tx) *txRecord {
	r, ok := u.txs[*tx.Sha()]
	if !ok {
		r = &txRecord{tx: tx}
		u.txs[*tx.Sha()] = r
		for _, input := range r.Tx().MsgTx().TxIn {
			u.previousOutpoints[input.PreviousOutpoint] = r
		}
	}
	return r
}
Exemplo n.º 9
0
// RemoveDoubleSpends removes all transactions which spend outputs spent by the
// passed transaction from the memory pool.  Removing those transactions then
// leads to removing all transactions which rely on them, recursively.  This is
// necessary when a block is connected to the main chain because the block may
// contain transactions which were previously unknown to the memory pool
//
// This function is safe for concurrent access.
func (mp *txMemPool) RemoveDoubleSpends(tx *btcutil.Tx) {
	// Protect concurrent access.
	mp.Lock()
	defer mp.Unlock()

	for _, txIn := range tx.MsgTx().TxIn {
		if txRedeemer, ok := mp.outpoints[txIn.PreviousOutpoint]; ok {
			if !txRedeemer.Sha().IsEqual(tx.Sha()) {
				mp.removeTransaction(txRedeemer)
			}
		}
	}
}
Exemplo n.º 10
0
// addTransaction adds the passed transaction to the memory pool.  It should
// not be called directly as it doesn't perform any validation.  This is a
// helper for maybeAcceptTransaction.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) addTransaction(tx *btcutil.Tx, height, fee int64) {
	// Add the transaction to the pool and mark the referenced outpoints
	// as spent by the pool.
	mp.pool[*tx.Sha()] = &TxDesc{
		Tx:     tx,
		Added:  time.Now(),
		Height: height,
		Fee:    fee,
	}
	for _, txIn := range tx.MsgTx().TxIn {
		mp.outpoints[txIn.PreviousOutpoint] = tx
	}
}
Exemplo n.º 11
0
func (r *txRecord) setCredit(c *credit, index uint32, tx *btcutil.Tx) error {
	if len(r.credits) <= int(index) {
		r.credits = extendCredits(r.credits, index)
	}
	if r.credits[index] != nil {
		if *r.tx.Sha() == *tx.Sha() {
			return ErrDuplicateInsert
		}
		return ErrInconsistentStore
	}

	r.credits[index] = c
	return nil
}
Exemplo n.º 12
0
func (r *txRecord) setCredit(index uint32, change bool, tx *btcutil.Tx) error {
	if r.credits == nil {
		r.credits = make([]*credit, 0, len(tx.MsgTx().TxOut))
	}
	for i := uint32(len(r.credits)); i <= index; i++ {
		r.credits = append(r.credits, nil)
	}
	if r.credits[index] != nil {
		if *r.tx.Sha() == *tx.Sha() {
			return ErrDuplicateInsert
		}
		return ErrInconsistentStore
	}
	r.credits[index] = &credit{change: change}
	return nil
}
Exemplo n.º 13
0
// FetchTransactionStore fetches the input transactions referenced by the
// passed transaction from the point of view of the end of the main chain.  It
// also attempts to fetch the transaction itself so the returned TxStore can be
// examined for duplicate transactions.
func (b *BlockChain) FetchTransactionStore(tx *btcutil.Tx) (TxStore, error) {
	// Create a set of needed transactions from the transactions referenced
	// by the inputs of the passed transaction.  Also, add the passed
	// transaction itself as a way for the caller to detect duplicates.
	txNeededSet := make(map[btcwire.ShaHash]struct{})
	txNeededSet[*tx.Sha()] = struct{}{}
	for _, txIn := range tx.MsgTx().TxIn {
		txNeededSet[txIn.PreviousOutPoint.Hash] = struct{}{}
	}

	// Request the input transactions from the point of view of the end of
	// the main chain without including fully spent trasactions in the
	// results.  Fully spent transactions are only needed for chain
	// reorganization which does not apply here.
	txStore := fetchTxStoreMain(b.db, txNeededSet, false)
	return txStore, nil
}
Exemplo n.º 14
0
// addOrphan adds an orphan transaction to the orphan pool.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) addOrphan(tx *btcutil.Tx) {
	// Limit the number orphan transactions to prevent memory exhaustion.  A
	// random orphan is evicted to make room if needed.
	mp.limitNumOrphans()

	mp.orphans[*tx.Sha()] = tx
	for _, txIn := range tx.MsgTx().TxIn {
		originTxHash := txIn.PreviousOutpoint.Hash
		if mp.orphansByPrev[originTxHash] == nil {
			mp.orphansByPrev[originTxHash] = list.New()
		}
		mp.orphansByPrev[originTxHash].PushBack(tx)
	}

	txmpLog.Debugf("Stored orphan transaction %v (total: %d)", tx.Sha(),
		len(mp.orphans))
}
Exemplo n.º 15
0
// spendTransaction updates the passed transaction store by marking the inputs
// to the passed transaction as spent.  It also adds the passed transaction to
// the store at the provided height.
func spendTransaction(txStore btcchain.TxStore, tx *btcutil.Tx, height int64) error {
	for _, txIn := range tx.MsgTx().TxIn {
		originHash := &txIn.PreviousOutPoint.Hash
		originIndex := txIn.PreviousOutPoint.Index
		if originTx, exists := txStore[*originHash]; exists {
			originTx.Spent[originIndex] = true
		}
	}

	txStore[*tx.Sha()] = &btcchain.TxData{
		Tx:          tx,
		Hash:        tx.Sha(),
		BlockHeight: height,
		Spent:       make([]bool, len(tx.MsgTx().TxOut)),
		Err:         nil,
	}

	return nil
}
Exemplo n.º 16
0
// removeTransaction is the internal function which implements the public
// RemoveTransaction.  See the comment for RemoveTransaction for more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) removeTransaction(tx *btcutil.Tx) {
	// Remove any transactions which rely on this one.
	txHash := tx.Sha()
	for i := uint32(0); i < uint32(len(tx.MsgTx().TxOut)); i++ {
		outpoint := btcwire.NewOutPoint(txHash, i)
		if txRedeemer, exists := mp.outpoints[*outpoint]; exists {
			mp.removeTransaction(txRedeemer)
		}
	}

	// Remove the transaction and mark the referenced outpoints as unspent
	// by the pool.
	if txDesc, exists := mp.pool[*txHash]; exists {
		for _, txIn := range txDesc.Tx.MsgTx().TxIn {
			delete(mp.outpoints, txIn.PreviousOutpoint)
		}
		delete(mp.pool, *txHash)
	}
}
Exemplo n.º 17
0
// ProcessTransaction is the main workhorse for handling insertion of new
// free-standing transactions into the memory pool.  It includes functionality
// such as rejecting duplicate transactions, ensuring transactions follow all
// rules, orphan transaction handling, and insertion into the memory pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool) error {
	// Protect concurrent access.
	mp.Lock()
	defer mp.Unlock()

	txmpLog.Tracef("Processing transaction %v", tx.Sha())

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

	if !isOrphan {
		// Generate the inventory vector and relay it.
		iv := btcwire.NewInvVect(btcwire.InvTypeTx, tx.Sha())
		mp.server.RelayInventory(iv)

		// 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(tx.Sha())
		if err != nil {
			return err
		}
	} else {
		// The transaction is an orphan (has inputs missing).  Reject
		// it if the flag to allow orphans is not set.
		if !allowOrphan {
			// NOTE: RejectDuplicate is really not an accurate
			// reject code here, but it matches the reference
			// implementation and there isn't a better choice due
			// to the limited number of reject codes.  Missing
			// inputs is assumed to mean they are already spent
			// which is not really always the case.
			return txRuleError(btcwire.RejectDuplicate,
				"transaction spends unknown inputs")
		}

		// Potentially add the orphan transaction to the orphan pool.
		err := mp.maybeAddOrphan(tx)
		if err != nil {
			return err
		}
	}

	return nil
}
Exemplo n.º 18
0
// ProcessTransaction is the main workhorse for handling insertion of new
// free-standing transactions into the memory pool.  It includes functionality
// such as rejecting duplicate transactions, ensuring transactions follow all
// rules, orphan transaction handling, and insertion into the memory pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool) error {
	// Protect concurrent access.
	mp.Lock()
	defer mp.Unlock()

	txmpLog.Tracef("Processing transaction %v", tx.Sha())

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

	if !isOrphan {
		// Generate the inventory vector and relay it.
		iv := btcwire.NewInvVect(btcwire.InvTypeTx, tx.Sha())
		mp.server.RelayInventory(iv)

		// 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(tx.Sha())
		if err != nil {
			return err
		}
	} else {
		// The transaction is an orphan (has inputs missing).  Reject
		// it if the flag to allow orphans is not set.
		if !allowOrphan {
			return TxRuleError("transaction spends unknown inputs")
		}

		// Potentially add the orphan transaction to the orphan pool.
		err := mp.maybeAddOrphan(tx)
		if err != nil {
			return err
		}
	}

	return nil
}
Exemplo n.º 19
0
// ProcessTransaction is the main workhorse for handling insertion of new
// free-standing transactions into the memory pool.  It includes functionality
// such as rejecting duplicate transactions, ensuring transactions follow all
// rules, orphan transaction handling, and insertion into the memory pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) ProcessTransaction(tx *btcutil.Tx) error {
	// Protect concurrent access.
	mp.Lock()
	defer mp.Unlock()

	txmpLog.Tracef("Processing transaction %v", tx.Sha())

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

	if !isOrphan {
		// Generate the inventory vector and relay it.
		iv := btcwire.NewInvVect(btcwire.InvTypeTx, tx.Sha())
		mp.server.RelayInventory(iv)

		// 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(tx.Sha())
		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)
		if err != nil {
			return err
		}
	}

	return nil
}
Exemplo n.º 20
0
// maybeAcceptTransaction is the internal function which implements the public
// MaybeAcceptTransaction.  See the comment for MaybeAcceptTransaction for
// more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNew, rateLimit bool) error {
	if isOrphan != nil {
		*isOrphan = false
	}
	txHash := tx.Sha()

	// 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.
	if mp.haveTransaction(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.MsgTx().LockTime > math.MaxInt32 {
		str := fmt.Sprintf("transaction %v 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 it's height is at least
	// one more than the current height.
	_, curHeight, err := mp.server.db.NewestSha()
	if err != nil {
		return err
	}
	nextBlockHeight := curHeight + 1

	// Don't allow non-standard transactions if the network parameters
	// forbid their relaying.
	if !activeNetParams.RelayNonStdTxs {
		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 {
				return TxRuleError("transaction already exists")
			}
		}
	}
	delete(txStore, *txHash)

	// Transaction is an orphan if any of the inputs don't exist.
	for _, txD := range txStore {
		if txD.Err == btcdb.TxShaMissing {
			if isOrphan != nil {
				*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 {
		if _, ok := err.(btcchain.RuleError); ok {
			return TxRuleError(err.Error())
		}
		return err
	}

	// Don't allow transactions with non-standard inputs if the network
	// parameters forbid their relaying.
	if !activeNetParams.RelayNonStdTxs {
		err := checkInputsStandard(tx, txStore)
		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.

	// Don't allow transactions with fees too low to get into a mined block.
	minRequiredFee := calcMinRelayFee(tx)
	if txFee < minRequiredFee {
		str := fmt.Sprintf("transaction %v has %d fees which is under "+
			"the required amount of %d", txHash, txFee,
			minRequiredFee)
		return TxRuleError(str)
	}

	// Free-to-relay transactions are rate limited here to prevent
	// penny-flooding with tiny transactions as a form of attack.
	if rateLimit && minRequiredFee == 0 {
		nowUnix := time.Now().Unix()
		// we decay passed data with an exponentially decaying ~10
		// minutes window - matches bitcoind handling.
		mp.pennyTotal *= math.Pow(1.0-1.0/600.0,
			float64(nowUnix-mp.lastPennyUnix))
		mp.lastPennyUnix = nowUnix

		// Are we still over the limit?
		if mp.pennyTotal >= cfg.FreeTxRelayLimit*10*1000 {
			str := fmt.Sprintf("transaction %v has 0 fees and has "+
				"been rejected by the rate limiter", txHash)
			return TxRuleError(str)
		}
		oldTotal := mp.pennyTotal

		mp.pennyTotal += float64(tx.MsgTx().SerializeSize())
		txmpLog.Tracef("rate limit: curTotal %v, nextTotal: %v, "+
			"limit %v", oldTotal, mp.pennyTotal,
			cfg.FreeTxRelayLimit*10*1000)
	}

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

	// Add to transaction pool.
	mp.addTransaction(tx, curHeight, txFee)

	txmpLog.Debugf("Accepted transaction %v (pool size: %v)", txHash,
		len(mp.pool))

	// Notify websocket clients about mempool transactions.
	if mp.server.rpcServer != nil {
		mp.server.rpcServer.ntfnMgr.NotifyMempoolTx(tx, isNew)
	}

	return nil
}
Exemplo n.º 21
0
// CheckTransactionInputs performs a series of checks on the inputs to a
// transaction to ensure they are valid.  An example of some of the checks
// include verifying all inputs exist, ensuring the coinbase seasoning
// requirements are met, detecting double spends, validating all values and fees
// are in the legal range and the total output amount doesn't exceed the input
// amount, and verifying the signatures to prove the spender was the owner of
// the bitcoins and therefore allowed to spend them.  As it checks the inputs,
// it also calculates the total fees for the transaction and returns that value.
func CheckTransactionInputs(tx *btcutil.Tx, txHeight int64, txStore TxStore) (int64, error) {
	// Coinbase transactions have no inputs.
	if IsCoinBase(tx) {
		return 0, nil
	}

	txHash := tx.Sha()
	var totalSatoshiIn int64
	for _, txIn := range tx.MsgTx().TxIn {
		// Ensure the input is available.
		txInHash := &txIn.PreviousOutpoint.Hash
		originTx, exists := txStore[*txInHash]
		if !exists || originTx.Err != nil || originTx.Tx == nil {
			str := fmt.Sprintf("unable to find input transaction "+
				"%v for transaction %v", txInHash, txHash)
			return 0, RuleError(str)
		}

		// Ensure the transaction is not spending coins which have not
		// yet reached the required coinbase maturity.
		if IsCoinBase(originTx.Tx) {
			originHeight := originTx.BlockHeight
			blocksSincePrev := txHeight - originHeight
			if blocksSincePrev < coinbaseMaturity {
				str := fmt.Sprintf("tried to spend coinbase "+
					"transaction %v from height %v at "+
					"height %v before required maturity "+
					"of %v blocks", txHash, originHeight,
					txHeight, coinbaseMaturity)
				return 0, RuleError(str)
			}
		}

		// Ensure the transaction is not double spending coins.
		originTxIndex := txIn.PreviousOutpoint.Index
		if originTxIndex >= uint32(len(originTx.Spent)) {
			return 0, fmt.Errorf("out of bounds input index %d in "+
				"transaction %v referenced from transaction %v",
				originTxIndex, txInHash, txHash)
		}
		if originTx.Spent[originTxIndex] {
			str := fmt.Sprintf("transaction %v tried to double "+
				"spend coins from transaction %v", txHash,
				txInHash)
			return 0, RuleError(str)
		}

		// Ensure the transaction amounts are in range.  Each of the
		// output values of the input transactions must not be negative
		// or more than the max allowed per transaction.  All amounts in
		// a transaction are in a unit value known as a satoshi.  One
		// bitcoin is a quantity of satoshi as defined by the
		// SatoshiPerBitcoin constant.
		originTxSatoshi := originTx.Tx.MsgTx().TxOut[originTxIndex].Value
		if originTxSatoshi < 0 {
			str := fmt.Sprintf("transaction output has negative "+
				"value of %v", originTxSatoshi)
			return 0, RuleError(str)
		}
		if originTxSatoshi > btcutil.MaxSatoshi {
			str := fmt.Sprintf("transaction output value of %v is "+
				"higher than max allowed value of %v",
				originTxSatoshi, btcutil.MaxSatoshi)
			return 0, RuleError(str)
		}

		// The total of all outputs must not be more than the max
		// allowed per transaction.  Also, we could potentially overflow
		// the accumulator so check for overflow.
		lastSatoshiIn := totalSatoshiIn
		totalSatoshiIn += originTxSatoshi
		if totalSatoshiIn < lastSatoshiIn ||
			totalSatoshiIn > btcutil.MaxSatoshi {
			str := fmt.Sprintf("total value of all transaction "+
				"inputs is %v which is higher than max "+
				"allowed value of %v", totalSatoshiIn,
				btcutil.MaxSatoshi)
			return 0, RuleError(str)
		}

		// Mark the referenced output as spent.
		originTx.Spent[originTxIndex] = true
	}

	// Calculate the total output amount for this transaction.  It is safe
	// to ignore overflow and out of range errors here because those error
	// conditions would have already been caught by checkTransactionSanity.
	var totalSatoshiOut int64
	for _, txOut := range tx.MsgTx().TxOut {
		totalSatoshiOut += txOut.Value
	}

	// Ensure the transaction does not spend more than its inputs.
	if totalSatoshiIn < totalSatoshiOut {
		str := fmt.Sprintf("total value of all transaction inputs for "+
			"transaction %v is %v which is less than the amount "+
			"spent of %v", txHash, totalSatoshiIn, totalSatoshiOut)
		return 0, RuleError(str)
	}

	// NOTE: bitcoind checks if the transaction fees are < 0 here, but that
	// is an impossible condition because of the check above that ensures
	// the inputs are >= the outputs.
	txFeeInSatoshi := totalSatoshiIn - totalSatoshiOut
	return txFeeInSatoshi, nil
}
Exemplo n.º 22
0
// maybeAcceptTransaction is the internal function which implements the public
// MaybeAcceptTransaction.  See the comment for MaybeAcceptTransaction for
// more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool) error {
	if isOrphan != nil {
		*isOrphan = false
	}
	txHash := tx.Sha()

	// 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.
	if mp.haveTransaction(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.MsgTx().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 it's height is at least
	// one more than the current height.
	_, 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 {
			if isOrphan != nil {
				*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 {
		if _, ok := err.(btcchain.RuleError); ok {
			return TxRuleError(err.Error())
		}
		return err
	}

	// Don't allow transactions with non-standard inputs on the main
	// network.
	if activeNetParams.btcnet == btcwire.MainNet {
		err := checkInputsStandard(tx, txStore)
		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.

	// Don't allow transactions with fees too low to get into a mined block.
	minRequiredFee := calcMinRelayFee(tx)
	if txFee < minRequiredFee {
		str := fmt.Sprintf("transaction %v has %d fees which is under "+
			"the required amount of %d", txHash, txFee,
			minRequiredFee)
		return TxRuleError(str)
	}

	// TODO(davec): Rate-limit 'free' transactions.  That is to say
	// transactions which are less than the minimum relay fee and are
	// therefore considered free.

	// Verify crypto signatures for each input and reject the transaction if
	// any don't verify.
	flags := btcscript.ScriptBip16 | btcscript.ScriptCanonicalSignatures
	err = btcchain.ValidateTransactionScripts(tx, txStore, flags)
	if err != nil {
		return err
	}

	// Add to transaction pool.
	mp.addTransaction(tx, curHeight, txFee)

	txmpLog.Debugf("Accepted transaction %v (pool size: %v)", txHash,
		len(mp.pool))

	// Notify wallets of mempool transactions to wallet addresses.
	if mp.server.rpcServer != nil {
		mp.server.rpcServer.NotifyForTxOuts(tx, nil)
	}

	return nil
}
Exemplo n.º 23
0
// Parse transaction takes care of testing the tx for certain rules; class A/B checking
// Fundraiser checks and Dex messages as well as properly delegating the raw messages to
// the proper object which. After a full Msg has been assembled it will return the Msg
// or an error with a description. Whoever called this function should delegate the msg
// to the proper object handler.
func (mp *MsgParser) ParseTx(tx *btcutil.Tx, height, time int64) (msgs []*Msg, err error) {
	sender, _ := mscutil.FindSender(tx.MsgTx().TxIn, mp.btcdb)

	// Check if this is a Class A transaction
	if isClassA(tx) {
		mscutil.Logger.Println("Got 'Class A' transaction with hash", tx.Sha())

		// Create a simple send transaction
		simpleSend, e := mscutil.MakeClassASimpleSend(sender, mscutil.GetAddrsClassA(tx))
		err = e

		if simpleSend != nil {
			mscutil.Logger.Println("Decoded Simple Send transaction:", simpleSend, simpleSend.Data)
			msgs = append(msgs, &Msg{msg: simpleSend})
		}
	} else {
		mscutil.Logger.Println("Got 'Class B' tx with hash", tx.Sha())

		// Parse addresses from the tx using class B rules
		plainTextKeys, receiver, e := mscutil.GetAddrsClassB(tx, sender)
		err = e
		if err == nil {
			// Receiver is first, data is second in the slice

			// Let's change 00000014h to 22d
			data := plainTextKeys[0][2:10]
			f, _ := hex.DecodeString(data)
			messageType := int(f[3])

			switch messageType {
			case mscutil.TxMsgTy:
				simpleSend, e := mscutil.MakeClassBSimpleSend(plainTextKeys, receiver, sender)
				err = e

				if simpleSend != nil {
					// TODO: Do we want to add the sender and receiver to the simple send message?
					mscutil.Logger.Println("Got Simple Send Class B from", sender, "to", receiver)
					msgs = append(msgs, &Msg{msg: simpleSend})
				}
			default:
				err = fmt.Errorf("Unknown message type %d. Support for transaction type not implemented yet.\n", messageType)
				return nil, err
			}
		}
	}

	if height <= mscutil.FundraiserEndBlock {
		// Collect the addresses and values for every input used for this transaction
		highestAddress, _ := mscutil.FindSender(tx.MsgTx().TxIn, mp.btcdb)

		var totalSpend int64
		for _, txOut := range tx.MsgTx().TxOut {
			addr, _ := mscutil.GetAddrs(txOut.PkScript)
			if addr[0].Addr == mscutil.ExodusAddress {
				totalSpend += txOut.Value
			}
		}

		fundraiserTx, e := mscutil.NewFundraiserTransaction(highestAddress, totalSpend, time)
		err = e

		msgs = append(msgs, &Msg{msg: fundraiserTx})
	}

	// If nothing has been found it check for Payment for accept DEx message.
	if len(msgs) == 0 {
		// TODO:
	}

	return
}
Exemplo n.º 24
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
}
Exemplo n.º 25
0
// maybeAcceptTransaction is the internal function which implements the public
// MaybeAcceptTransaction.  See the comment for MaybeAcceptTransaction for
// more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNew, rateLimit bool) error {
	if isOrphan != nil {
		*isOrphan = false
	}
	txHash := tx.Sha()

	// 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.
	if mp.haveTransaction(txHash) {
		str := fmt.Sprintf("already have transaction %v", txHash)
		return txRuleError(btcwire.RejectDuplicate, 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 cerr, ok := err.(btcchain.RuleError); ok {
			return chainRuleError(cerr)
		}
		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(btcwire.RejectInvalid, 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.MsgTx().LockTime > math.MaxInt32 {
		str := fmt.Sprintf("transaction %v has a lock time after "+
			"2038 which is not accepted yet", txHash)
		return txRuleError(btcwire.RejectNonstandard, str)
	}

	// Get the current height of the main chain.  A standalone transaction
	// will be mined into the next block at best, so it's height is at least
	// one more than the current height.
	_, curHeight, err := mp.server.db.NewestSha()
	if err != nil {
		// This is an unexpected error so don't turn it into a rule
		// error.
		return err
	}
	nextBlockHeight := curHeight + 1

	// Don't allow non-standard transactions if the network parameters
	// forbid their relaying.
	if !activeNetParams.RelayNonStdTxs {
		err := checkTransactionStandard(tx, nextBlockHeight)
		if err != nil {
			// Attempt to extract a reject code from the error so
			// it can be retained.  When not possible, fall back to
			// a non standard error.
			rejectCode, found := extractRejectCode(err)
			if !found {
				rejectCode = btcwire.RejectNonstandard
			}
			str := fmt.Sprintf("transaction %v is not standard: %v",
				txHash, err)
			return txRuleError(rejectCode, 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 {
		if cerr, ok := err.(btcchain.RuleError); ok {
			return chainRuleError(cerr)
		}
		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 {
				return txRuleError(btcwire.RejectDuplicate,
					"transaction already exists")
			}
		}
	}
	delete(txStore, *txHash)

	// Transaction is an orphan if any of the inputs don't exist.
	for _, txD := range txStore {
		if txD.Err == btcdb.ErrTxShaMissing {
			if isOrphan != nil {
				*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 {
		if cerr, ok := err.(btcchain.RuleError); ok {
			return chainRuleError(cerr)
		}
		return err
	}

	// Don't allow transactions with non-standard inputs if the network
	// parameters forbid their relaying.
	if !activeNetParams.RelayNonStdTxs {
		err := checkInputsStandard(tx, txStore)
		if err != nil {
			// Attempt to extract a reject code from the error so
			// it can be retained.  When not possible, fall back to
			// a non standard error.
			rejectCode, found := extractRejectCode(err)
			if !found {
				rejectCode = btcwire.RejectNonstandard
			}
			str := fmt.Sprintf("transaction %v has a non-standard "+
				"input: %v", txHash, err)
			return txRuleError(rejectCode, 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.

	// Don't allow transactions with an excessive number of signature
	// operations which would result in making it impossible to mine.  Since
	// the coinbase address itself can contain signature operations, the
	// maximum allowed signature operations per transaction is less than
	// the maximum allowed signature operations per block.
	numSigOps, err := btcchain.CountP2SHSigOps(tx, false, txStore)
	if err != nil {
		if cerr, ok := err.(btcchain.RuleError); ok {
			return chainRuleError(cerr)
		}
		return err
	}
	numSigOps += btcchain.CountSigOps(tx)
	if numSigOps > maxSigOpsPerTx {
		str := fmt.Sprintf("transaction %v has too many sigops: %d > %d",
			txHash, numSigOps, maxSigOpsPerTx)
		return txRuleError(btcwire.RejectNonstandard, str)
	}

	// Don't allow transactions with fees too low to get into a mined block.
	//
	// Most miners allow a free transaction area in blocks they mine to go
	// alongside the area used for high-priority transactions as well as
	// transactions with fees.  A transaction size of up to 1000 bytes is
	// considered safe to go into this section.  Further, the minimum fee
	// calculated below on its own would encourage several small
	// transactions to avoid fees rather than one single larger transaction
	// which is more desirable.  Therefore, as long as the size of the
	// transaction does not exceeed 1000 less than the reserved space for
	// high-priority transactions, don't require a fee for it.
	serializedSize := int64(tx.MsgTx().SerializeSize())
	minFee := calcMinRequiredTxRelayFee(serializedSize)
	if serializedSize >= (defaultBlockPrioritySize-1000) && txFee < minFee {
		str := fmt.Sprintf("transaction %v has %d fees which is under "+
			"the required amount of %d", txHash, txFee,
			minFee)
		return txRuleError(btcwire.RejectInsufficientFee, str)
	}

	// Free-to-relay transactions are rate limited here to prevent
	// penny-flooding with tiny transactions as a form of attack.
	if rateLimit && txFee < minFee {
		nowUnix := time.Now().Unix()
		// we decay passed data with an exponentially decaying ~10
		// minutes window - matches bitcoind handling.
		mp.pennyTotal *= math.Pow(1.0-1.0/600.0,
			float64(nowUnix-mp.lastPennyUnix))
		mp.lastPennyUnix = nowUnix

		// Are we still over the limit?
		if mp.pennyTotal >= cfg.FreeTxRelayLimit*10*1000 {
			str := fmt.Sprintf("transaction %v has been rejected "+
				"by the rate limiter due to low fees", txHash)
			return txRuleError(btcwire.RejectInsufficientFee, str)
		}
		oldTotal := mp.pennyTotal

		mp.pennyTotal += float64(serializedSize)
		txmpLog.Tracef("rate limit: curTotal %v, nextTotal: %v, "+
			"limit %v", oldTotal, mp.pennyTotal,
			cfg.FreeTxRelayLimit*10*1000)
	}

	// Verify crypto signatures for each input and reject the transaction if
	// any don't verify.
	err = btcchain.ValidateTransactionScripts(tx, txStore,
		standardScriptVerifyFlags)
	if err != nil {
		if cerr, ok := err.(btcchain.RuleError); ok {
			return chainRuleError(cerr)
		}
		return err
	}

	// Add to transaction pool.
	mp.addTransaction(tx, curHeight, txFee)

	txmpLog.Debugf("Accepted transaction %v (pool size: %v)", txHash,
		len(mp.pool))

	if mp.server.rpcServer != nil {
		// Notify websocket clients about mempool transactions.
		mp.server.rpcServer.ntfnMgr.NotifyMempoolTx(tx, isNew)

		// Potentially notify any getblocktemplate long poll clients
		// about stale block templates due to the new transaction.
		mp.server.rpcServer.gbtWorkState.NotifyMempoolTx(mp.lastUpdated)
	}

	return nil
}
Exemplo n.º 26
0
// Parse transaction takes care of testing the tx for certain rules; class A/B checking
// Fundraiser checks and Dex messages as well as properly delegating the raw messages to
// the proper object which. After a full Msg has been assembled it will return the Msg
// or an error with a description. Whoever called this function should delegate the msg
// to the proper object handler.
func (mp *MsgParser) ParseTx(tx *btcutil.Tx, height, time int64) (msg *Msg, err error) {
	sender, _ := mscutil.FindSender(tx.MsgTx().TxIn, mp.server.btcdb)

	// Check if this is a Class A transaction
	if isClassA(tx) {
		mscutil.Logger.Println("Got 'Class A' transaction with hash", tx.Sha())

		highestAddress, _ := mscutil.FindSender(tx.MsgTx().TxIn, mp.server.btcdb)

		// Create a simple send transaction
		simpleSend, e := mscutil.MakeClassASimpleSend(highestAddress, mscutil.GetAddrsClassA(tx))
		err = e

		if simpleSend != nil {
			mscutil.Logger.Println("Decoded Simple Send transaction:", simpleSend, simpleSend.Data)
			msg = &Msg{msg: simpleSend}
			mscutil.Logger.Fatal("SHUTDOWN")
		}
	} else {
		mscutil.Logger.Println("Got 'Class B' tx")

		// Parse addresses from the tx using class B rules
		plainTextKeys, receiver, err := mscutil.GetAddrsClassB(tx, sender)
		if err == nil {
			// Receiver is first, data is second in the slice
			data := plainTextKeys[0][1]
			// Figure out the message type
			msgType := mscutil.GetTypeFromAddress(string(data))
			switch msgType {
			case mscutil.TxMsgTy:
				simpleSend, e := mscutil.MakeClassBSimpleSend(plainTextKeys, receiver)
				err = e

				if simpleSend != nil {
					mscutil.Logger.Println("Got simple send class b transaction")
					msg = &Msg{msg: simpleSend}
				}
			case mscutil.DexMsgTy:
			default:
				mscutil.Logger.Println("Unknown message type %d. FIXME or erroneus.\n", int(msgType))
			}
		}
	}

	// If nothing has been found it is either a Exodus Kickstarter / Payment for accept DEx message.
	if msg == nil && err != nil {

		if height <= mscutil.FundraiserEndBlock {
			// Collect the addresses and values for every input used for this transaction
			highestAddress, _ := mscutil.FindSender(tx.MsgTx().TxIn, mp.server.btcdb)

			var totalSpend int64
			for _, txOut := range tx.MsgTx().TxOut {
				addr, _ := mscutil.GetAddrs(txOut.PkScript)
				if addr[0].Addr == mscutil.ExodusAddress {
					totalSpend += txOut.Value
				}
			}

			fundraiserTx, e := mscutil.NewFundraiserTransaction(highestAddress, totalSpend, time)
			err = e

			msg = &Msg{msg: fundraiserTx}
		} else {
			//MakeDexMessage(outputs)
		}
	}

	return
}