From 99993425afc2c352b26e678b7ffbc74362ac3527 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 12 Nov 2021 14:11:02 +0100 Subject: rpc: Only allow specific types to be P2(W)SH wrapped in decodescript --- src/rpc/rawtransaction.cpp | 67 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 10 deletions(-) (limited to 'src/rpc') diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b369cce014..bd56e584e1 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -552,8 +552,10 @@ static RPCHelpMan decodescript() {RPCResult::Type::STR, "asm", "Script public key"}, {RPCResult::Type::STR, "type", "The output type (e.g. " + GetAllOutputTypes() + ")"}, {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"}, - {RPCResult::Type::STR, "p2sh", /* optional */ true, "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"}, - {RPCResult::Type::OBJ, "segwit", /* optional */ true, "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)", + {RPCResult::Type::STR, "p2sh", /*optional=*/true, + "address of P2SH script wrapping this redeem script (not returned for types that should not be wrapped)"}, + {RPCResult::Type::OBJ, "segwit", /*optional=*/true, + "Result of a witness script public key wrapping this redeem script (not returned for types that should not be wrapped)", { {RPCResult::Type::STR, "asm", "String representation of the script public key"}, {RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"}, @@ -584,22 +586,68 @@ static RPCHelpMan decodescript() std::vector> solutions_data; const TxoutType which_type{Solver(script, solutions_data)}; - if (which_type != TxoutType::SCRIPTHASH) { - // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, - // don't return the address for a P2SH of the P2SH. + const bool can_wrap{[&] { + switch (which_type) { + case TxoutType::MULTISIG: + case TxoutType::NONSTANDARD: + case TxoutType::PUBKEY: + case TxoutType::PUBKEYHASH: + case TxoutType::WITNESS_V0_KEYHASH: + case TxoutType::WITNESS_V0_SCRIPTHASH: + // Can be wrapped if the checks below pass + break; + case TxoutType::NULL_DATA: + case TxoutType::SCRIPTHASH: + case TxoutType::WITNESS_UNKNOWN: + case TxoutType::WITNESS_V1_TAPROOT: + // Should not be wrapped + return false; + } // no default case, so the compiler can warn about missing cases + if (!script.HasValidOps() || script.IsUnspendable()) { + return false; + } + for (CScript::const_iterator it{script.begin()}; it != script.end();) { + opcodetype op; + CHECK_NONFATAL(script.GetOp(it, op)); + if (op == OP_CHECKSIGADD || IsOpSuccess(op)) { + return false; + } + } + return true; + }()}; + + if (can_wrap) { r.pushKV("p2sh", EncodeDestination(ScriptHash(script))); // P2SH and witness programs cannot be wrapped in P2WSH, if this script // is a witness program, don't return addresses for a segwit programs. - if (which_type == TxoutType::PUBKEY || which_type == TxoutType::PUBKEYHASH || which_type == TxoutType::MULTISIG || which_type == TxoutType::NONSTANDARD) { + const bool can_wrap_P2WSH{[&] { + switch (which_type) { + case TxoutType::MULTISIG: + case TxoutType::PUBKEY: // Uncompressed pubkeys cannot be used with segwit checksigs. // If the script contains an uncompressed pubkey, skip encoding of a segwit program. - if ((which_type == TxoutType::PUBKEY) || (which_type == TxoutType::MULTISIG)) { for (const auto& solution : solutions_data) { if ((solution.size() != 1) && !CPubKey(solution).IsCompressed()) { - return r; + return false; } } - } + return true; + case TxoutType::NONSTANDARD: + case TxoutType::PUBKEYHASH: + // Can be P2WSH wrapped + return true; + case TxoutType::NULL_DATA: + case TxoutType::SCRIPTHASH: + case TxoutType::WITNESS_UNKNOWN: + case TxoutType::WITNESS_V0_KEYHASH: + case TxoutType::WITNESS_V0_SCRIPTHASH: + case TxoutType::WITNESS_V1_TAPROOT: + // Should not be wrapped + return false; + } // no default case, so the compiler can warn about missing cases + CHECK_NONFATAL(false); + }()}; + if (can_wrap_P2WSH) { UniValue sr(UniValue::VOBJ); CScript segwitScr; if (which_type == TxoutType::PUBKEY) { @@ -608,7 +656,6 @@ static RPCHelpMan decodescript() segwitScr = GetScriptForDestination(WitnessV0KeyHash(uint160{solutions_data[0]})); } else { // Scripts that are not fit for P2WPKH are encoded as P2WSH. - // Newer segwit program versions should be considered when then become available. segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script)); } ScriptPubKeyToUniv(segwitScr, sr, /* include_hex */ true); -- cgit v1.2.3