mempool: Refactor mempool code to its own package. (#737)

This does the minimum work necessary to refactor the mempool code into
its own package.  The idea is that separating this code into its own
package will greatly improve its testability, allow independent
benchmarking and profiling, and open up some interesting opportunities
for future development related to the memory pool.

There are likely some areas related to policy that could be further
refactored, however it is better to do that in future commits in order
to keep the changeset as small as possible during this refactor.

Overview of the major changes:

- Create the new package
- Move several files into the new package:
  - mempool.go -> mempool/mempool.go
  - mempoolerror.go -> mempool/error.go
  - policy.go -> mempool/policy.go
  - policy_test.go -> mempool/policy_test.go
- Update mempool logging to use the new mempool package logger
- Rename mempoolPolicy to Policy (so it's now mempool.Policy)
- Rename mempoolConfig to Config (so it's now mempool.Config)
- Rename mempoolTxDesc to TxDesc (so it's now mempool.TxDesc)
- Rename txMemPool to TxPool (so it's now mempool.TxPool)
- Move defaultBlockPrioritySize to the new package and export it
- Export DefaultMinRelayTxFee from the mempool package
- Export the CalcPriority function from the mempool package
- Introduce a new RawMempoolVerbose function on the TxPool and update
  the RPC server to use it
- Update all references to the mempool to use the package.
- Add a skeleton README.md
This commit is contained in:
Dave Collins 2016-08-19 11:08:37 -05:00 committed by GitHub
parent 87b3756c8c
commit 7fac099bee
12 changed files with 229 additions and 149 deletions

View file

@ -17,6 +17,7 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/database"
"github.com/btcsuite/btcd/mempool"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
@ -475,7 +476,7 @@ func (b *blockManager) handleTxMsg(tmsg *txMsg) {
// simply rejected as opposed to something actually going wrong,
// so log it as such. Otherwise, something really did go wrong,
// so log it as an actual error.
if _, ok := err.(RuleError); ok {
if _, ok := err.(mempool.RuleError); ok {
bmgrLog.Debugf("Rejected transaction %v from %s: %v",
txHash, tmsg.peer, err)
} else {
@ -485,7 +486,7 @@ func (b *blockManager) handleTxMsg(tmsg *txMsg) {
// Convert the error into an appropriate reject message and
// send it.
code, reason := errToRejectErr(err)
code, reason := mempool.ErrToRejectErr(err)
tmsg.peer.PushRejectMsg(wire.CmdTx, code, reason, txHash,
false)
return
@ -585,7 +586,7 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
// Convert the error into an appropriate reject message and
// send it.
code, reason := errToRejectErr(err)
code, reason := mempool.ErrToRejectErr(err)
bmsg.peer.PushRejectMsg(wire.CmdBlock, code, reason,
blockHash, false)
return

View file

@ -22,6 +22,7 @@ import (
"github.com/btcsuite/btcd/database"
_ "github.com/btcsuite/btcd/database/ffldb"
"github.com/btcsuite/btcd/mempool"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
flags "github.com/btcsuite/go-flags"
@ -46,7 +47,6 @@ const (
defaultBlockMaxSize = 750000
blockMaxSizeMin = 1000
blockMaxSizeMax = wire.MaxBlockPayload - 1000
defaultBlockPrioritySize = 50000
defaultGenerate = false
defaultMaxOrphanTransactions = 1000
defaultMaxOrphanTxSize = 5000
@ -344,11 +344,11 @@ func loadConfig() (*config, []string, error) {
DbType: defaultDbType,
RPCKey: defaultRPCKeyFile,
RPCCert: defaultRPCCertFile,
MinRelayTxFee: defaultMinRelayTxFee.ToBTC(),
MinRelayTxFee: mempool.DefaultMinRelayTxFee.ToBTC(),
FreeTxRelayLimit: defaultFreeTxRelayLimit,
BlockMinSize: defaultBlockMinSize,
BlockMaxSize: defaultBlockMaxSize,
BlockPrioritySize: defaultBlockPrioritySize,
BlockPrioritySize: mempool.DefaultBlockPrioritySize,
MaxOrphanTxs: defaultMaxOrphanTransactions,
SigCacheMaxSize: defaultSigCacheMaxSize,
Generate: defaultGenerate,

2
log.go
View file

@ -12,6 +12,7 @@ import (
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/blockchain/indexers"
"github.com/btcsuite/btcd/database"
"github.com/btcsuite/btcd/mempool"
"github.com/btcsuite/btcd/peer"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btclog"
@ -136,6 +137,7 @@ func useLogger(subsystemID string, logger btclog.Logger) {
case "TXMP":
txmpLog = logger
mempool.UseLogger(logger)
}
}

23
mempool/README.md Normal file
View file

@ -0,0 +1,23 @@
mempool
=======
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)]
(https://travis-ci.org/btcsuite/btcd) [![ISC License]
(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)]
(http://godoc.org/github.com/btcsuite/btcd/mempool)
## Overview
This package is currently a work in progress.
## Installation and Updating
```bash
$ go get -u github.com/btcsuite/btcd/mempool
```
## License
Package mempool is licensed under the [copyfree](http://copyfree.org) ISC
License.

View file

@ -1,8 +1,8 @@
// Copyright (c) 2014 The btcsuite developers
// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
package mempool
import (
"github.com/btcsuite/btcd/blockchain"
@ -109,9 +109,9 @@ func extractRejectCode(err error) (wire.RejectCode, bool) {
return wire.RejectInvalid, false
}
// errToRejectErr examines the underlying type of the error and returns a reject
// ErrToRejectErr examines the underlying type of the error and returns a reject
// code and string appropriate to be sent in a wire.MsgReject message.
func errToRejectErr(err error) (wire.RejectCode, string) {
func ErrToRejectErr(err error) (wire.RejectCode, string) {
// Return the reject code along with the error text if it can be
// extracted from the error.
rejectCode, found := extractRejectCode(err)

32
mempool/log.go Normal file
View file

@ -0,0 +1,32 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package mempool
import (
"github.com/btcsuite/btclog"
)
// log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller
// requests it.
var log btclog.Logger
// The default amount of logging is none.
func init() {
DisableLog()
}
// DisableLog disables all library log output. Logging output is disabled
// by default until either UseLogger or SetLogWriter are called.
func DisableLog() {
log = btclog.Disabled
}
// UseLogger uses a specified Logger to output package logging info.
// This should be used in preference to SetLogWriter if the caller is also
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
package mempool
import (
"container/list"
@ -16,6 +16,8 @@ import (
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/blockchain/indexers"
"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/mining"
"github.com/btcsuite/btcd/txscript"
@ -24,26 +26,30 @@ import (
)
const (
// DefaultBlockPrioritySize is the default size in bytes for high-
// priority / low-fee transactions. It is used to help determine which
// are allowed into the mempool and consequently affects their relay and
// inclusion when generating block templates.
DefaultBlockPrioritySize = 50000
// MinHighPriority is the minimum priority value that allows a
// transaction to be considered high priority.
MinHighPriority = btcutil.SatoshiPerBitcoin * 144.0 / 250
// mempoolHeight is the height used for the "block" height field of the
// contextual transaction information provided in a transaction view.
mempoolHeight = 0x7fffffff
)
// mempoolTxDesc is a descriptor containing a transaction in the mempool along
// with additional metadata.
type mempoolTxDesc struct {
mining.TxDesc
// StartingPriority is the priority of the transaction when it was added
// to the pool.
StartingPriority float64
}
// mempoolConfig is a descriptor containing the memory pool configuration.
type mempoolConfig struct {
// Config is a descriptor containing the memory pool configuration.
type Config struct {
// Policy defines the various mempool configuration options related
// to policy.
Policy mempoolPolicy
Policy Policy
// ChainParams identifies which chain parameters the txpool is
// associated with.
ChainParams *chaincfg.Params
// FetchUtxoView defines the function to use to fetch unspent
// transaction output information.
@ -65,9 +71,9 @@ type mempoolConfig struct {
AddrIndex *indexers.AddrIndex
}
// mempoolPolicy houses the policy (configuration parameters) which is used to
// Policy houses the policy (configuration parameters) which is used to
// control the mempool.
type mempoolPolicy struct {
type Policy struct {
// DisableRelayPriority defines whether to relay free or low-fee
// transactions that do not have enough priority to be relayed.
DisableRelayPriority bool
@ -95,16 +101,26 @@ type mempoolPolicy struct {
MinRelayTxFee btcutil.Amount
}
// txMemPool is used as a source of transactions that need to be mined into
// blocks and relayed to other peers. It is safe for concurrent access from
// multiple peers.
type txMemPool struct {
// TxDesc is a descriptor containing a transaction in the mempool along with
// additional metadata.
type TxDesc struct {
mining.TxDesc
// StartingPriority is the priority of the transaction when it was added
// to the pool.
StartingPriority float64
}
// TxPool is used as a source of transactions that need to be mined into blocks
// and relayed to other peers. It is safe for concurrent access from multiple
// peers.
type TxPool struct {
// The following variables must only be used atomically.
lastUpdated int64 // last time pool was updated
sync.RWMutex
cfg mempoolConfig
pool map[chainhash.Hash]*mempoolTxDesc
cfg Config
pool map[chainhash.Hash]*TxDesc
orphans map[chainhash.Hash]*btcutil.Tx
orphansByPrev map[chainhash.Hash]map[chainhash.Hash]*btcutil.Tx
outpoints map[wire.OutPoint]*btcutil.Tx
@ -112,14 +128,14 @@ type txMemPool struct {
lastPennyUnix int64 // unix time of last ``penny spend''
}
// Ensure the txMemPool type implements the mining.TxSource interface.
var _ mining.TxSource = (*txMemPool)(nil)
// Ensure the TxPool type implements the mining.TxSource interface.
var _ mining.TxSource = (*TxPool)(nil)
// removeOrphan is the internal function which implements the public
// RemoveOrphan. See the comment for RemoveOrphan for more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) removeOrphan(txHash *chainhash.Hash) {
func (mp *TxPool) removeOrphan(txHash *chainhash.Hash) {
// Nothing to do if passed tx is not an orphan.
tx, exists := mp.orphans[*txHash]
if !exists {
@ -148,7 +164,7 @@ func (mp *txMemPool) removeOrphan(txHash *chainhash.Hash) {
// previous orphan index.
//
// This function is safe for concurrent access.
func (mp *txMemPool) RemoveOrphan(txHash *chainhash.Hash) {
func (mp *TxPool) RemoveOrphan(txHash *chainhash.Hash) {
mp.Lock()
mp.removeOrphan(txHash)
mp.Unlock()
@ -158,7 +174,7 @@ func (mp *txMemPool) RemoveOrphan(txHash *chainhash.Hash) {
// orphan if adding a new one would cause it to overflow the max allowed.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) limitNumOrphans() error {
func (mp *TxPool) limitNumOrphans() error {
if len(mp.orphans)+1 > mp.cfg.Policy.MaxOrphanTxs &&
mp.cfg.Policy.MaxOrphanTxs > 0 {
@ -196,7 +212,7 @@ func (mp *txMemPool) limitNumOrphans() error {
// 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) {
func (mp *TxPool) 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()
@ -211,14 +227,14 @@ func (mp *txMemPool) addOrphan(tx *btcutil.Tx) {
mp.orphansByPrev[originTxHash][*tx.Hash()] = tx
}
txmpLog.Debugf("Stored orphan transaction %v (total: %d)", tx.Hash(),
log.Debugf("Stored orphan transaction %v (total: %d)", tx.Hash(),
len(mp.orphans))
}
// maybeAddOrphan potentially adds an orphan to the orphan pool.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) maybeAddOrphan(tx *btcutil.Tx) error {
func (mp *TxPool) maybeAddOrphan(tx *btcutil.Tx) error {
// Ignore orphan transactions that are too large. This helps avoid
// a memory exhaustion attack based on sending a lot of really large
// orphans. In the case there is a valid transaction larger than this,
@ -247,7 +263,7 @@ func (mp *txMemPool) maybeAddOrphan(tx *btcutil.Tx) error {
// exists in the main pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) isTransactionInPool(hash *chainhash.Hash) bool {
func (mp *TxPool) isTransactionInPool(hash *chainhash.Hash) bool {
if _, exists := mp.pool[*hash]; exists {
return true
}
@ -259,7 +275,7 @@ func (mp *txMemPool) isTransactionInPool(hash *chainhash.Hash) bool {
// exists in the main pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) IsTransactionInPool(hash *chainhash.Hash) bool {
func (mp *TxPool) IsTransactionInPool(hash *chainhash.Hash) bool {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
@ -271,7 +287,7 @@ func (mp *txMemPool) IsTransactionInPool(hash *chainhash.Hash) bool {
// in the orphan pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) isOrphanInPool(hash *chainhash.Hash) bool {
func (mp *TxPool) isOrphanInPool(hash *chainhash.Hash) bool {
if _, exists := mp.orphans[*hash]; exists {
return true
}
@ -283,7 +299,7 @@ func (mp *txMemPool) isOrphanInPool(hash *chainhash.Hash) bool {
// in the orphan pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) IsOrphanInPool(hash *chainhash.Hash) bool {
func (mp *TxPool) IsOrphanInPool(hash *chainhash.Hash) bool {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
@ -295,7 +311,7 @@ func (mp *txMemPool) IsOrphanInPool(hash *chainhash.Hash) bool {
// in the main pool or in the orphan pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) haveTransaction(hash *chainhash.Hash) bool {
func (mp *TxPool) haveTransaction(hash *chainhash.Hash) bool {
return mp.isTransactionInPool(hash) || mp.isOrphanInPool(hash)
}
@ -303,7 +319,7 @@ func (mp *txMemPool) haveTransaction(hash *chainhash.Hash) bool {
// in the main pool or in the orphan pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) HaveTransaction(hash *chainhash.Hash) bool {
func (mp *TxPool) HaveTransaction(hash *chainhash.Hash) bool {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
@ -315,7 +331,7 @@ func (mp *txMemPool) HaveTransaction(hash *chainhash.Hash) bool {
// 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, removeRedeemers bool) {
func (mp *TxPool) removeTransaction(tx *btcutil.Tx, removeRedeemers bool) {
txHash := tx.Hash()
if removeRedeemers {
// Remove any transactions which rely on this one.
@ -350,7 +366,7 @@ func (mp *txMemPool) removeTransaction(tx *btcutil.Tx, removeRedeemers bool) {
// they would otherwise become orphans.
//
// This function is safe for concurrent access.
func (mp *txMemPool) RemoveTransaction(tx *btcutil.Tx, removeRedeemers bool) {
func (mp *TxPool) RemoveTransaction(tx *btcutil.Tx, removeRedeemers bool) {
// Protect concurrent access.
mp.Lock()
defer mp.Unlock()
@ -365,7 +381,7 @@ func (mp *txMemPool) RemoveTransaction(tx *btcutil.Tx, removeRedeemers bool) {
// contain transactions which were previously unknown to the memory pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) RemoveDoubleSpends(tx *btcutil.Tx) {
func (mp *TxPool) RemoveDoubleSpends(tx *btcutil.Tx) {
// Protect concurrent access.
mp.Lock()
defer mp.Unlock()
@ -384,17 +400,17 @@ func (mp *txMemPool) RemoveDoubleSpends(tx *btcutil.Tx) {
// helper for maybeAcceptTransaction.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil.Tx, height int32, fee int64) {
func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil.Tx, height int32, fee int64) {
// Add the transaction to the pool and mark the referenced outpoints
// as spent by the pool.
mp.pool[*tx.Hash()] = &mempoolTxDesc{
mp.pool[*tx.Hash()] = &TxDesc{
TxDesc: mining.TxDesc{
Tx: tx,
Added: time.Now(),
Height: height,
Fee: fee,
},
StartingPriority: calcPriority(tx.MsgTx(), utxoView, height),
StartingPriority: CalcPriority(tx.MsgTx(), utxoView, height),
}
for _, txIn := range tx.MsgTx().TxIn {
mp.outpoints[txIn.PreviousOutPoint] = tx
@ -414,7 +430,7 @@ func (mp *txMemPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcu
// main chain.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) checkPoolDoubleSpend(tx *btcutil.Tx) error {
func (mp *TxPool) checkPoolDoubleSpend(tx *btcutil.Tx) error {
for _, txIn := range tx.MsgTx().TxIn {
if txR, exists := mp.outpoints[txIn.PreviousOutPoint]; exists {
str := fmt.Sprintf("output %v already spent by "+
@ -433,7 +449,7 @@ func (mp *txMemPool) checkPoolDoubleSpend(tx *btcutil.Tx) error {
// transaction pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) fetchInputUtxos(tx *btcutil.Tx) (*blockchain.UtxoViewpoint, error) {
func (mp *TxPool) fetchInputUtxos(tx *btcutil.Tx) (*blockchain.UtxoViewpoint, error) {
utxoView, err := mp.cfg.FetchUtxoView(tx)
if err != nil {
return nil, err
@ -457,7 +473,7 @@ func (mp *txMemPool) fetchInputUtxos(tx *btcutil.Tx) (*blockchain.UtxoViewpoint,
// orphans.
//
// This function is safe for concurrent access.
func (mp *txMemPool) FetchTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) {
func (mp *TxPool) FetchTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
@ -474,7 +490,7 @@ func (mp *txMemPool) FetchTransaction(txHash *chainhash.Hash) (*btcutil.Tx, erro
// more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit bool) ([]*chainhash.Hash, error) {
func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit bool) ([]*chainhash.Hash, error) {
txHash := tx.Hash()
// Don't accept the transaction if it already exists in the pool. This
@ -521,7 +537,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
// Don't allow non-standard transactions if the network parameters
// forbid their relaying.
if !activeNetParams.RelayNonStdTxs {
if !mp.cfg.ChainParams.RelayNonStdTxs {
err := checkTransactionStandard(tx, nextBlockHeight,
mp.cfg.TimeSource, mp.cfg.Policy.MinRelayTxFee)
if err != nil {
@ -596,7 +612,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
// Also returns the fees associated with the transaction which will be
// used later.
txFee, err := blockchain.CheckTransactionInputs(tx, nextBlockHeight,
utxoView, activeNetParams.Params)
utxoView, mp.cfg.ChainParams)
if err != nil {
if cerr, ok := err.(blockchain.RuleError); ok {
return nil, chainRuleError(cerr)
@ -606,7 +622,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
// Don't allow transactions with non-standard inputs if the network
// parameters forbid their relaying.
if !activeNetParams.RelayNonStdTxs {
if !mp.cfg.ChainParams.RelayNonStdTxs {
err := checkInputsStandard(tx, utxoView)
if err != nil {
// Attempt to extract a reject code from the error so
@ -659,7 +675,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
serializedSize := int64(tx.MsgTx().SerializeSize())
minFee := calcMinRequiredTxRelayFee(serializedSize,
mp.cfg.Policy.MinRelayTxFee)
if serializedSize >= (defaultBlockPrioritySize-1000) && txFee < minFee {
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)
@ -671,12 +687,12 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
// memory pool from blocks that have been disconnected during a reorg
// are exempted.
if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee {
currentPriority := calcPriority(tx.MsgTx(), utxoView,
currentPriority := CalcPriority(tx.MsgTx(), utxoView,
nextBlockHeight)
if currentPriority <= minHighPriority {
if currentPriority <= MinHighPriority {
str := fmt.Sprintf("transaction %v has insufficient "+
"priority (%g <= %g)", txHash,
currentPriority, minHighPriority)
currentPriority, MinHighPriority)
return nil, txRuleError(wire.RejectInsufficientFee, str)
}
}
@ -685,8 +701,8 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
// 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.
// 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
@ -700,7 +716,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
oldTotal := mp.pennyTotal
mp.pennyTotal += float64(serializedSize)
txmpLog.Tracef("rate limit: curTotal %v, nextTotal: %v, "+
log.Tracef("rate limit: curTotal %v, nextTotal: %v, "+
"limit %v", oldTotal, mp.pennyTotal,
mp.cfg.Policy.FreeTxRelayLimit*10*1000)
}
@ -719,7 +735,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
// Add to transaction pool.
mp.addTransaction(utxoView, tx, best.Height, txFee)
txmpLog.Debugf("Accepted transaction %v (pool size: %v)", txHash,
log.Debugf("Accepted transaction %v (pool size: %v)", txHash,
len(mp.pool))
return nil, nil
@ -736,7 +752,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
// be added to the orphan pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) MaybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit bool) ([]*chainhash.Hash, error) {
func (mp *TxPool) MaybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit bool) ([]*chainhash.Hash, error) {
// Protect concurrent access.
mp.Lock()
defer mp.Unlock()
@ -748,7 +764,7 @@ func (mp *txMemPool) MaybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
// ProcessOrphans. See the comment for ProcessOrphans for more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) processOrphans(hash *chainhash.Hash) []*btcutil.Tx {
func (mp *TxPool) processOrphans(hash *chainhash.Hash) []*btcutil.Tx {
var acceptedTxns []*btcutil.Tx
// Start with processing at least the passed hash.
@ -793,9 +809,8 @@ func (mp *txMemPool) processOrphans(hash *chainhash.Hash) []*btcutil.Tx {
if err != nil {
// TODO: Remove orphans that depend on this
// failed transaction.
txmpLog.Debugf("Unable to move "+
"orphan transaction %v to mempool: %v",
tx.Hash(), err)
log.Debugf("Unable to move orphan transaction "+
"%v to mempool: %v", tx.Hash(), err)
continue
}
@ -840,7 +855,7 @@ func (mp *txMemPool) processOrphans(hash *chainhash.Hash) []*btcutil.Tx {
// no transactions were moved from the orphan pool to the mempool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) ProcessOrphans(hash *chainhash.Hash) []*btcutil.Tx {
func (mp *TxPool) ProcessOrphans(hash *chainhash.Hash) []*btcutil.Tx {
mp.Lock()
acceptedTxns := mp.processOrphans(hash)
mp.Unlock()
@ -859,12 +874,12 @@ func (mp *txMemPool) ProcessOrphans(hash *chainhash.Hash) []*btcutil.Tx {
// the passed one being accepted.
//
// This function is safe for concurrent access.
func (mp *txMemPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool) ([]*btcutil.Tx, error) {
func (mp *TxPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit bool) ([]*btcutil.Tx, error) {
// Protect concurrent access.
mp.Lock()
defer mp.Unlock()
txmpLog.Tracef("Processing transaction %v", tx.Hash())
log.Tracef("Processing transaction %v", tx.Hash())
// Potentially accept the transaction to the memory pool.
missingParents, err := mp.maybeAcceptTransaction(tx, true, rateLimit)
@ -919,7 +934,7 @@ func (mp *txMemPool) ProcessTransaction(tx *btcutil.Tx, allowOrphan, rateLimit b
// include the orphan pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) Count() int {
func (mp *TxPool) Count() int {
mp.RLock()
defer mp.RUnlock()
@ -930,7 +945,7 @@ func (mp *txMemPool) Count() int {
// pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) TxHashes() []*chainhash.Hash {
func (mp *TxPool) TxHashes() []*chainhash.Hash {
mp.RLock()
defer mp.RUnlock()
@ -949,11 +964,11 @@ func (mp *txMemPool) TxHashes() []*chainhash.Hash {
// The descriptors are to be treated as read only.
//
// This function is safe for concurrent access.
func (mp *txMemPool) TxDescs() []*mempoolTxDesc {
func (mp *TxPool) TxDescs() []*TxDesc {
mp.RLock()
defer mp.RUnlock()
descs := make([]*mempoolTxDesc, len(mp.pool))
descs := make([]*TxDesc, len(mp.pool))
i := 0
for _, desc := range mp.pool {
descs[i] = desc
@ -968,7 +983,7 @@ func (mp *txMemPool) TxDescs() []*mempoolTxDesc {
//
// This is part of the mining.TxSource interface implementation and is safe for
// concurrent access as required by the interface contract.
func (mp *txMemPool) MiningDescs() []*mining.TxDesc {
func (mp *TxPool) MiningDescs() []*mining.TxDesc {
mp.RLock()
defer mp.RUnlock()
@ -982,23 +997,69 @@ func (mp *txMemPool) MiningDescs() []*mining.TxDesc {
return descs
}
// 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.RLock()
defer mp.RUnlock()
result := make(map[string]*btcjson.GetRawMempoolVerboseResult,
len(mp.pool))
best := mp.cfg.Chain.BestSnapshot()
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 = CalcPriority(tx.MsgTx(), utxos,
best.Height+1)
}
mpd := &btcjson.GetRawMempoolVerboseResult{
Size: int32(tx.MsgTx().SerializeSize()),
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
}
// LastUpdated returns the last time a transaction was added to or removed from
// the main pool. It does not include the orphan pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) LastUpdated() time.Time {
func (mp *TxPool) LastUpdated() time.Time {
return time.Unix(atomic.LoadInt64(&mp.lastUpdated), 0)
}
// newTxMemPool returns a new memory pool for validating and storing standalone
// New returns a new memory pool for validating and storing standalone
// transactions until they are mined into a block.
func newTxMemPool(cfg *mempoolConfig) *txMemPool {
memPool := &txMemPool{
func New(cfg *Config) *TxPool {
return &TxPool{
cfg: *cfg,
pool: make(map[chainhash.Hash]*mempoolTxDesc),
pool: make(map[chainhash.Hash]*TxDesc),
orphans: make(map[chainhash.Hash]*btcutil.Tx),
orphansByPrev: make(map[chainhash.Hash]map[chainhash.Hash]*btcutil.Tx),
outpoints: make(map[wire.OutPoint]*btcutil.Tx),
}
return memPool
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
package mempool
import (
"fmt"
@ -38,12 +38,12 @@ const (
// (1 + 15*74 + 3) + (15*34 + 3) + 23 = 1650
maxStandardSigScriptSize = 1650
// defaultMinRelayTxFee is the minimum fee in satoshi that is required
// DefaultMinRelayTxFee is the minimum fee in satoshi that is required
// for a transaction to be treated as free for relay and mining
// purposes. It is also used to help determine if a transaction is
// considered dust and as a base for calculating minimum required fees
// for larger transactions. This value is in Satoshi/1000 bytes.
defaultMinRelayTxFee = btcutil.Amount(1000)
DefaultMinRelayTxFee = btcutil.Amount(1000)
// maxStandardMultiSigKeys is the maximum number of public keys allowed
// in a multi-signature transaction output script for it to be
@ -75,11 +75,11 @@ func calcMinRequiredTxRelayFee(serializedSize int64, minRelayTxFee btcutil.Amoun
return minFee
}
// calcPriority returns a transaction priority given a transaction and the sum
// CalcPriority returns a transaction priority given a transaction and the sum
// of each of its input values multiplied by their age (# of confirmations).
// Thus, the final formula for the priority is:
// sum(inputValue * inputAge) / adjustedTxSize
func calcPriority(tx *wire.MsgTx, utxoView *blockchain.UtxoViewpoint, nextBlockHeight int32) float64 {
func CalcPriority(tx *wire.MsgTx, utxoView *blockchain.UtxoViewpoint, nextBlockHeight int32) float64 {
// In order to encourage spending multiple old unspent transaction
// outputs thereby reducing the total set, don't count the constant
// overhead for each input as well as enough bytes of the signature

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
package mempool
import (
"bytes"
@ -36,13 +36,13 @@ func TestCalcMinRequiredTxRelayFee(t *testing.T) {
{
"100 bytes with default minimum relay fee",
100,
defaultMinRelayTxFee,
DefaultMinRelayTxFee,
100,
},
{
"max standard tx size with default minimum relay fee",
maxStandardTxSize,
defaultMinRelayTxFee,
DefaultMinRelayTxFee,
100000,
},
{
@ -470,7 +470,7 @@ func TestCheckTransactionStandard(t *testing.T) {
for _, test := range tests {
// Ensure standardness is as expected.
err := checkTransactionStandard(btcutil.NewTx(&test.tx),
test.height, timeSource, defaultMinRelayTxFee)
test.height, timeSource, DefaultMinRelayTxFee)
if err == nil && test.isStandard {
// Test passes since function returned standard for a
// transaction which is intended to be standard.

View file

@ -12,6 +12,7 @@ import (
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/mempool"
"github.com/btcsuite/btcd/mining"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
@ -27,10 +28,6 @@ const (
// for the updated version.
generatedBlockVersion = 4
// minHighPriority is the minimum priority value that allows a
// transaction to be considered high priority.
minHighPriority = btcutil.SatoshiPerBitcoin * 144.0 / 250
// blockHeaderOverhead is the max number of bytes it takes to serialize
// a block header and max possible transaction count.
blockHeaderOverhead = wire.MaxBlockHeaderPayload + wire.MaxVarIntPayload
@ -522,7 +519,7 @@ mempoolLoop:
// Calculate the final transaction priority using the input
// value age sum as well as the adjusted transaction size. The
// formula is: sum(inputValue * inputAge) / adjustedTxSize
prioItem.priority = calcPriority(tx.MsgTx(), utxos,
prioItem.priority = mempool.CalcPriority(tx.MsgTx(), utxos,
nextBlockHeight)
// Calculate the fee in Satoshi/kB.
@ -623,13 +620,13 @@ mempoolLoop:
// the priority size or there are no more high-priority
// transactions.
if !sortedByFee && (blockPlusTxSize >= policy.BlockPrioritySize ||
prioItem.priority <= minHighPriority) {
prioItem.priority <= mempool.MinHighPriority) {
minrLog.Tracef("Switching to sort by fees per "+
"kilobyte blockSize %d >= BlockPrioritySize "+
"%d || priority %.2f <= minHighPriority %.2f",
blockPlusTxSize, policy.BlockPrioritySize,
prioItem.priority, minHighPriority)
prioItem.priority, mempool.MinHighPriority)
sortedByFee = true
priorityQueue.SetLessFunc(txPQByFee)
@ -641,7 +638,7 @@ mempoolLoop:
// final one in the high-priority section, so just fall
// though to the code below so it is added now.
if blockPlusTxSize > policy.BlockPrioritySize ||
prioItem.priority < minHighPriority {
prioItem.priority < mempool.MinHighPriority {
heap.Push(priorityQueue, prioItem)
continue

View file

@ -33,6 +33,7 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/database"
"github.com/btcsuite/btcd/mempool"
"github.com/btcsuite/btcd/mining"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
@ -2227,53 +2228,14 @@ func handleGetPeerInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{})
func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.GetRawMempoolCmd)
mp := s.server.txMemPool
descs := mp.TxDescs()
if c.Verbose != nil && *c.Verbose {
result := make(map[string]*btcjson.GetRawMempoolVerboseResult,
len(descs))
best := s.chain.BestSnapshot()
mp.RLock()
defer mp.RUnlock()
for _, desc := range descs {
// 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 = calcPriority(tx.MsgTx(),
utxos, best.Height+1)
}
mpd := &btcjson.GetRawMempoolVerboseResult{
Size: int32(tx.MsgTx().SerializeSize()),
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 s.server.txMemPool.haveTransaction(hash) {
mpd.Depends = append(mpd.Depends,
hash.String())
}
}
result[tx.Hash().String()] = mpd
}
return result, nil
return mp.RawMempoolVerbose(), nil
}
// The response is simply an array of the transaction hashes if the
// verbose flag is not set.
descs := mp.TxDescs()
hashStrings := make([]string, len(descs))
for i := range hashStrings {
hashStrings[i] = descs[i].Tx.Hash().String()
@ -3452,7 +3414,7 @@ func handleSendRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan st
// so log it as an actual error. In both cases, a JSON-RPC
// error is returned to the client with the deserialization
// error code (to match bitcoind behavior).
if _, ok := err.(RuleError); ok {
if _, ok := err.(mempool.RuleError); ok {
rpcsLog.Debugf("Rejected transaction %v: %v", tx.Hash(),
err)
} else {

View file

@ -26,6 +26,7 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/database"
"github.com/btcsuite/btcd/mempool"
"github.com/btcsuite/btcd/mining"
"github.com/btcsuite/btcd/peer"
"github.com/btcsuite/btcd/txscript"
@ -184,7 +185,7 @@ type server struct {
sigCache *txscript.SigCache
rpcServer *rpcServer
blockManager *blockManager
txMemPool *txMemPool
txMemPool *mempool.TxPool
cpuMiner *CPUMiner
modifyRebroadcastInv chan interface{}
pendingPeers chan *serverPeer
@ -2515,8 +2516,8 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
}
s.blockManager = bm
txC := mempoolConfig{
Policy: mempoolPolicy{
txC := mempool.Config{
Policy: mempool.Policy{
DisableRelayPriority: cfg.NoRelayPriority,
FreeTxRelayLimit: cfg.FreeTxRelayLimit,
MaxOrphanTxs: cfg.MaxOrphanTxs,
@ -2524,13 +2525,14 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
MaxSigOpsPerTx: blockchain.MaxSigOpsPerBlock / 5,
MinRelayTxFee: cfg.minRelayTxFee,
},
ChainParams: chainParams,
FetchUtxoView: s.blockManager.chain.FetchUtxoView,
Chain: s.blockManager.chain,
SigCache: s.sigCache,
TimeSource: s.timeSource,
AddrIndex: s.addrIndex,
}
s.txMemPool = newTxMemPool(&txC)
s.txMemPool = mempool.New(&txC)
// Create the mining policy based on the configuration options.
// NOTE: The CPU miner relies on the mempool, so the mempool has to be