mempool: modify mempool sanity checks to be segwit aware
This commit is contained in:
parent
137aabd631
commit
26ff8ddce4
6 changed files with 82 additions and 29 deletions
|
@ -53,7 +53,7 @@ const (
|
||||||
blockMaxSizeMax = wire.MaxBlockPayload - 1000
|
blockMaxSizeMax = wire.MaxBlockPayload - 1000
|
||||||
defaultGenerate = false
|
defaultGenerate = false
|
||||||
defaultMaxOrphanTransactions = 100
|
defaultMaxOrphanTransactions = 100
|
||||||
defaultMaxOrphanTxSize = mempool.MaxStandardTxSize
|
defaultMaxOrphanTxSize = 100000
|
||||||
defaultSigCacheMaxSize = 100000
|
defaultSigCacheMaxSize = 100000
|
||||||
sampleConfigFilename = "sample-btcd.conf"
|
sampleConfigFilename = "sample-btcd.conf"
|
||||||
defaultTxIndex = false
|
defaultTxIndex = false
|
||||||
|
|
|
@ -73,9 +73,18 @@ type Config struct {
|
||||||
// utxo view.
|
// utxo view.
|
||||||
CalcSequenceLock func(*btcutil.Tx, *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error)
|
CalcSequenceLock func(*btcutil.Tx, *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error)
|
||||||
|
|
||||||
|
// IsDeploymentActive returns true if the target deploymentID is
|
||||||
|
// active, and false otherwise. The mempool uses this function to gauge
|
||||||
|
// if transactions using new to be soft-forked rules should be allowed
|
||||||
|
// into the mempool or not.
|
||||||
|
IsDeploymentActive func(deploymentID uint32) (bool, error)
|
||||||
|
|
||||||
// SigCache defines a signature cache to use.
|
// SigCache defines a signature cache to use.
|
||||||
SigCache *txscript.SigCache
|
SigCache *txscript.SigCache
|
||||||
|
|
||||||
|
// HashCache defines the transaction hash mid-state cache to use.
|
||||||
|
HashCache *txscript.HashCache
|
||||||
|
|
||||||
// AddrIndex defines the optional address index instance to use for
|
// AddrIndex defines the optional address index instance to use for
|
||||||
// indexing the unconfirmed transactions in the memory pool.
|
// indexing the unconfirmed transactions in the memory pool.
|
||||||
// This can be nil if the address index is not enabled.
|
// This can be nil if the address index is not enabled.
|
||||||
|
@ -112,10 +121,10 @@ type Policy struct {
|
||||||
// of big orphans.
|
// of big orphans.
|
||||||
MaxOrphanTxSize int
|
MaxOrphanTxSize int
|
||||||
|
|
||||||
// MaxSigOpsPerTx is the maximum number of signature operations
|
// MaxSigOpCostPerTx is the cumulative maximum cost of all the signature
|
||||||
// in a single transaction we will relay or mine. It is a fraction
|
// operations in a single transaction we will relay or mine. It is a
|
||||||
// of the max signature operations for a block.
|
// fraction of the max signature operations for a block.
|
||||||
MaxSigOpsPerTx int
|
MaxSigOpCostPerTx int
|
||||||
|
|
||||||
// MinRelayTxFee defines the minimum transaction fee in BTC/kB to be
|
// MinRelayTxFee defines the minimum transaction fee in BTC/kB to be
|
||||||
// considered a non-zero fee.
|
// considered a non-zero fee.
|
||||||
|
@ -604,6 +613,22 @@ func (mp *TxPool) FetchTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error)
|
||||||
func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejectDupOrphans bool) ([]*chainhash.Hash, *TxDesc, error) {
|
func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejectDupOrphans bool) ([]*chainhash.Hash, *TxDesc, error) {
|
||||||
txHash := tx.Hash()
|
txHash := tx.Hash()
|
||||||
|
|
||||||
|
// If a transaction has iwtness data, and segwit isn't active yet, If
|
||||||
|
// segwit isn't active yet, then we won't accept it into the mempool as
|
||||||
|
// it can't be mined yet.
|
||||||
|
if tx.MsgTx().HasWitness() {
|
||||||
|
segwitActive, err := mp.cfg.IsDeploymentActive(chaincfg.DeploymentSegwit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !segwitActive {
|
||||||
|
str := fmt.Sprintf("transaction %v has witness data, "+
|
||||||
|
"but segwit isn't active yet", txHash)
|
||||||
|
return nil, nil, txRuleError(wire.RejectNonstandard, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Don't accept the transaction if it already exists in the pool. This
|
// Don't accept the transaction if it already exists in the pool. This
|
||||||
// applies to orphan transactions as well when the reject duplicate
|
// applies to orphan transactions as well when the reject duplicate
|
||||||
// orphans flag is set. This check is intended to be a quick check to
|
// orphans flag is set. This check is intended to be a quick check to
|
||||||
|
@ -780,17 +805,17 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
|
||||||
// the coinbase address itself can contain signature operations, the
|
// the coinbase address itself can contain signature operations, the
|
||||||
// maximum allowed signature operations per transaction is less than
|
// maximum allowed signature operations per transaction is less than
|
||||||
// the maximum allowed signature operations per block.
|
// the maximum allowed signature operations per block.
|
||||||
numSigOps, err := blockchain.CountP2SHSigOps(tx, false, utxoView)
|
// TODO(roasbeef): last bool should be conditional on segwit activation
|
||||||
|
sigOpCost, err := blockchain.GetSigOpCost(tx, false, utxoView, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if cerr, ok := err.(blockchain.RuleError); ok {
|
if cerr, ok := err.(blockchain.RuleError); ok {
|
||||||
return nil, nil, chainRuleError(cerr)
|
return nil, nil, chainRuleError(cerr)
|
||||||
}
|
}
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
numSigOps += blockchain.CountSigOps(tx)
|
if sigOpCost > mp.cfg.Policy.MaxSigOpCostPerTx {
|
||||||
if numSigOps > mp.cfg.Policy.MaxSigOpsPerTx {
|
str := fmt.Sprintf("transaction %v sigop cost is too high: %d > %d",
|
||||||
str := fmt.Sprintf("transaction %v has too many sigops: %d > %d",
|
txHash, sigOpCost, mp.cfg.Policy.MaxSigOpCostPerTx)
|
||||||
txHash, numSigOps, mp.cfg.Policy.MaxSigOpsPerTx)
|
|
||||||
return nil, nil, txRuleError(wire.RejectNonstandard, str)
|
return nil, nil, txRuleError(wire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -857,7 +882,8 @@ func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejec
|
||||||
// Verify crypto signatures for each input and reject the transaction if
|
// Verify crypto signatures for each input and reject the transaction if
|
||||||
// any don't verify.
|
// any don't verify.
|
||||||
err = blockchain.ValidateTransactionScripts(tx, utxoView,
|
err = blockchain.ValidateTransactionScripts(tx, utxoView,
|
||||||
txscript.StandardVerifyFlags, mp.cfg.SigCache)
|
txscript.StandardVerifyFlags, mp.cfg.SigCache,
|
||||||
|
mp.cfg.HashCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if cerr, ok := err.(blockchain.RuleError); ok {
|
if cerr, ok := err.(blockchain.RuleError); ok {
|
||||||
return nil, nil, chainRuleError(cerr)
|
return nil, nil, chainRuleError(cerr)
|
||||||
|
|
|
@ -310,7 +310,7 @@ func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutp
|
||||||
FreeTxRelayLimit: 15.0,
|
FreeTxRelayLimit: 15.0,
|
||||||
MaxOrphanTxs: 5,
|
MaxOrphanTxs: 5,
|
||||||
MaxOrphanTxSize: 1000,
|
MaxOrphanTxSize: 1000,
|
||||||
MaxSigOpsPerTx: blockchain.MaxSigOpsPerBlock / 5,
|
MaxSigOpCostPerTx: blockchain.MaxBlockSigOpsCost / 4,
|
||||||
MinRelayTxFee: 1000, // 1 Satoshi per byte
|
MinRelayTxFee: 1000, // 1 Satoshi per byte
|
||||||
MaxTxVersion: 1,
|
MaxTxVersion: 1,
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,10 +19,9 @@ const (
|
||||||
// that are considered standard in a pay-to-script-hash script.
|
// that are considered standard in a pay-to-script-hash script.
|
||||||
maxStandardP2SHSigOps = 15
|
maxStandardP2SHSigOps = 15
|
||||||
|
|
||||||
// MaxStandardTxSize is the maximum size allowed for transactions that
|
// maxStandardTxCost is the max weight permitted by any transaction
|
||||||
// are considered standard and will therefore be relayed and considered
|
// according to the current default policy.
|
||||||
// for mining.
|
maxStandardTxWeight = 400000
|
||||||
MaxStandardTxSize = 100000
|
|
||||||
|
|
||||||
// maxStandardSigScriptSize is the maximum size allowed for a
|
// maxStandardSigScriptSize is the maximum size allowed for a
|
||||||
// transaction input signature script to be considered standard. This
|
// transaction input signature script to be considered standard. This
|
||||||
|
@ -217,17 +216,43 @@ func isDust(txOut *wire.TxOut, minRelayTxFee btcutil.Amount) bool {
|
||||||
// 36 prev outpoint, 1 script len, 73 script [1 OP_DATA_72,
|
// 36 prev outpoint, 1 script len, 73 script [1 OP_DATA_72,
|
||||||
// 72 sig], 4 sequence
|
// 72 sig], 4 sequence
|
||||||
//
|
//
|
||||||
|
// Pay-to-witness-pubkey-hash bytes breakdown:
|
||||||
|
//
|
||||||
|
// Output to witness key hash (31 bytes);
|
||||||
|
// 8 value, 1 script len, 22 script [1 OP_0, 1 OP_DATA_20,
|
||||||
|
// 20 bytes hash160]
|
||||||
|
//
|
||||||
|
// Input (67 bytes as the 107 witness stack is discounted):
|
||||||
|
// 36 prev outpoint, 1 script len, 0 script (not sigScript), 107
|
||||||
|
// witness stack bytes [1 element length, 33 compressed pubkey,
|
||||||
|
// element length 72 sig], 4 sequence
|
||||||
|
//
|
||||||
|
//
|
||||||
// Theoretically this could examine the script type of the output script
|
// Theoretically this could examine the script type of the output script
|
||||||
// and use a different size for the typical input script size for
|
// and use a different size for the typical input script size for
|
||||||
// pay-to-pubkey vs pay-to-pubkey-hash inputs per the above breakdowns,
|
// pay-to-pubkey vs pay-to-pubkey-hash inputs per the above breakdowns,
|
||||||
// but the only combinination which is less than the value chosen is
|
// but the only combination which is less than the value chosen is
|
||||||
// a pay-to-pubkey script with a compressed pubkey, which is not very
|
// a pay-to-pubkey script with a compressed pubkey, which is not very
|
||||||
// common.
|
// common.
|
||||||
//
|
//
|
||||||
// The most common scripts are pay-to-pubkey-hash, and as per the above
|
// The most common scripts are pay-to-pubkey-hash, and as per the above
|
||||||
// breakdown, the minimum size of a p2pkh input script is 148 bytes. So
|
// breakdown, the minimum size of a p2pkh input script is 148 bytes. So
|
||||||
// that figure is used.
|
// that figure is used. If the output being spent is a witness program,
|
||||||
totalSize := txOut.SerializeSize() + 148
|
// then we apply the witness discount to the size of the signature.
|
||||||
|
//
|
||||||
|
// The segwit analogue to p2pkh is a p2wkh output. This is the smallest
|
||||||
|
// output possible using the new segwit features. The 107 bytes of
|
||||||
|
// witness data is discounted by a factor of 4, leading to a computed
|
||||||
|
// value of 67 bytes of witness data.
|
||||||
|
//
|
||||||
|
// Both cases share a 41 byte preamble required to reference the input
|
||||||
|
// being spent and the sequence number of the input.
|
||||||
|
totalSize := txOut.SerializeSize() + 41
|
||||||
|
if txscript.IsWitnessProgram(txOut.PkScript) {
|
||||||
|
totalSize += (107 / blockchain.WitnessScaleFactor)
|
||||||
|
} else {
|
||||||
|
totalSize += 107
|
||||||
|
}
|
||||||
|
|
||||||
// The output is considered dust if the cost to the network to spend the
|
// The output is considered dust if the cost to the network to spend the
|
||||||
// coins is more than 1/3 of the minimum free transaction relay fee.
|
// coins is more than 1/3 of the minimum free transaction relay fee.
|
||||||
|
@ -275,10 +300,10 @@ func checkTransactionStandard(tx *btcutil.Tx, height int32,
|
||||||
// almost as much to process as the sender fees, limit the maximum
|
// almost as much to process as the sender fees, limit the maximum
|
||||||
// size of a transaction. This also helps mitigate CPU exhaustion
|
// size of a transaction. This also helps mitigate CPU exhaustion
|
||||||
// attacks.
|
// attacks.
|
||||||
serializedLen := msgTx.SerializeSize()
|
txWeight := blockchain.GetTransactionWeight(tx)
|
||||||
if serializedLen > MaxStandardTxSize {
|
if txWeight > maxStandardTxWeight {
|
||||||
str := fmt.Sprintf("transaction size of %v is larger than max "+
|
str := fmt.Sprintf("weight of transaction %v is larger than max "+
|
||||||
"allowed size of %v", serializedLen, MaxStandardTxSize)
|
"allowed weight of %v", txWeight, maxStandardTxWeight)
|
||||||
return txRuleError(wire.RejectNonstandard, str)
|
return txRuleError(wire.RejectNonstandard, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +370,7 @@ func checkTransactionStandard(tx *btcutil.Tx, height int32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTxVirtualSize computes the virtual size of a given transaction. A
|
// GetTxVirtualSize computes the virtual size of a given transaction. A
|
||||||
// transaction's virtual size is based off it's weight, creating a discount for
|
// transaction's virtual size is based off its weight, creating a discount for
|
||||||
// any witness data it contains, proportional to the current
|
// any witness data it contains, proportional to the current
|
||||||
// blockchain.WitnessScaleFactor value.
|
// blockchain.WitnessScaleFactor value.
|
||||||
func GetTxVirtualSize(tx *btcutil.Tx) int64 {
|
func GetTxVirtualSize(tx *btcutil.Tx) int64 {
|
||||||
|
|
|
@ -41,13 +41,13 @@ func TestCalcMinRequiredTxRelayFee(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"max standard tx size with default minimum relay fee",
|
"max standard tx size with default minimum relay fee",
|
||||||
MaxStandardTxSize,
|
maxStandardTxWeight / 4,
|
||||||
DefaultMinRelayTxFee,
|
DefaultMinRelayTxFee,
|
||||||
100000,
|
100000,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"max standard tx size with max satoshi relay fee",
|
"max standard tx size with max satoshi relay fee",
|
||||||
MaxStandardTxSize,
|
maxStandardTxWeight / 4,
|
||||||
btcutil.MaxSatoshi,
|
btcutil.MaxSatoshi,
|
||||||
btcutil.MaxSatoshi,
|
btcutil.MaxSatoshi,
|
||||||
},
|
},
|
||||||
|
@ -360,7 +360,7 @@ func TestCheckTransactionStandard(t *testing.T) {
|
||||||
TxOut: []*wire.TxOut{{
|
TxOut: []*wire.TxOut{{
|
||||||
Value: 0,
|
Value: 0,
|
||||||
PkScript: bytes.Repeat([]byte{0x00},
|
PkScript: bytes.Repeat([]byte{0x00},
|
||||||
MaxStandardTxSize+1),
|
(maxStandardTxWeight/4)+1),
|
||||||
}},
|
}},
|
||||||
LockTime: 0,
|
LockTime: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -2470,8 +2470,10 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
|
||||||
CalcSequenceLock: func(tx *btcutil.Tx, view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) {
|
CalcSequenceLock: func(tx *btcutil.Tx, view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) {
|
||||||
return bm.chain.CalcSequenceLock(tx, view, true)
|
return bm.chain.CalcSequenceLock(tx, view, true)
|
||||||
},
|
},
|
||||||
SigCache: s.sigCache,
|
IsDeploymentActive: bm.chain.IsDeploymentActive,
|
||||||
AddrIndex: s.addrIndex,
|
SigCache: s.sigCache,
|
||||||
|
HashCache: s.hashCache,
|
||||||
|
AddrIndex: s.addrIndex,
|
||||||
}
|
}
|
||||||
s.txMemPool = mempool.New(&txC)
|
s.txMemPool = mempool.New(&txC)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue