// 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 }
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 }
// 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 }
// 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 } } } }
// 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 } } } } }
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 }
// 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()) } }
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 }
// 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) } } } }
// 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 } }
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 }
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 }
// 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 }
// 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)) }
// 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 }
// 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) } }
// 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 }
// 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 }
// 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 }
// 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 }
// 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 }
// 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 }
// 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 }
// 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 }
// 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 }
// 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 }