// Copyright (c) 2013-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package mempool import ( "fmt" "time" "github.com/kaspanet/kaspad/blockdag" "github.com/kaspanet/kaspad/txscript" "github.com/kaspanet/kaspad/util" "github.com/kaspanet/kaspad/wire" ) const ( // maxStandardP2SHSigOps is the maximum number of signature operations // that are considered standard in a pay-to-script-hash script. maxStandardP2SHSigOps = 15 // maxStandardSigScriptSize is the maximum size allowed for a // transaction input signature script to be considered standard. This // value allows for a 15-of-15 CHECKMULTISIG pay-to-script-hash with // compressed keys. // // The form of the overall script is: OP_0 <15 signatures> OP_PUSHDATA2 // <2 bytes len> [OP_15 <15 pubkeys> OP_15 OP_CHECKMULTISIG] // // For the p2sh script portion, each of the 15 compressed pubkeys are // 33 bytes (plus one for the OP_DATA_33 opcode), and the thus it totals // to (15*34)+3 = 513 bytes. Next, each of the 15 signatures is a max // of 73 bytes (plus one for the OP_DATA_73 opcode). Also, there is one // extra byte for the initial extra OP_0 push and 3 bytes for the // OP_PUSHDATA2 needed to specify the 513 bytes for the script push. // That brings the total to 1+(15*74)+3+513 = 1627. This value also // adds a few extra bytes to provide a little buffer. // (1 + 15*74 + 3) + (15*34 + 3) + 23 = 1650 maxStandardSigScriptSize = 1650 // MaxStandardTxSize is the maximum size allowed for transactions that // are considered standard and will therefore be relayed and considered // for mining. MaxStandardTxSize = 100000 // DefaultMinRelayTxFee is the minimum fee in sompi that is required // for a transaction to be treated as free for relay and mining // purposes. It is also used to help determine if a transaction is // considered dust and as a base for calculating minimum required fees // for larger transactions. This value is in sompi/1000 bytes. DefaultMinRelayTxFee = util.Amount(1000) ) // calcMinRequiredTxRelayFee returns the minimum transaction fee required for a // transaction with the passed serialized size to be accepted into the memory // pool and relayed. func calcMinRequiredTxRelayFee(serializedSize int64, minRelayTxFee util.Amount) int64 { // Calculate the minimum fee for a transaction to be allowed into the // mempool and relayed by scaling the base fee (which is the minimum // free transaction relay fee). minTxRelayFee is in sompi/kB so // multiply by serializedSize (which is in bytes) and divide by 1000 to // get minimum sompis. minFee := (serializedSize * int64(minRelayTxFee)) / 1000 if minFee == 0 && minRelayTxFee > 0 { minFee = int64(minRelayTxFee) } // Set the minimum fee to the maximum possible value if the calculated // fee is not in the valid range for monetary amounts. if minFee < 0 || minFee > util.MaxSompi { minFee = util.MaxSompi } return minFee } // 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 // standard form and, for pay-to-script-hash, does not have more than // maxStandardP2SHSigOps signature operations. func checkInputsStandard(tx *util.Tx, utxoSet blockdag.UTXOSet) error { // NOTE: The reference implementation also does a coinbase check here, // but coinbases have already been rejected prior to calling this // function so no need to recheck. for i, txIn := range tx.MsgTx().TxIn { // It is safe to elide existence and index checks here since // they have already been checked prior to calling this // function. entry, _ := utxoSet.Get(txIn.PreviousOutpoint) originScriptPubKey := entry.ScriptPubKey() switch txscript.GetScriptClass(originScriptPubKey) { case txscript.ScriptHashTy: numSigOps := txscript.GetPreciseSigOpCount( txIn.SignatureScript, originScriptPubKey, true) if numSigOps > maxStandardP2SHSigOps { str := fmt.Sprintf("transaction input #%d has "+ "%d signature operations which is more "+ "than the allowed max amount of %d", i, numSigOps, maxStandardP2SHSigOps) return txRuleError(wire.RejectNonstandard, str) } case txscript.NonStandardTy: str := fmt.Sprintf("transaction input #%d has a "+ "non-standard script form", i) return txRuleError(wire.RejectNonstandard, str) } } return nil } // isDust returns whether or not the passed transaction output amount is // considered dust or not based on the passed minimum transaction relay fee. // Dust is defined in terms of the minimum transaction relay fee. In // particular, if the cost to the network to spend coins is more than 1/3 of the // minimum transaction relay fee, it is considered dust. func isDust(txOut *wire.TxOut, minRelayTxFee util.Amount) bool { // Unspendable outputs are considered dust. if txscript.IsUnspendable(txOut.ScriptPubKey) { return true } // The total serialized size consists of the output and the associated // input script to redeem it. Since there is no input script // to redeem it yet, use the minimum size of a typical input script. // // Pay-to-pubkey-hash bytes breakdown: // // Output to hash (34 bytes): // 8 value, 1 script len, 25 script [1 OP_DUP, 1 OP_HASH_160, // 1 OP_DATA_20, 20 hash, 1 OP_EQUALVERIFY, 1 OP_CHECKSIG] // // Input with compressed pubkey (148 bytes): // 36 prev outpoint, 1 script len, 107 script [1 OP_DATA_72, 72 sig, // 1 OP_DATA_33, 33 compressed pubkey], 4 sequence // // Input with uncompressed pubkey (180 bytes): // 36 prev outpoint, 1 script len, 139 script [1 OP_DATA_72, 72 sig, // 1 OP_DATA_65, 65 compressed pubkey], 4 sequence // // Pay-to-pubkey bytes breakdown: // // Output to compressed pubkey (44 bytes): // 8 value, 1 script len, 35 script [1 OP_DATA_33, // 33 compressed pubkey, 1 OP_CHECKSIG] // // Output to uncompressed pubkey (76 bytes): // 8 value, 1 script len, 67 script [1 OP_DATA_65, 65 pubkey, // 1 OP_CHECKSIG] // // Input (114 bytes): // 36 prev outpoint, 1 script len, 73 script [1 OP_DATA_72, // 72 sig], 4 sequence // // Theoretically this could examine the script type of the output script // 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, // 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 // common. // // 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 // that figure is used. totalSize := txOut.SerializeSize() + 148 // 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. // minFreeTxRelayFee is in sompi/KB, so multiply by 1000 to // convert to bytes. // // Using the typical values for a pay-to-pubkey-hash transaction from // the breakdown above and the default minimum free transaction relay // fee of 1000, this equates to values less than 546 sompi being // considered dust. // // The following is equivalent to (value/totalSize) * (1/3) * 1000 // without needing to do floating point math. return txOut.Value*1000/(3*uint64(totalSize)) < uint64(minRelayTxFee) } // checkTransactionStandard performs a series of checks on a transaction to // ensure it is a "standard" transaction. A standard transaction is one that // conforms to several additional limiting cases over what is considered a // "sane" transaction such as having a version in the supported range, being // finalized, conforming to more stringent size constraints, having scripts // of recognized forms, and not containing "dust" outputs (those that are // so small it costs more to process them than they are worth). func checkTransactionStandard(tx *util.Tx, blueScore uint64, medianTimePast time.Time, policy *Policy) error { // The transaction must be a currently supported version. msgTx := tx.MsgTx() if msgTx.Version > policy.MaxTxVersion || msgTx.Version < 1 { str := fmt.Sprintf("transaction version %d is not in the "+ "valid range of %d-%d", msgTx.Version, 1, policy.MaxTxVersion) return txRuleError(wire.RejectNonstandard, str) } // The transaction must be finalized to be standard and therefore // considered for inclusion in a block. if !blockdag.IsFinalizedTransaction(tx, blueScore, medianTimePast) { return txRuleError(wire.RejectNonstandard, "transaction is not finalized") } // Since extremely large transactions with a lot of inputs can cost // almost as much to process as the sender fees, limit the maximum // size of a transaction. This also helps mitigate CPU exhaustion // attacks. serializedLen := msgTx.SerializeSize() if serializedLen > MaxStandardTxSize { str := fmt.Sprintf("transaction size of %d is larger than max "+ "allowed size of %d", serializedLen, MaxStandardTxSize) return txRuleError(wire.RejectNonstandard, str) } for i, txIn := range msgTx.TxIn { // Each transaction input signature script must not exceed the // maximum size allowed for a standard transaction. See // the comment on maxStandardSigScriptSize for more details. sigScriptLen := len(txIn.SignatureScript) if sigScriptLen > maxStandardSigScriptSize { str := fmt.Sprintf("transaction input %d: signature "+ "script size of %d bytes is large than max "+ "allowed size of %d bytes", i, sigScriptLen, maxStandardSigScriptSize) return txRuleError(wire.RejectNonstandard, str) } // Each transaction input signature script must only contain // opcodes which push data onto the stack. isPushOnly, err := txscript.IsPushOnlyScript(txIn.SignatureScript) if err != nil { str := fmt.Sprintf("transaction input %d: IsPushOnlyScript: %t. Error %s", i, isPushOnly, err) return txRuleError(wire.RejectNonstandard, str) } if !isPushOnly { str := fmt.Sprintf("transaction input %d: signature "+ "script is not push only", i) return txRuleError(wire.RejectNonstandard, str) } } // None of the output public key scripts can be a non-standard script or // be "dust". for i, txOut := range msgTx.TxOut { scriptClass := txscript.GetScriptClass(txOut.ScriptPubKey) if scriptClass == txscript.NonStandardTy { str := fmt.Sprintf("transaction output %d: non-standard script form", i) return txRuleError(wire.RejectNonstandard, str) } if isDust(txOut, policy.MinRelayTxFee) { str := fmt.Sprintf("transaction output %d: payment "+ "of %d is dust", i, txOut.Value) return txRuleError(wire.RejectDust, str) } } return nil }