diff --git a/src/bloom.cpp b/src/bloom.cpp --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -164,10 +164,9 @@ insert(COutPoint(txid, i)); } else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) { - txnouttype type; std::vector> vSolutions; - if (Solver(txout.scriptPubKey, type, vSolutions) && - (type == TX_PUBKEY || type == TX_MULTISIG)) { + txnouttype type = Solver(txout.scriptPubKey, vSolutions); + if (type == TX_PUBKEY || type == TX_MULTISIG) { insert(COutPoint(txid, i)); } } diff --git a/src/core_write.cpp b/src/core_write.cpp --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -181,8 +181,7 @@ out.pushKV("hex", HexStr(script.begin(), script.end())); std::vector> solns; - txnouttype type; - Solver(script, type, solns); + txnouttype type = Solver(script, solns); out.pushKV("type", GetTxnOutputType(type)); CTxDestination address; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -40,11 +40,11 @@ bool IsStandard(const CScript &scriptPubKey, txnouttype &whichType) { std::vector> vSolutions; - if (!Solver(scriptPubKey, whichType, vSolutions)) { - return false; - } + whichType = Solver(scriptPubKey, vSolutions); - if (whichType == TX_MULTISIG) { + if (whichType == TX_NONSTANDARD) { + return false; + } else if (whichType == TX_MULTISIG) { uint8_t m = vSolutions.front()[0]; uint8_t n = vSolutions.back()[0]; // Support up to x-of-3 multisig txns as standard @@ -66,7 +66,7 @@ } } - return whichType != TX_NONSTANDARD; + return true; } bool IsStandardTx(const CTransaction &tx, std::string &reason) { @@ -151,14 +151,10 @@ const CTxOut &prev = mapInputs.GetOutputFor(in); std::vector> vSolutions; - txnouttype whichType; - // get the scriptPubKey corresponding to this input: - const CScript &prevScript = prev.scriptPubKey; - if (!Solver(prevScript, whichType, vSolutions)) { + txnouttype whichType = Solver(prev.scriptPubKey, vSolutions); + if (whichType == TX_NONSTANDARD) { return false; - } - - if (whichType == TX_SCRIPTHASH) { + } else if (whichType == TX_SCRIPTHASH) { std::vector> stack; // convert the scriptSig into a stack, so we can inspect the // redeemScript diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -53,8 +53,7 @@ IsMineResult ret = IsMineResult::NO; std::vector vSolutions; - txnouttype whichType; - Solver(scriptPubKey, whichType, vSolutions); + txnouttype whichType = Solver(scriptPubKey, vSolutions); CKeyID keyID; switch (whichType) { diff --git a/src/script/sign.cpp b/src/script/sign.cpp --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -112,9 +112,7 @@ std::vector sig; std::vector vSolutions; - if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) { - return false; - } + whichTypeRet = Solver(scriptPubKey, vSolutions); switch (whichTypeRet) { case TX_NONSTANDARD: @@ -310,9 +308,8 @@ } // Get scripts - txnouttype script_type; std::vector> solutions; - Solver(txout.scriptPubKey, script_type, solutions); + txnouttype script_type = Solver(txout.scriptPubKey, solutions); CScript next_script = txout.scriptPubKey; if (script_type == TX_SCRIPTHASH && !stack.script.empty() && @@ -324,7 +321,7 @@ next_script = std::move(redeem_script); // Get redeemScript type - Solver(next_script, script_type, solutions); + script_type = Solver(next_script, solutions); stack.script.pop_back(); } if (script_type == TX_MULTISIG && !stack.script.empty()) { diff --git a/src/script/standard.h b/src/script/standard.h --- a/src/script/standard.h +++ b/src/script/standard.h @@ -83,12 +83,12 @@ * script hash, for P2PKH it will contain the key hash, etc. * * @param[in] scriptPubKey Script to parse - * @param[out] typeRet The script type * @param[out] vSolutionsRet Vector of parsed pubkeys and hashes - * @return True if script matches standard template + * @return The script type. TX_NONSTANDARD represents a + * failed solve. */ -bool Solver(const CScript &scriptPubKey, txnouttype &typeRet, - std::vector> &vSolutionsRet); +txnouttype Solver(const CScript &scriptPubKey, + std::vector> &vSolutionsRet); /** * Parse a standard scriptPubKey for the destination address. Assigns result to diff --git a/src/script/standard.cpp b/src/script/standard.cpp --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -98,19 +98,18 @@ return (it + 1 == script.end()); } -bool Solver(const CScript &scriptPubKey, txnouttype &typeRet, - std::vector> &vSolutionsRet) { +txnouttype Solver(const CScript &scriptPubKey, + std::vector> &vSolutionsRet) { vSolutionsRet.clear(); // Shortcut for pay-to-script-hash, which are more constrained than the // other types: // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL if (scriptPubKey.IsPayToScriptHash()) { - typeRet = TX_SCRIPTHASH; std::vector hashBytes(scriptPubKey.begin() + 2, scriptPubKey.begin() + 22); vSolutionsRet.push_back(hashBytes); - return true; + return TX_SCRIPTHASH; } // Provably prunable, data-carrying output @@ -120,47 +119,39 @@ // script. if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin() + 1)) { - typeRet = TX_NULL_DATA; - return true; + return TX_NULL_DATA; } std::vector data; if (MatchPayToPubkey(scriptPubKey, data)) { - typeRet = TX_PUBKEY; vSolutionsRet.push_back(std::move(data)); - return true; + return TX_PUBKEY; } if (MatchPayToPubkeyHash(scriptPubKey, data)) { - typeRet = TX_PUBKEYHASH; vSolutionsRet.push_back(std::move(data)); - return true; + return TX_PUBKEYHASH; } unsigned int required; std::vector> keys; if (MatchMultisig(scriptPubKey, required, keys)) { - typeRet = TX_MULTISIG; // safe as required is in range 1..16 vSolutionsRet.push_back({static_cast(required)}); vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end()); // safe as size is in range 1..16 vSolutionsRet.push_back({static_cast(keys.size())}); - return true; + return TX_MULTISIG; } vSolutionsRet.clear(); - typeRet = TX_NONSTANDARD; - return false; + return TX_NONSTANDARD; } bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet) { std::vector vSolutions; - txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) { - return false; - } + txnouttype whichType = Solver(scriptPubKey, vSolutions); if (whichType == TX_PUBKEY) { CPubKey pubKey(vSolutions[0]); @@ -187,12 +178,11 @@ std::vector &addressRet, int &nRequiredRet) { addressRet.clear(); - typeRet = TX_NONSTANDARD; std::vector vSolutions; - if (!Solver(scriptPubKey, typeRet, vSolutions)) { + typeRet = Solver(scriptPubKey, vSolutions); + if (typeRet == TX_NONSTANDARD) { return false; - } - if (typeRet == TX_NULL_DATA) { + } else if (typeRet == TX_NULL_DATA) { // This is data, not addresses return false; } diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -47,14 +47,12 @@ } CScript s; - txnouttype whichType; std::vector> solutions; // TX_PUBKEY s.clear(); s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_PUBKEY); BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0])); @@ -62,8 +60,7 @@ s.clear(); s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_PUBKEYHASH); BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); @@ -71,8 +68,7 @@ CScript redeemScript(s); // initialize with leftover P2PKH script s.clear(); s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_SCRIPTHASH); BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript))); @@ -80,8 +76,7 @@ s.clear(); s << OP_1 << ToByteVector(pubkeys[0]) << ToByteVector(pubkeys[1]) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_MULTISIG); BOOST_CHECK_EQUAL(solutions.size(), 4U); BOOST_CHECK(solutions[0] == std::vector({1})); BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); @@ -91,8 +86,7 @@ s.clear(); s << OP_2 << ToByteVector(pubkeys[0]) << ToByteVector(pubkeys[1]) << ToByteVector(pubkeys[2]) << OP_3 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_MULTISIG); BOOST_CHECK_EQUAL(solutions.size(), 5U); BOOST_CHECK(solutions[0] == std::vector({2})); BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); @@ -104,15 +98,13 @@ s.clear(); s << OP_RETURN << std::vector({0}) << std::vector({75}) << std::vector({255}); - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NULL_DATA); BOOST_CHECK_EQUAL(solutions.size(), 0U); // TX_WITNESS_V0_KEYHASH s.clear(); s << OP_0 << ToByteVector(pubkeys[0].GetID()); - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0U); // TX_WITNESS_V0_SCRIPTHASH @@ -123,15 +115,13 @@ s.clear(); s << OP_0 << ToByteVector(scriptHash); - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0U); // TX_NONSTANDARD s.clear(); s << OP_9 << OP_ADD << OP_11 << OP_EQUAL; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); // Try some non-minimal PUSHDATA pushes in various standard scripts @@ -140,8 +130,7 @@ s.clear(); AppendPush(s, pushdataop, ToByteVector(pubkeys[0])); s << OP_CHECKSIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); // mutated TX_PUBKEYHASH @@ -149,8 +138,7 @@ s << OP_DUP << OP_HASH160; AppendPush(s, pushdataop, ToByteVector(pubkeys[0].GetID())); s << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); // mutated TX_SCRIPTHASH @@ -158,8 +146,7 @@ s << OP_HASH160; AppendPush(s, pushdataop, ToByteVector(CScriptID(redeemScript))); s << OP_EQUAL; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); // mutated TX_MULTISIG -- pubkey @@ -167,8 +154,7 @@ s << OP_1; AppendPush(s, pushdataop, ToByteVector(pubkeys[0])); s << ToByteVector(pubkeys[1]) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); // mutated TX_MULTISIG -- num_signatures @@ -176,8 +162,7 @@ AppendPush(s, pushdataop, {1}); s << ToByteVector(pubkeys[0]) << ToByteVector(pubkeys[1]) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); // mutated TX_MULTISIG -- num_pubkeys @@ -185,8 +170,7 @@ s << OP_1 << ToByteVector(pubkeys[0]) << ToByteVector(pubkeys[1]); AppendPush(s, pushdataop, {2}); s << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); } @@ -195,14 +179,12 @@ s.clear(); s << std::vector{1} << ToByteVector(pubkeys[0]) << ToByteVector(pubkeys[1]) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); s.clear(); s << OP_1 << ToByteVector(pubkeys[0]) << ToByteVector(pubkeys[1]) << std::vector{2} << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); BOOST_CHECK_EQUAL(solutions.size(), 0); // Non-minimal pushes in OP_RETURN scripts are standard (some OP_RETURN @@ -212,8 +194,7 @@ OP_RETURN, OP_RESERVED, OP_PUSHDATA1, 0x00, 0x01, 0x01, OP_PUSHDATA4, 0x01, 0x00, 0x00, 0x00, 0xaa}; s.assign(op_return_nonminimal.begin(), op_return_nonminimal.end()); - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NULL_DATA); BOOST_CHECK_EQUAL(solutions.size(), 0); } @@ -224,59 +205,58 @@ pubkey = key.GetPubKey(); CScript s; - txnouttype whichType; std::vector> solutions; // TX_PUBKEY with incorrectly sized pubkey s.clear(); s << std::vector(30, 0x01) << OP_CHECKSIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_PUBKEYHASH with incorrectly sized key hash s.clear(); s << OP_DUP << OP_HASH160 << ToByteVector(pubkey) << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_SCRIPTHASH with incorrectly sized script hash s.clear(); s << OP_HASH160 << std::vector(21, 0x01) << OP_EQUAL; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_MULTISIG 0/2 s.clear(); s << OP_0 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_MULTISIG 2/1 s.clear(); s << OP_2 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_MULTISIG n = 2 with 1 pubkey s.clear(); s << OP_1 << ToByteVector(pubkey) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_MULTISIG n = 1 with 0 pubkeys s.clear(); s << OP_1 << OP_1 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_NULL_DATA with other opcodes s.clear(); s << OP_RETURN << std::vector({75}) << OP_ADD; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_WITNESS with unknown version s.clear(); s << OP_1 << ToByteVector(pubkey); - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_WITNESS with incorrect program size s.clear(); s << OP_0 << std::vector(19, 0x01); - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); } BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4664,9 +4664,8 @@ void ProcessSubScript(const CScript &subscript, UniValue &obj, bool include_addresses = false) const { // Always present: script type and redeemscript - txnouttype which_type; std::vector> solutions_data; - Solver(subscript, which_type, solutions_data); + txnouttype which_type = Solver(subscript, solutions_data); obj.pushKV("script", GetTxnOutputType(which_type)); obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));