mempool/mining: Decouple and optimize priority calcs.
This does three things: - Splits the priority calculation logic from the TxDesc type - Modifies the calcPriority function to perform the value age calculation instead of accepting it as a parameter - Changes the starting priority to be calculated when the transaction is added to the pool The first is useful as it is a step towards decoupling the mining code from the internals of the memory pool. Also, the concept of priority is related to mining policy, so it makes more sense to have the calculations separate than being defined on the memory pool tx descriptor. The second change has been made because everywhere that uses the calcPriority function first has to calculate the value age anyways and by making it part of the function it can be avoided altogether in certain circumstances thereby provided a bit of optimization. The third change ensure the starting priority is safe for reentrancy which will be important once the mempool is split into a separate package.
This commit is contained in:
parent
f41ff545be
commit
8ab565ce21
4 changed files with 26 additions and 52 deletions
47
mempool.go
47
mempool.go
|
@ -47,7 +47,7 @@ type TxDesc struct {
|
|||
Added time.Time // Time when added to pool.
|
||||
Height int32 // Blockheight when added to pool.
|
||||
Fee int64 // Transaction fees.
|
||||
startingPriority float64 // Priority when added to the pool.
|
||||
StartingPriority float64 // Priority when added to the pool.
|
||||
}
|
||||
|
||||
// txMemPool is used as a source of transactions that need to be mined into
|
||||
|
@ -374,14 +374,15 @@ 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(tx *btcutil.Tx, height int32, fee int64) {
|
||||
func (mp *txMemPool) addTransaction(txStore blockchain.TxStore, 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.Sha()] = &TxDesc{
|
||||
Tx: tx,
|
||||
Added: time.Now(),
|
||||
Height: height,
|
||||
Fee: fee,
|
||||
Tx: tx,
|
||||
Added: time.Now(),
|
||||
Height: height,
|
||||
Fee: fee,
|
||||
StartingPriority: calcPriority(tx.MsgTx(), txStore, height),
|
||||
}
|
||||
for _, txIn := range tx.MsgTx().TxIn {
|
||||
mp.outpoints[txIn.PreviousOutPoint] = tx
|
||||
|
@ -462,29 +463,6 @@ func (mp *txMemPool) indexScriptAddressToTx(pkScript []byte, tx *btcutil.Tx) err
|
|||
return nil
|
||||
}
|
||||
|
||||
// StartingPriority calculates the priority of this tx descriptor's underlying
|
||||
// transaction relative to when it was first added to the mempool. The result
|
||||
// is lazily computed and then cached for subsequent function calls.
|
||||
func (txD *TxDesc) StartingPriority(txStore blockchain.TxStore) float64 {
|
||||
// Return our cached result.
|
||||
if txD.startingPriority != float64(0) {
|
||||
return txD.startingPriority
|
||||
}
|
||||
|
||||
// Compute our starting priority caching the result.
|
||||
inputAge := calcInputValueAge(txD, txStore, txD.Height)
|
||||
txD.startingPriority = calcPriority(txD.Tx, inputAge)
|
||||
|
||||
return txD.startingPriority
|
||||
}
|
||||
|
||||
// CurrentPriority calculates the current priority of this tx descriptor's
|
||||
// underlying transaction relative to the next block height.
|
||||
func (txD *TxDesc) CurrentPriority(txStore blockchain.TxStore, nextBlockHeight int32) float64 {
|
||||
inputAge := calcInputValueAge(txD, txStore, nextBlockHeight)
|
||||
return calcPriority(txD.Tx, inputAge)
|
||||
}
|
||||
|
||||
// checkPoolDoubleSpend checks whether or not the passed transaction is
|
||||
// attempting to spend coins already spent by other transactions in the pool.
|
||||
// Note it does not check for double spends against transactions already in the
|
||||
|
@ -772,13 +750,8 @@ 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 && !cfg.NoRelayPriority && txFee < minFee {
|
||||
txD := &TxDesc{
|
||||
Tx: tx,
|
||||
Added: time.Now(),
|
||||
Height: curHeight,
|
||||
Fee: txFee,
|
||||
}
|
||||
currentPriority := txD.CurrentPriority(txStore, nextBlockHeight)
|
||||
currentPriority := calcPriority(tx.MsgTx(), txStore,
|
||||
nextBlockHeight)
|
||||
if currentPriority <= minHighPriority {
|
||||
str := fmt.Sprintf("transaction %v has insufficient "+
|
||||
"priority (%g <= %g)", txHash,
|
||||
|
@ -823,7 +796,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo
|
|||
}
|
||||
|
||||
// Add to transaction pool.
|
||||
mp.addTransaction(tx, curHeight, txFee)
|
||||
mp.addTransaction(txStore, tx, curHeight, txFee)
|
||||
|
||||
txmpLog.Debugf("Accepted transaction %v (pool size: %v)", txHash,
|
||||
len(mp.pool))
|
||||
|
|
|
@ -540,7 +540,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 = txDesc.CurrentPriority(txStore, nextBlockHeight)
|
||||
prioItem.priority = calcPriority(tx.MsgTx(), txStore, nextBlockHeight)
|
||||
|
||||
// Calculate the fee in Satoshi/kB.
|
||||
txSize := tx.MsgTx().SerializeSize()
|
||||
|
|
11
policy.go
11
policy.go
|
@ -79,7 +79,7 @@ func calcMinRequiredTxRelayFee(serializedSize int64, minRelayTxFee btcutil.Amoun
|
|||
// 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 *btcutil.Tx, inputValueAge float64) float64 {
|
||||
func calcPriority(tx *wire.MsgTx, txStore blockchain.TxStore, 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
|
||||
|
@ -101,16 +101,17 @@ func calcPriority(tx *btcutil.Tx, inputValueAge float64) float64 {
|
|||
//
|
||||
// Thus 1 + 73 + 1 + 1 + 33 + 1 = 110
|
||||
overhead := 0
|
||||
for _, txIn := range tx.MsgTx().TxIn {
|
||||
for _, txIn := range tx.TxIn {
|
||||
// Max inputs + size can't possibly overflow here.
|
||||
overhead += 41 + minInt(110, len(txIn.SignatureScript))
|
||||
}
|
||||
|
||||
serializedTxSize := tx.MsgTx().SerializeSize()
|
||||
serializedTxSize := tx.SerializeSize()
|
||||
if overhead >= serializedTxSize {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
inputValueAge := calcInputValueAge(tx, txStore, nextBlockHeight)
|
||||
return inputValueAge / float64(serializedTxSize-overhead)
|
||||
}
|
||||
|
||||
|
@ -120,9 +121,9 @@ func calcPriority(tx *btcutil.Tx, inputValueAge float64) float64 {
|
|||
// 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(txDesc *TxDesc, txStore blockchain.TxStore, nextBlockHeight int32) float64 {
|
||||
func calcInputValueAge(tx *wire.MsgTx, txStore blockchain.TxStore, nextBlockHeight int32) float64 {
|
||||
var totalInputAge float64
|
||||
for _, txIn := range txDesc.Tx.MsgTx().TxIn {
|
||||
for _, txIn := range tx.TxIn {
|
||||
originHash := &txIn.PreviousOutPoint.Hash
|
||||
originIndex := txIn.PreviousOutPoint.Index
|
||||
|
||||
|
|
18
rpcserver.go
18
rpcserver.go
|
@ -2404,15 +2404,15 @@ func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{
|
|||
mp.RLock()
|
||||
defer mp.RUnlock()
|
||||
for _, desc := range descs {
|
||||
// Calculate the starting and current priority from the
|
||||
// the tx's inputs. Use zeros if one or more of the
|
||||
// input transactions can't be found for some reason.
|
||||
var startingPriority, currentPriority float64
|
||||
inputTxs, err := mp.fetchInputTransactions(desc.Tx, false)
|
||||
// Calculate the current priority from the the tx's
|
||||
// inputs. Use zero if one or more of the input
|
||||
// transactions can't be found for some reason.
|
||||
tx := desc.Tx
|
||||
var currentPriority float64
|
||||
inputTxs, err := mp.fetchInputTransactions(tx, false)
|
||||
if err == nil {
|
||||
startingPriority = desc.StartingPriority(inputTxs)
|
||||
currentPriority = desc.CurrentPriority(inputTxs,
|
||||
newestHeight+1)
|
||||
currentPriority = calcPriority(tx.MsgTx(),
|
||||
inputTxs, newestHeight+1)
|
||||
}
|
||||
|
||||
mpd := &btcjson.GetRawMempoolVerboseResult{
|
||||
|
@ -2420,7 +2420,7 @@ func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{
|
|||
Fee: btcutil.Amount(desc.Fee).ToBTC(),
|
||||
Time: desc.Added.Unix(),
|
||||
Height: int64(desc.Height),
|
||||
StartingPriority: startingPriority,
|
||||
StartingPriority: desc.StartingPriority,
|
||||
CurrentPriority: currentPriority,
|
||||
Depends: make([]string, 0),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue