// RawMempoolVerbose returns all of the entries in the mempool as a fully // populated btcjson result. // // This function is safe for concurrent access. func (mp *TxPool) RawMempoolVerbose() map[string]*btcjson.GetRawMempoolVerboseResult { mp.mtx.RLock() defer mp.mtx.RUnlock() result := make(map[string]*btcjson.GetRawMempoolVerboseResult, len(mp.pool)) bestHeight := mp.cfg.BestHeight() for _, desc := range mp.pool { // Calculate the current priority based on the inputs to // the transaction. Use zero if one or more of the // input transactions can't be found for some reason. tx := desc.Tx var currentPriority float64 utxos, err := mp.fetchInputUtxos(tx) if err == nil { currentPriority = mining.CalcPriority(tx.MsgTx(), utxos, bestHeight+1) } mpd := &btcjson.GetRawMempoolVerboseResult{ Size: int32(tx.MsgTx().SerializeSize()), Vsize: int32(blockchain.GetTxVirtualSize(tx)), Fee: btcutil.Amount(desc.Fee).ToBTC(), Time: desc.Added.Unix(), Height: int64(desc.Height), StartingPriority: desc.StartingPriority, CurrentPriority: currentPriority, Depends: make([]string, 0), } for _, txIn := range tx.MsgTx().TxIn { hash := &txIn.PreviousOutPoint.Hash if mp.haveTransaction(hash) { mpd.Depends = append(mpd.Depends, hash.String()) } } result[tx.Hash().String()] = mpd } return result }
// TxToString prints out some info about a transaction. for testing / debugging func TxToString(tx *wire.MsgTx) string { str := fmt.Sprintf("size %d vsize %d wsize %d locktime %d txid %s\n", tx.SerializeSize(), blockchain.GetTxVirtualSize(btcutil.NewTx(tx)), tx.SerializeSize(), tx.LockTime, tx.TxSha().String()) for i, in := range tx.TxIn { str += fmt.Sprintf("Input %d spends %s\n", i, in.PreviousOutPoint.String()) str += fmt.Sprintf("\tSigScript: %x\n", in.SignatureScript) for j, wit := range in.Witness { str += fmt.Sprintf("\twitness %d: %x\n", j, wit) } } for i, out := range tx.TxOut { if out != nil { str += fmt.Sprintf("output %d script: %x amt: %d\n", i, out.PkScript, out.Value) } else { str += fmt.Sprintf("output %d nil (WARNING)\n", i) } } return str }
// EstFee gives a fee estimate based on a tx and a sat/Byte target. // The TX should have all outputs, including the change address already // populated (with potentially 0 amount. Also it should have all inputs // populated, but inputs don't need to have sigscripts or witnesses // (it'll guess the sizes of sigs/wits that arent' filled in). func EstFee(otx *wire.MsgTx, spB int64) int64 { mtsig := make([]byte, 72) mtpub := make([]byte, 33) tx := otx.Copy() // iterate through txins, replacing subscript sigscripts with noise // sigs or witnesses for _, txin := range tx.TxIn { // check wpkh if len(txin.SignatureScript) == 22 && txin.SignatureScript[0] == 0x00 && txin.SignatureScript[1] == 0x14 { txin.SignatureScript = nil txin.Witness = make([][]byte, 2) txin.Witness[0] = mtsig txin.Witness[1] = mtpub } else if len(txin.SignatureScript) == 34 && txin.SignatureScript[0] == 0x00 && txin.SignatureScript[1] == 0x20 { // p2wsh -- sig lenght is a total guess! txin.SignatureScript = nil txin.Witness = make([][]byte, 3) // 3 sigs? totally guessing here txin.Witness[0] = mtsig txin.Witness[1] = mtsig txin.Witness[2] = mtsig } else { // assume everything else is p2pkh. Even though it's not txin.Witness = nil txin.SignatureScript = make([]byte, 105) // len of p2pkh sigscript } } fmt.Printf(TxToString(tx)) size := int64(blockchain.GetTxVirtualSize(btcutil.NewTx(tx))) fmt.Printf("%d spB, est vsize %d, fee %d\n", spB, size, size*spB) return size * spB }
// 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 *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejectDupOrphans bool) ([]*chainhash.Hash, *TxDesc, error) { txHash := tx.Hash() // Don't accept the transaction if it already exists in the pool. This // applies to orphan transactions as well when the reject duplicate // orphans flag is set. This check is intended to be a quick check to // weed out duplicates. if mp.isTransactionInPool(txHash) || (rejectDupOrphans && mp.isOrphanInPool(txHash)) { str := fmt.Sprintf("already have transaction %v", txHash) return nil, nil, txRuleError(wire.RejectDuplicate, str) } // Perform preliminary sanity checks on the transaction. This makes // use of blockchain which contains the invariant rules for what // transactions are allowed into blocks. err := blockchain.CheckTransactionSanity(tx) if err != nil { if cerr, ok := err.(blockchain.RuleError); ok { return nil, nil, chainRuleError(cerr) } return nil, nil, err } // A standalone transaction must not be a coinbase transaction. if blockchain.IsCoinBase(tx) { str := fmt.Sprintf("transaction %v is an individual coinbase", txHash) return nil, nil, txRuleError(wire.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 nil, nil, txRuleError(wire.RejectNonstandard, str) } // Get the current height of the main chain. A standalone transaction // will be mined into the next block at best, so its height is at least // one more than the current height. bestHeight := mp.cfg.BestHeight() nextBlockHeight := bestHeight + 1 medianTimePast := mp.cfg.MedianTimePast() // Don't allow non-standard transactions if the network parameters // forbid their acceptance. if !mp.cfg.Policy.AcceptNonStd { err = checkTransactionStandard(tx, nextBlockHeight, medianTimePast, mp.cfg.Policy.MinRelayTxFee, mp.cfg.Policy.MaxTxVersion) 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 = wire.RejectNonstandard } str := fmt.Sprintf("transaction %v is not standard: %v", txHash, err) return nil, nil, 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 nil, nil, err } // Fetch all of the unspent transaction outputs 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. utxoView, err := mp.fetchInputUtxos(tx) if err != nil { if cerr, ok := err.(blockchain.RuleError); ok { return nil, nil, chainRuleError(cerr) } return nil, nil, err } // Don't allow the transaction if it exists in the main chain and is not // not already fully spent. txEntry := utxoView.LookupEntry(txHash) if txEntry != nil && !txEntry.IsFullySpent() { return nil, nil, txRuleError(wire.RejectDuplicate, "transaction already exists") } delete(utxoView.Entries(), *txHash) // Transaction is an orphan if any of the referenced input transactions // don't exist. Adding orphans to the orphan pool is not handled by // this function, and the caller should use maybeAddOrphan if this // behavior is desired. var missingParents []*chainhash.Hash for originHash, entry := range utxoView.Entries() { if entry == nil || entry.IsFullySpent() { // Must make a copy of the hash here since the iterator // is replaced and taking its address directly would // result in all of the entries pointing to the same // memory location and thus all be the final hash. hashCopy := originHash missingParents = append(missingParents, &hashCopy) } } if len(missingParents) > 0 { return missingParents, nil, nil } // Don't allow the transaction into the mempool unless its sequence // lock is active, meaning that it'll be allowed into the next block // with respect to its defined relative lock times. sequenceLock, err := mp.cfg.CalcSequenceLock(tx, utxoView) if err != nil { if cerr, ok := err.(blockchain.RuleError); ok { return nil, nil, chainRuleError(cerr) } return nil, nil, err } if !blockchain.SequenceLockActive(sequenceLock, nextBlockHeight, medianTimePast) { return nil, nil, txRuleError(wire.RejectNonstandard, "transaction's sequence locks on inputs not met") } // Perform several checks on the transaction inputs using the invariant // rules in blockchain for what transactions are allowed into blocks. // Also returns the fees associated with the transaction which will be // used later. txFee, err := blockchain.CheckTransactionInputs(tx, nextBlockHeight, utxoView, mp.cfg.ChainParams) if err != nil { if cerr, ok := err.(blockchain.RuleError); ok { return nil, nil, chainRuleError(cerr) } return nil, nil, err } // Don't allow transactions with non-standard inputs if the network // parameters forbid their acceptance. if !mp.cfg.Policy.AcceptNonStd { err := checkInputsStandard(tx, utxoView) 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 = wire.RejectNonstandard } str := fmt.Sprintf("transaction %v has a non-standard "+ "input: %v", txHash, err) return nil, nil, 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. // TODO(roasbeef): last bool should be conditional on segwit activation sigOpCost, err := blockchain.GetSigOpCost(tx, false, utxoView, true, true) if err != nil { if cerr, ok := err.(blockchain.RuleError); ok { return nil, nil, chainRuleError(cerr) } return nil, nil, err } if sigOpCost > mp.cfg.Policy.MaxSigOpCostPerTx { str := fmt.Sprintf("transaction %v sigop cost is too high: %d > %d", txHash, sigOpCost, maxStandardSigOpsCost) return nil, nil, txRuleError(wire.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 := blockchain.GetTxVirtualSize(tx) minFee := calcMinRequiredTxRelayFee(serializedSize, mp.cfg.Policy.MinRelayTxFee) 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 nil, nil, txRuleError(wire.RejectInsufficientFee, str) } // Require that free transactions have sufficient priority to be mined // in the next block. Transactions which are being added back to the // memory pool from blocks that have been disconnected during a reorg // are exempted. if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee { currentPriority := mining.CalcPriority(tx.MsgTx(), utxoView, nextBlockHeight) if currentPriority <= mining.MinHighPriority { str := fmt.Sprintf("transaction %v has insufficient "+ "priority (%g <= %g)", txHash, currentPriority, mining.MinHighPriority) return nil, nil, txRuleError(wire.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() // Decay passed data with an exponentially decaying ~10 minute // 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 >= mp.cfg.Policy.FreeTxRelayLimit*10*1000 { str := fmt.Sprintf("transaction %v has been rejected "+ "by the rate limiter due to low fees", txHash) return nil, nil, txRuleError(wire.RejectInsufficientFee, str) } oldTotal := mp.pennyTotal mp.pennyTotal += float64(serializedSize) log.Tracef("rate limit: curTotal %v, nextTotal: %v, "+ "limit %v", oldTotal, mp.pennyTotal, mp.cfg.Policy.FreeTxRelayLimit*10*1000) } // Verify crypto signatures for each input and reject the transaction if // any don't verify. err = blockchain.ValidateTransactionScripts(tx, utxoView, txscript.StandardVerifyFlags, mp.cfg.SigCache, mp.cfg.HashCache) if err != nil { if cerr, ok := err.(blockchain.RuleError); ok { return nil, nil, chainRuleError(cerr) } return nil, nil, err } // Add to transaction pool. txD := mp.addTransaction(utxoView, tx, bestHeight, txFee) log.Debugf("Accepted transaction %v (pool size: %v)", txHash, len(mp.pool)) return nil, txD, nil }