diff --git a/mempool/mempool.go b/mempool/mempool.go index b278ff86..c473bf38 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -34,10 +34,6 @@ const ( // 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 - // 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 // next scan. @@ -489,7 +485,7 @@ func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *btcutil Height: height, Fee: fee, }, - StartingPriority: CalcPriority(tx.MsgTx(), utxoView, height), + StartingPriority: mining.CalcPriority(tx.MsgTx(), utxoView, height), } for _, txIn := range tx.MsgTx().TxIn { 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 { - utxoView.AddTxOuts(poolTxDesc.Tx, mempoolHeight) + utxoView.AddTxOuts(poolTxDesc.Tx, mining.UnminedHeight) } } 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 // are exempted. if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee { - currentPriority := CalcPriority(tx.MsgTx(), utxoView, + currentPriority := mining.CalcPriority(tx.MsgTx(), utxoView, nextBlockHeight) if currentPriority <= MinHighPriority { str := fmt.Sprintf("transaction %v has insufficient "+ @@ -1099,7 +1095,7 @@ func (mp *TxPool) RawMempoolVerbose() map[string]*btcjson.GetRawMempoolVerboseRe var currentPriority float64 utxos, err := mp.fetchInputUtxos(tx) if err == nil { - currentPriority = CalcPriority(tx.MsgTx(), utxos, + currentPriority = mining.CalcPriority(tx.MsgTx(), utxos, bestHeight+1) } diff --git a/mempool/policy.go b/mempool/policy.go index 229471d4..fecf5952 100644 --- a/mempool/policy.go +++ b/mempool/policy.go @@ -80,82 +80,6 @@ func calcMinRequiredTxRelayFee(serializedSize int64, minRelayTxFee btcutil.Amoun 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 // 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 @@ -418,12 +342,3 @@ func checkTransactionStandard(tx *btcutil.Tx, height int32, 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 -} diff --git a/mining.go b/mining.go index 8db0090d..aa8c7702 100644 --- a/mining.go +++ b/mining.go @@ -517,7 +517,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 = mempool.CalcPriority(tx.MsgTx(), utxos, + prioItem.priority = mining.CalcPriority(tx.MsgTx(), utxos, nextBlockHeight) // Calculate the fee in Satoshi/kB. diff --git a/mining/policy.go b/mining/policy.go index 26b1f4e6..5b404351 100644 --- a/mining/policy.go +++ b/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 // license that can be found in the LICENSE file. 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 // the generation of block templates. See the documentation for @@ -27,3 +38,88 @@ type Policy struct { // (block template generation). 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) +}