I don't really find this fn to be a UniValue helper, pretty much all the logic is sighash specific. While thinking for a better place to sit, I ended up thinking the better solution may be to keep it where it is but just remove the (imo unnecessary) dependency on UV? Offloading the default value and type check to a helper function like ParseSighashString is opaque, and even though my approach here is slightly more verbose in the callsite, I find the clarity of what's happening to be worth it (different callsites may require different behaviour in case of wrong types or sighash_str).
What do you think?
(I think returning a std::optional instead of throwing is probably even better, but orthogonal to this pull)
<details>
<summary>git diff on master</summary>
diff --git a/src/core_io.h b/src/core_io.h
index 997f3bfd5b..ea07042b82 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -46,7 +46,7 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
*/
bool ParseHashStr(const std::string& strHex, uint256& result);
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
-int ParseSighashString(const UniValue& sighash);
+int ParseSighashString(const std::string& sighash);
// core_write.cpp
UniValue ValueFromAmount(const CAmount amount);
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 84cd559b7f..31f121206c 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -252,26 +252,20 @@ std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strN
return ParseHex(strHex);
}
-int ParseSighashString(const UniValue& sighash)
+int ParseSighashString(const std::string& sighash_str)
{
- int hash_type = SIGHASH_DEFAULT;
- if (!sighash.isNull()) {
- static std::map<std::string, int> map_sighash_values = {
- {std::string("DEFAULT"), int(SIGHASH_DEFAULT)},
- {std::string("ALL"), int(SIGHASH_ALL)},
- {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
- {std::string("NONE"), int(SIGHASH_NONE)},
- {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)},
- {std::string("SINGLE"), int(SIGHASH_SINGLE)},
- {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)},
- };
- const std::string& strHashType = sighash.get_str();
- const auto& it = map_sighash_values.find(strHashType);
- if (it != map_sighash_values.end()) {
- hash_type = it->second;
- } else {
- throw std::runtime_error(strHashType + " is not a valid sighash parameter.");
- }
+ static std::map<std::string, int> map_sighash_values = {
+ {std::string("DEFAULT"), int(SIGHASH_DEFAULT)},
+ {std::string("ALL"), int(SIGHASH_ALL)},
+ {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)},
+ {std::string("NONE"), int(SIGHASH_NONE)},
+ {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)},
+ {std::string("SINGLE"), int(SIGHASH_SINGLE)},
+ {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)},
+ };
+ const auto& it{map_sighash_values.find(sighash_str)};
+ if (it != map_sighash_values.end()) {
+ return it->second;
}
- return hash_type;
+ throw std::runtime_error(sighash_str + " is not a valid sighash parameter.");
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index eb0200ccf5..e0e30b2d9e 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1964,7 +1964,7 @@ RPCHelpMan descriptorprocesspsbt()
EvalDescriptorStringOrObject(descs[i], provider, /*expand_priv=*/true);
}
- int sighash_type = ParseSighashString(request.params[2]);
+ int sighash_type{request.params[2].isNull() ? SIGHASH_DEFAULT: ParseSighashString(request.params[2].get_str())};
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
bool finalize = request.params[4].isNull() ? true : request.params[4].get_bool();
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 3a6fa39e4d..7272cca1c5 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -289,7 +289,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)
{
- int nHashType = ParseSighashString(hashType);
+ int nHashType{hashType.isNull() ? SIGHASH_DEFAULT : ParseSighashString(hashType.get_str())};
// Script verification errors
std::map<int, bilingual_str> input_errors;
diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp
index bfa856211d..5cb2c64f26 100644
--- a/src/test/fuzz/parse_univalue.cpp
+++ b/src/test/fuzz/parse_univalue.cpp
@@ -73,10 +73,6 @@ FUZZ_TARGET(parse_univalue, .init = initialize_parse_univalue)
} catch (const UniValue&) {
} catch (const std::runtime_error&) {
}
- try {
- (void)ParseSighashString(univalue);
- } catch (const std::runtime_error&) {
- }
try {
(void)AmountFromValue(univalue);
} catch (const UniValue&) {
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index b695d4bed3..3d74b12ef7 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -943,7 +943,7 @@ RPCHelpMan signrawtransactionwithwallet()
// Parse the prevtxs array
ParsePrevouts(request.params[1], nullptr, coins);
- int nHashType = ParseSighashString(request.params[2]);
+ int nHashType{request.params[2].isNull() ? SIGHASH_DEFAULT: ParseSighashString(request.params[2].get_str())};
// Script verification errors
std::map<int, bilingual_str> input_errors;
@@ -1587,7 +1587,7 @@ RPCHelpMan walletprocesspsbt()
}
// Get the sighash type
- int nHashType = ParseSighashString(request.params[2]);
+ int nHashType{request.params[2].isNull() ? SIGHASH_DEFAULT: ParseSighashString(request.params[2].get_str())};
// Fill transaction with our data and also sign
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
</details>