mining/mempool: Move priority code to mining pkg.

This moves the priority-related code from the mempool package to the
mining package and also exports a new constant named UnminedHeight which
takes the place of the old unexported mempoolHeight.

Even though the mempool makes use of the priority code to make decisions
about what it will accept, priority really has to do with mining since
it influences which transactions will end up into a block.  This change
also has the side effect of being a step towards enabling separation of
the mining code into its own package which, as previously mentioned,
needs access to the priority calculation code as well.

Finally, the mempoolHeight variable was poorly named since what it
really represents is a transaction that has not been mined into a block
yet.  Renaming the variable to more accurately reflect its purpose makes
it clear that it belongs in the mining package which also needs the
definition now as well since the priority calculation code relies on it.
This will also benefit an outstanding PR which needs access to the same
value.
This commit is contained in:
Dave Collins 2016-10-25 22:48:57 -05:00
parent c2fb0cb18e
commit 2cfc6478ce
No known key found for this signature in database
GPG key ID: B8904D9D9C93D1F2
4 changed files with 103 additions and 96 deletions

View file

@ -34,10 +34,6 @@ const (
// transaction to be considered high priority. // transaction to be considered high priority.
MinHighPriority = btcutil.SatoshiPerBitcoin * 144.0 / 250 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
// orphanTTL is the maximum amount of time an orphan is allowed to // orphanTTL is the maximum amount of time an orphan is allowed to
// stay in the orphan pool before it expires and is evicted during the // stay in the orphan pool before it expires and is evicted during the
// next scan. // next scan.
@ -489,7 +485,7 @@ func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil
Height: height, Height: height,
Fee: fee, Fee: fee,
}, },
StartingPriority: CalcPriority(tx.MsgTx(), utxoView, height), StartingPriority: mining.CalcPriority(tx.MsgTx(), utxoView, height),
} }
for _, txIn := range tx.MsgTx().TxIn { for _, txIn := range tx.MsgTx().TxIn {
mp.outpoints[txIn.PreviousOutPoint] = tx mp.outpoints[txIn.PreviousOutPoint] = tx
@ -541,7 +537,7 @@ func (mp *TxPool) fetchInputUtxos(tx *btcutil.Tx) (*blockchain.UtxoViewpoint, er
} }
if poolTxDesc, exists := mp.pool[originHash]; exists { if poolTxDesc, exists := mp.pool[originHash]; exists {
utxoView.AddTxOuts(poolTxDesc.Tx, mempoolHeight) utxoView.AddTxOuts(poolTxDesc.Tx, mining.UnminedHeight)
} }
} }
return utxoView, nil return utxoView, nil
@ -771,7 +767,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
// memory pool from blocks that have been disconnected during a reorg // memory pool from blocks that have been disconnected during a reorg
// are exempted. // are exempted.
if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee { if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee {
currentPriority := CalcPriority(tx.MsgTx(), utxoView, currentPriority := mining.CalcPriority(tx.MsgTx(), utxoView,
nextBlockHeight) nextBlockHeight)
if currentPriority <= MinHighPriority { if currentPriority <= MinHighPriority {
str := fmt.Sprintf("transaction %v has insufficient "+ str := fmt.Sprintf("transaction %v has insufficient "+
@ -1099,7 +1095,7 @@ func (mp *TxPool) RawMempoolVerbose() map[string]*btcjson.GetRawMempoolVerboseRe
var currentPriority float64 var currentPriority float64
utxos, err := mp.fetchInputUtxos(tx) utxos, err := mp.fetchInputUtxos(tx)
if err == nil { if err == nil {
currentPriority = CalcPriority(tx.MsgTx(), utxos, currentPriority = mining.CalcPriority(tx.MsgTx(), utxos,
bestHeight+1) bestHeight+1)
} }

View file

@ -80,82 +80,6 @@ func calcMinRequiredTxRelayFee(serializedSize int64, minRelayTxFee btcutil.Amoun
return minFee return minFee
} }
// 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 {
// 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
// script to cover a pay-to-script-hash redemption with a compressed
// pubkey. This makes additional inputs free by boosting the priority
// of the transaction accordingly. No more incentive is given to avoid
// encouraging gaming future transactions through the use of junk
// outputs. This is the same logic used in the reference
// implementation.
//
// The constant overhead for a txin is 41 bytes since the previous
// outpoint is 36 bytes + 4 bytes for the sequence + 1 byte the
// signature script length.
//
// A compressed pubkey pay-to-script-hash redemption with a maximum len
// signature is of the form:
// [OP_DATA_73 <73-byte sig> + OP_DATA_35 + {OP_DATA_33
// <33 byte compresed pubkey> + OP_CHECKSIG}]
//
// Thus 1 + 73 + 1 + 1 + 33 + 1 = 110
overhead := 0
for _, txIn := range tx.TxIn {
// Max inputs + size can't possibly overflow here.
overhead += 41 + minInt(110, len(txIn.SignatureScript))
}
serializedTxSize := tx.SerializeSize()
if overhead >= serializedTxSize {
return 0.0
}
inputValueAge := calcInputValueAge(tx, utxoView, nextBlockHeight)
return inputValueAge / float64(serializedTxSize-overhead)
}
// calcInputValueAge is a helper function used to calculate the input age of
// a transaction. The input age for a txin is the number of confirmations
// since the referenced txout multiplied by its output value. The total input
// age is the sum of this value for each txin. Any inputs to the transaction
// which are currently in the mempool and hence not mined into a block yet,
// contribute no additional input age to the transaction.
func calcInputValueAge(tx *wire.MsgTx, utxoView *blockchain.UtxoViewpoint, nextBlockHeight int32) float64 {
var totalInputAge float64
for _, txIn := range tx.TxIn {
// Don't attempt to accumulate the total input age if the
// referenced transaction output doesn't exist.
originHash := &txIn.PreviousOutPoint.Hash
originIndex := txIn.PreviousOutPoint.Index
txEntry := utxoView.LookupEntry(originHash)
if txEntry != nil && !txEntry.IsOutputSpent(originIndex) {
// Inputs with dependencies currently in the mempool
// have their block height set to a special constant.
// Their input age should be computed as zero since
// their parent hasn't made it into a block yet.
var inputAge int32
originHeight := txEntry.BlockHeight()
if originHeight == mempoolHeight {
inputAge = 0
} else {
inputAge = nextBlockHeight - originHeight
}
// Sum the input value times age.
inputValue := txEntry.AmountByIndex(originIndex)
totalInputAge += float64(inputValue * int64(inputAge))
}
}
return totalInputAge
}
// checkInputsStandard performs a series of checks on a transaction's inputs // checkInputsStandard performs a series of checks on a transaction's inputs
// to ensure they are "standard". A standard transaction input within the // to ensure they are "standard". A standard transaction input within the
// context of this function is one whose referenced public key script is of a // context of this function is one whose referenced public key script is of a
@ -418,12 +342,3 @@ func checkTransactionStandard(tx *btcutil.Tx, height int32,
return nil return nil
} }
// minInt is a helper function to return the minimum of two ints. This avoids
// a math import and the need to cast to floats.
func minInt(a, b int) int {
if a < b {
return a
}
return b
}

View file

@ -517,7 +517,7 @@ mempoolLoop:
// Calculate the final transaction priority using the input // Calculate the final transaction priority using the input
// value age sum as well as the adjusted transaction size. The // value age sum as well as the adjusted transaction size. The
// formula is: sum(inputValue * inputAge) / adjustedTxSize // formula is: sum(inputValue * inputAge) / adjustedTxSize
prioItem.priority = mempool.CalcPriority(tx.MsgTx(), utxos, prioItem.priority = mining.CalcPriority(tx.MsgTx(), utxos,
nextBlockHeight) nextBlockHeight)
// Calculate the fee in Satoshi/kB. // Calculate the fee in Satoshi/kB.

View file

@ -1,10 +1,21 @@
// Copyright (c) 2014-2015 The btcsuite developers // Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package mining package mining
import "github.com/btcsuite/btcutil" import (
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
const (
// UnminedHeight is the height used for the "block" height field of the
// contextual transaction information provided in a transaction store
// when it has not yet been mined into a block.
UnminedHeight = 0x7fffffff
)
// Policy houses the policy (configuration parameters) which is used to control // Policy houses the policy (configuration parameters) which is used to control
// the generation of block templates. See the documentation for // the generation of block templates. See the documentation for
@ -27,3 +38,88 @@ type Policy struct {
// (block template generation). // (block template generation).
TxMinFreeFee btcutil.Amount TxMinFreeFee btcutil.Amount
} }
// minInt is a helper function to return the minimum of two ints. This avoids
// a math import and the need to cast to floats.
func minInt(a, b int) int {
if a < b {
return a
}
return b
}
// calcInputValueAge is a helper function used to calculate the input age of
// a transaction. The input age for a txin is the number of confirmations
// since the referenced txout multiplied by its output value. The total input
// age is the sum of this value for each txin. Any inputs to the transaction
// which are currently in the mempool and hence not mined into a block yet,
// contribute no additional input age to the transaction.
func calcInputValueAge(tx *wire.MsgTx, utxoView *blockchain.UtxoViewpoint, nextBlockHeight int32) float64 {
var totalInputAge float64
for _, txIn := range tx.TxIn {
// Don't attempt to accumulate the total input age if the
// referenced transaction output doesn't exist.
originHash := &txIn.PreviousOutPoint.Hash
originIndex := txIn.PreviousOutPoint.Index
txEntry := utxoView.LookupEntry(originHash)
if txEntry != nil && !txEntry.IsOutputSpent(originIndex) {
// Inputs with dependencies currently in the mempool
// have their block height set to a special constant.
// Their input age should computed as zero since their
// parent hasn't made it into a block yet.
var inputAge int32
originHeight := txEntry.BlockHeight()
if originHeight == UnminedHeight {
inputAge = 0
} else {
inputAge = nextBlockHeight - originHeight
}
// Sum the input value times age.
inputValue := txEntry.AmountByIndex(originIndex)
totalInputAge += float64(inputValue * int64(inputAge))
}
}
return totalInputAge
}
// 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 {
// 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
// script to cover a pay-to-script-hash redemption with a compressed
// pubkey. This makes additional inputs free by boosting the priority
// of the transaction accordingly. No more incentive is given to avoid
// encouraging gaming future transactions through the use of junk
// outputs. This is the same logic used in the reference
// implementation.
//
// The constant overhead for a txin is 41 bytes since the previous
// outpoint is 36 bytes + 4 bytes for the sequence + 1 byte the
// signature script length.
//
// A compressed pubkey pay-to-script-hash redemption with a maximum len
// signature is of the form:
// [OP_DATA_73 <73-byte sig> + OP_DATA_35 + {OP_DATA_33
// <33 byte compresed pubkey> + OP_CHECKSIG}]
//
// Thus 1 + 73 + 1 + 1 + 33 + 1 = 110
overhead := 0
for _, txIn := range tx.TxIn {
// Max inputs + size can't possibly overflow here.
overhead += 41 + minInt(110, len(txIn.SignatureScript))
}
serializedTxSize := tx.SerializeSize()
if overhead >= serializedTxSize {
return 0.0
}
inputValueAge := calcInputValueAge(tx, utxoView, nextBlockHeight)
return inputValueAge / float64(serializedTxSize-overhead)
}