decred.org/dcrwallet/v3@v3.1.0/wallet/txrules/rules.go (about) 1 // Copyright (c) 2016 The btcsuite developers 2 // Copyright (c) 2016-2021 The Decred developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package txrules 7 8 import ( 9 "decred.org/dcrwallet/v3/errors" 10 "github.com/decred/dcrd/dcrutil/v4" 11 "github.com/decred/dcrd/txscript/v4" 12 "github.com/decred/dcrd/txscript/v4/stdscript" 13 "github.com/decred/dcrd/wire" 14 ) 15 16 // StakeSubScriptType potentially transforms the provided script type by 17 // converting the various stake-specific script types to their associated sub 18 // type. It will be returned unmodified otherwise. 19 func StakeSubScriptType(scriptType stdscript.ScriptType) (stdscript.ScriptType, bool) { 20 switch scriptType { 21 case stdscript.STStakeSubmissionPubKeyHash, stdscript.STStakeChangePubKeyHash, 22 stdscript.STStakeGenPubKeyHash, stdscript.STStakeRevocationPubKeyHash, 23 stdscript.STTreasuryGenPubKeyHash: 24 25 return stdscript.STPubKeyHashEcdsaSecp256k1, true 26 27 case stdscript.STStakeSubmissionScriptHash, stdscript.STStakeChangeScriptHash, 28 stdscript.STStakeGenScriptHash, stdscript.STStakeRevocationScriptHash, 29 stdscript.STTreasuryGenScriptHash: 30 31 return stdscript.STScriptHash, true 32 } 33 34 return scriptType, false 35 } 36 37 // DefaultRelayFeePerKb is the default minimum relay fee policy for a mempool. 38 const DefaultRelayFeePerKb dcrutil.Amount = 0.0001 * 1e8 39 40 // IsDustAmount determines whether a transaction output value and script length would 41 // cause the output to be considered dust. Transactions with dust outputs are 42 // not standard and are rejected by mempools with default policies. 43 func IsDustAmount(amount dcrutil.Amount, scriptSize int, relayFeePerKb dcrutil.Amount) bool { 44 // Calculate the total (estimated) cost to the network. This is 45 // calculated using the serialize size of the output plus the serial 46 // size of a transaction input which redeems it. The output is assumed 47 // to be compressed P2PKH as this is the most common script type. Use 48 // the average size of a compressed P2PKH redeem input (165) rather than 49 // the largest possible (txsizes.RedeemP2PKHInputSize). 50 totalSize := 8 + 2 + wire.VarIntSerializeSize(uint64(scriptSize)) + 51 scriptSize + 165 52 53 // Dust is defined as an output value where the total cost to the network 54 // (output size + input size) is greater than 1/3 of the relay fee. 55 return int64(amount)*1000/(3*int64(totalSize)) < int64(relayFeePerKb) 56 } 57 58 // IsDustOutput determines whether a transaction output is considered dust. 59 // Transactions with dust outputs are not standard and are rejected by mempools 60 // with default policies. 61 func IsDustOutput(output *wire.TxOut, relayFeePerKb dcrutil.Amount) bool { 62 // Unspendable outputs which solely carry data are not checked for dust. 63 if stdscript.IsNullDataScript(output.Version, output.PkScript) { 64 return false 65 } 66 67 // All other unspendable outputs are considered dust. 68 if txscript.IsUnspendable(output.Value, output.PkScript) { 69 return true 70 } 71 72 return IsDustAmount(dcrutil.Amount(output.Value), len(output.PkScript), 73 relayFeePerKb) 74 } 75 76 // CheckOutput performs simple consensus and policy tests on a transaction 77 // output. Returns with errors.Invalid if output violates consensus rules, and 78 // errors.Policy if the output violates a non-consensus policy. 79 func CheckOutput(output *wire.TxOut, relayFeePerKb dcrutil.Amount) error { 80 if output.Value < 0 { 81 return errors.E(errors.Invalid, "transaction output amount is negative") 82 } 83 if output.Value > dcrutil.MaxAmount { 84 return errors.E(errors.Invalid, "transaction output amount exceeds maximum value") 85 } 86 if IsDustOutput(output, relayFeePerKb) { 87 return errors.E(errors.Policy, "transaction output is dust") 88 } 89 return nil 90 } 91 92 // FeeForSerializeSize calculates the required fee for a transaction of some 93 // arbitrary size given a mempool's relay fee policy. 94 func FeeForSerializeSize(relayFeePerKb dcrutil.Amount, txSerializeSize int) dcrutil.Amount { 95 fee := relayFeePerKb * dcrutil.Amount(txSerializeSize) / 1000 96 97 if fee == 0 && relayFeePerKb > 0 { 98 fee = relayFeePerKb 99 } 100 101 if fee < 0 || fee > dcrutil.MaxAmount { 102 fee = dcrutil.MaxAmount 103 } 104 105 return fee 106 } 107 108 func sumOutputValues(outputs []*wire.TxOut) (totalOutput dcrutil.Amount) { 109 for _, txOut := range outputs { 110 totalOutput += dcrutil.Amount(txOut.Value) 111 } 112 return totalOutput 113 } 114 115 // PaysHighFees checks whether the signed transaction pays insanely high fees. 116 // Transactons are defined to have a high fee if they have pay a fee rate that 117 // is 1000 time higher than the default fee. 118 func PaysHighFees(totalInput dcrutil.Amount, tx *wire.MsgTx) bool { 119 fee := totalInput - sumOutputValues(tx.TxOut) 120 if fee <= 0 { 121 // Impossible to determine 122 return false 123 } 124 125 maxFee := FeeForSerializeSize(1000*DefaultRelayFeePerKb, tx.SerializeSize()) 126 return fee > maxFee 127 } 128 129 // TxPaysHighFees checks whether the signed transaction pays insanely high fees. 130 // Transactons are defined to have a high fee if they have pay a fee rate that 131 // is 1000 time higher than the default fee. Total transaction input value is 132 // determined by summing the ValueIn fields of each input, and an error is returned 133 // if any input values were the null value. 134 func TxPaysHighFees(tx *wire.MsgTx) (bool, error) { 135 var input dcrutil.Amount 136 for i, in := range tx.TxIn { 137 if in.ValueIn < 0 { 138 err := errors.Errorf("transaction input %d does not "+ 139 "specify the input value", i) 140 return false, errors.E(errors.Invalid, err) 141 } 142 input += dcrutil.Amount(in.ValueIn) 143 } 144 return PaysHighFees(input, tx), nil 145 }