// 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. However, it should also be noted // that standard inputs also are those which have a clean stack after execution // and only contain pushed data in their signature scripts. This function does // not perform those checks because the script engine already does this more // accurately and concisely via the txscript.ScriptVerifyCleanStack and // txscript.ScriptVerifySigPushOnly flags. func checkInputsStandard(tx *btcutil.Tx, utxoView *blockchain.UtxoViewpoint) 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. prevOut := txIn.PreviousOutPoint entry := utxoView.LookupEntry(&prevOut.Hash) originPkScript := entry.PkScriptByIndex(prevOut.Index) switch txscript.GetScriptClass(originPkScript) { case txscript.ScriptHashTy: numSigOps := txscript.GetPreciseSigOpCount( txIn.SignatureScript, originPkScript, 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 }
// CountP2SHSigOps returns the number of signature operations for all input // transactions which are of the pay-to-script-hash type. This uses the // precise, signature operation counting mechanism from the script engine which // requires access to the input transaction scripts. func CountP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool, txStore TxStore) (int, error) { // Coinbase transactions have no interesting inputs. if isCoinBaseTx { return 0, nil } // Accumulate the number of signature operations in all transaction // inputs. msgTx := tx.MsgTx() totalSigOps := 0 for _, txIn := range msgTx.TxIn { // Ensure the referenced input transaction is available. txInHash := &txIn.PreviousOutPoint.Hash originTx, exists := txStore[*txInHash] if !exists || originTx.Err != nil || originTx.Tx == nil { str := fmt.Sprintf("unable to find input transaction "+ "%v referenced from transaction %v", txInHash, tx.Sha()) return 0, ruleError(ErrMissingTx, str) } originMsgTx := originTx.Tx.MsgTx() // Ensure the output index in the referenced transaction is // available. originTxIndex := txIn.PreviousOutPoint.Index if originTxIndex >= uint32(len(originMsgTx.TxOut)) { str := fmt.Sprintf("out of bounds input index %d in "+ "transaction %v referenced from transaction %v", originTxIndex, txInHash, tx.Sha()) return 0, ruleError(ErrBadTxInput, str) } // We're only interested in pay-to-script-hash types, so skip // this input if it's not one. pkScript := originMsgTx.TxOut[originTxIndex].PkScript if !txscript.IsPayToScriptHash(pkScript) { continue } // Count the precise number of signature operations in the // referenced public key script. sigScript := txIn.SignatureScript numSigOps := txscript.GetPreciseSigOpCount(sigScript, pkScript, true) // We could potentially overflow the accumulator so check for // overflow. lastSigOps := totalSigOps totalSigOps += numSigOps if totalSigOps < lastSigOps { str := fmt.Sprintf("the public key script from "+ "output index %d in transaction %v contains "+ "too many signature operations - overflow", originTxIndex, txInHash) return 0, ruleError(ErrTooManySigOps, str) } } return totalSigOps, nil }
// TestGetPreciseSigOps ensures the more precise signature operation counting // mechanism which includes signatures in P2SH scripts works as expected. func TestGetPreciseSigOps(t *testing.T) { t.Parallel() tests := []struct { name string scriptSig []byte nSigOps int err error }{ { name: "scriptSig doesn't parse", scriptSig: []byte{txscript.OP_PUSHDATA1, 2}, err: txscript.ErrStackShortScript, }, { name: "scriptSig isn't push only", scriptSig: []byte{txscript.OP_1, txscript.OP_DUP}, nSigOps: 0, }, { name: "scriptSig length 0", scriptSig: nil, nSigOps: 0, }, { name: "No script at the end", // No script at end but still push only. scriptSig: []byte{txscript.OP_1, txscript.OP_1}, nSigOps: 0, }, { name: "pushed script doesn't parse", scriptSig: []byte{txscript.OP_DATA_2, txscript.OP_PUSHDATA1, 2}, err: txscript.ErrStackShortScript, }, } // The signature in the p2sh script is nonsensical for the tests since // this script will never be executed. What matters is that it matches // the right pattern. pkScript := mustParseShortForm("HASH160 DATA_20 0x433ec2ac1ffa1b7b7d0" + "27f564529c57197f9ae88 EQUAL") for _, test := range tests { count := txscript.GetPreciseSigOpCount(test.scriptSig, pkScript, true) if count != test.nSigOps { t.Errorf("%s: expected count of %d, got %d", test.name, test.nSigOps, count) } } }
// CountP2SHSigOps returns the number of signature operations for all input // transactions which are of the pay-to-script-hash type. This uses the // precise, signature operation counting mechanism from the script engine which // requires access to the input transaction scripts. func CountP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool, utxoView *UtxoViewpoint) (int, error) { // Coinbase transactions have no interesting inputs. if isCoinBaseTx { return 0, nil } // Accumulate the number of signature operations in all transaction // inputs. msgTx := tx.MsgTx() totalSigOps := 0 for txInIndex, txIn := range msgTx.TxIn { // Ensure the referenced input transaction is available. originTxHash := &txIn.PreviousOutPoint.Hash originTxIndex := txIn.PreviousOutPoint.Index txEntry := utxoView.LookupEntry(originTxHash) if txEntry == nil || txEntry.IsOutputSpent(originTxIndex) { str := fmt.Sprintf("unable to find unspent output "+ "%v referenced from transaction %s:%d", txIn.PreviousOutPoint, tx.Hash(), txInIndex) return 0, ruleError(ErrMissingTx, str) } // We're only interested in pay-to-script-hash types, so skip // this input if it's not one. pkScript := txEntry.PkScriptByIndex(originTxIndex) if !txscript.IsPayToScriptHash(pkScript) { continue } // Count the precise number of signature operations in the // referenced public key script. sigScript := txIn.SignatureScript numSigOps := txscript.GetPreciseSigOpCount(sigScript, pkScript, true) // We could potentially overflow the accumulator so check for // overflow. lastSigOps := totalSigOps totalSigOps += numSigOps if totalSigOps < lastSigOps { str := fmt.Sprintf("the public key script from output "+ "%v contains too many signature operations - "+ "overflow", txIn.PreviousOutPoint) return 0, ruleError(ErrTooManySigOps, str) } } return totalSigOps, nil }