Skip to content

Commit

Permalink
Add the starting+current priority to getrawmempool.
Browse files Browse the repository at this point in the history
  • Loading branch information
Roasbeef authored and davecgh committed Nov 17, 2014
1 parent c36b00c commit a49b0d0
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 29 deletions.
72 changes: 68 additions & 4 deletions mempool.go
Expand Up @@ -81,10 +81,11 @@ const (
// TxDesc is a descriptor containing a transaction in the mempool and the
// metadata we store about it.
type TxDesc struct {
Tx *btcutil.Tx // Transaction.
Added time.Time // Time when added to pool.
Height int64 // Blockheight when added to pool.
Fee int64 // Transaction fees.
Tx *btcutil.Tx // Transaction.
Added time.Time // Time when added to pool.
Height int64 // Blockheight when added to pool.
Fee int64 // Transaction fees.
startingPriority float64 // Priority when added to the pool.
}

// txMemPool is used as a source of transactions that need to be mined into
Expand Down Expand Up @@ -670,6 +671,69 @@ func (mp *txMemPool) addTransaction(tx *btcutil.Tx, height, fee int64) {
mp.lastUpdated = time.Now()
}

// 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 btcchain.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)
txSize := txD.Tx.MsgTx().SerializeSize()
txD.startingPriority = calcPriority(txD.Tx, txSize, 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 btcchain.TxStore, nextBlockHeight int64) float64 {
inputAge := calcInputValueAge(txD, txStore, nextBlockHeight)
txSize := txD.Tx.MsgTx().SerializeSize()
return calcPriority(txD.Tx, txSize, inputAge)
}

// 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. If the tx
// depends on one currently in the mempool, then its input age is zero.
func calcInputValueAge(txDesc *TxDesc, txStore btcchain.TxStore,
nextBlockHeight int64) float64 {
var totalInputAge float64
for _, txIn := range txDesc.Tx.MsgTx().TxIn {
originHash := &txIn.PreviousOutPoint.Hash
originIndex := txIn.PreviousOutPoint.Index

// Don't attempt to accumulate the total input age if the txIn
// in question doesn't exist.
if txData, exists := txStore[*originHash]; exists && txData.Tx != nil {
originTxOut := txData.Tx.MsgTx().TxOut[originIndex]

// Transactions 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 int64
if txData.BlockHeight == mempoolHeight {
inputAge = 0
} else {
inputAge = nextBlockHeight - txData.BlockHeight
}

// Sum the input value times age.
inputValue := originTxOut.Value
totalInputAge += float64(inputValue * inputAge)
}
}

return totalInputAge
}

// 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
Expand Down
26 changes: 7 additions & 19 deletions mining.go
Expand Up @@ -513,15 +513,10 @@ mempoolLoop:
continue
}

// Calculate the input value age sum for the transaction. This
// is comprised of the sum all of input amounts multiplied by
// their respective age (number of confirmations since the
// referenced input transaction). While doing the above, also
// setup dependencies for any transactions which reference other
// transactions in the mempool so they can be properly ordered
// below.
// Setup dependencies for any transactions which reference
// other transactions in the mempool so they can be properly
// ordered below.
prioItem := &txPrioItem{tx: txDesc.Tx}
inputValueAge := float64(0.0)
for _, txIn := range tx.MsgTx().TxIn {
originHash := &txIn.PreviousOutPoint.Hash
originIndex := txIn.PreviousOutPoint.Index
Expand Down Expand Up @@ -550,9 +545,8 @@ mempoolLoop:
}
prioItem.dependsOn[*originHash] = struct{}{}

// No need to calculate or sum input value age
// for this input since it's zero due to
// the input age multiplier of 0.
// Skip the check below. We already know the
// referenced transaction is available.
continue
}

Expand All @@ -566,25 +560,19 @@ mempoolLoop:
originIndex, originHash)
continue mempoolLoop
}

// Sum the input value times age.
originTxOut := txData.Tx.MsgTx().TxOut[originIndex]
inputValue := originTxOut.Value
inputAge := nextBlockHeight - txData.BlockHeight
inputValueAge += float64(inputValue * inputAge)
}

// 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
txSize := tx.MsgTx().SerializeSize()
prioItem.priority = calcPriority(tx, txSize, inputValueAge)
prioItem.priority = txDesc.CurrentPriority(txStore, nextBlockHeight)

// Calculate the fee in Satoshi/KB.
// NOTE: This is a more precise value than the one calculated
// during calcMinRelayFee which rounds up to the nearest full
// kilobyte boundary. This is beneficial since it provides an
// incentive to create smaller transactions.
txSize := tx.MsgTx().SerializeSize()
prioItem.feePerKB = float64(txDesc.Fee) / (float64(txSize) / 1000)
prioItem.fee = txDesc.Fee

Expand Down
32 changes: 26 additions & 6 deletions rpcserver.go
Expand Up @@ -2290,19 +2290,39 @@ func handleGetPeerInfo(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{})
// handleGetRawMempool implements the getrawmempool command.
func handleGetRawMempool(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.GetRawMempoolCmd)
descs := s.server.txMemPool.TxDescs()
mp := s.server.txMemPool
descs := mp.TxDescs()

if c.Verbose {
result := make(map[string]*btcjson.GetRawMempoolResult, len(descs))

_, newestHeight, err := s.server.db.NewestSha()
if err != nil {
rpcsLog.Errorf("Cannot get newest sha: %v", err)
return nil, btcjson.ErrBlockNotFound
}

mp.RLock()
defer mp.RUnlock()
for _, desc := range descs {
// Calculate the starting and ending priority from the
// the tx's inputs. If we can't find the input for some
// reason, then we display zero in place.
inputTxs, err := mp.fetchInputTransactions(desc.Tx)
var startingPriority, endingPriority float64
if err == nil {
startingPriority = desc.StartingPriority(inputTxs)
endingPriority = desc.CurrentPriority(inputTxs,
newestHeight+1)
}

mpd := &btcjson.GetRawMempoolResult{
Size: int32(desc.Tx.MsgTx().SerializeSize()),
Fee: float64(desc.Fee) /
btcutil.SatoshiPerBitcoin,
Size: int32(desc.Tx.MsgTx().SerializeSize()),
Fee: btcutil.Amount(desc.Fee).ToUnit(btcutil.AmountSatoshi),
Time: desc.Added.Unix(),
Height: desc.Height,
StartingPriority: 0, // We don't mine.
CurrentPriority: 0, // We don't mine.
StartingPriority: startingPriority,
CurrentPriority: endingPriority,
Depends: make([]string, 0),
}
for _, txIn := range desc.Tx.MsgTx().TxIn {
Expand Down

0 comments on commit a49b0d0

Please sign in to comment.