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:
parent
c2fb0cb18e
commit
2cfc6478ce
4 changed files with 103 additions and 96 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
100
mining/policy.go
100
mining/policy.go
|
@ -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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue