refactor: use SignOptions for SignTransaction
What changed, and why it matters
This is a straightforward code cleanup change. It replaces a plain integer parameter for the signature hash type with a small options structure called SignOptions. All existing callers are updated to pass the same value inside the new structure. There is no change to what the code actually does or to any security behavior.
No security action needed. Treat as normal refactoring review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors SignTransaction to accept a const SignOptions& instead of a raw int nHashType. A new struct SignOptions { int sighash_type{SIGHASH_DEFAULT}; } is added in src/script/sign.h. All call sites pass {.sighash_type = …} with the same value previously passed directly. The implementation in src/script/sign.cpp reads options.sighash_type where it previously read nHashType. A separate one-line cleanup in wallet/scriptpubkeyman.cpp adds an explicit /out_sigdata=/nullptr comment but makes no functional change.
Changed components
src/script/sign.cppsrc/script/sign.hsrc/rpc/rawtransaction_util.cppsrc/wallet/scriptpubkeyman.cpptest/bench/fuzz callers of SignTransactionInspect captured patch +18 / −13
diff --git a/src/bench/sign_transaction.cpp b/src/bench/sign_transaction.cpp
index 5405265b..63a5d97f 100644
--- a/src/bench/sign_transaction.cpp
+++ b/src/bench/sign_transaction.cpp
@@ -68,7 +68,7 @@ static void SignTransactionSingleInput(benchmark::Bench& bench, InputType input_
const CScript& prev_spk = prev_spks[(iter++) % prev_spks.size()];
coins[prevout] = Coin(CTxOut(10000, prev_spk), /*nHeightIn=*/100, /*fCoinBaseIn=*/false);
std::map<int, bilingual_str> input_errors;
- bool complete = SignTransaction(tx, &keystore, coins, SIGHASH_ALL, input_errors);
+ bool complete = SignTransaction(tx, &keystore, coins, {.sighash_type = SIGHASH_ALL}, input_errors);
assert(complete);
});
}
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index 0a92a057..8f07fd34 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -77,7 +77,8 @@ static void VerifyScriptBench(benchmark::Bench& bench, ScriptType script_type)
{txSpend.vin[0].prevout, Coin(txCredit.vout[0], /*nHeightIn=*/100, /*fCoinBaseIn=*/false)}
};
std::map<int, bilingual_str> input_errors;
- assert(SignTransaction(txSpend, &keystore, coins, SIGHASH_ALL, input_errors));
+ bool complete = SignTransaction(txSpend, &keystore, coins, {.sighash_type = SIGHASH_ALL}, input_errors);
+ assert(complete);
// Weak sanity check on witness data to ensure we produced the intended spending type
assert(txSpend.vin[0].scriptWitness.stack.size() == ExpectedWitnessStackSize(script_type));
txdata.Init(txSpend, /*spent_outputs=*/{txCredit.vout[0]});
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index d3a1b875..f8b6e208 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -318,7 +318,7 @@ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
// Script verification errors
std::map<int, bilingual_str> input_errors;
- bool complete = SignTransaction(mtx, keystore, coins, *nHashType, input_errors);
+ bool complete = SignTransaction(mtx, keystore, coins, {.sighash_type = *nHashType}, input_errors);
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
}
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 9ea2157f..6184cc54 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -1006,9 +1006,9 @@ bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
return false;
}
-bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, int nHashType, std::map<int, bilingual_str>& input_errors)
+bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const SignOptions& options, std::map<int, bilingual_str>& input_errors)
{
- bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
+ bool fHashSingle = ((options.sighash_type & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
@@ -1044,7 +1044,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(*keystore, MutableTransactionSignatureCreator(mtx, i, amount, &txdata, nHashType), prevPubKey, sigdata);
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(mtx, i, amount, &txdata, options.sighash_type), prevPubKey, sigdata);
}
UpdateInput(txin, sigdata);
diff --git a/src/script/sign.h b/src/script/sign.h
index 8d502755..c0b1b768 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -25,6 +25,10 @@ struct bilingual_str;
struct CMutableTransaction;
struct SignatureData;
+struct SignOptions {
+ int sighash_type{SIGHASH_DEFAULT};
+};
+
/** Interface for signature creators. */
class BaseSignatureCreator {
public:
@@ -120,6 +124,6 @@ void UpdateInput(CTxIn& input, const SignatureData& data);
bool IsSegWitOutput(const SigningProvider& provider, const CScript& script);
/** Sign the CMutableTransaction */
-bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* provider, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors);
+bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* provider, const std::map<COutPoint, Coin>& coins, const SignOptions& options, std::map<int, bilingual_str>& input_errors);
#endif // BITCOIN_SCRIPT_SIGN_H
diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp
index e4cf8c2a..c709a902 100644
--- a/src/test/fuzz/script_sign.cpp
+++ b/src/test/fuzz/script_sign.cpp
@@ -132,7 +132,7 @@ FUZZ_TARGET(script_sign, .init = initialize_script_sign)
}
std::map<COutPoint, Coin> coins{ConsumeCoins(fuzzed_data_provider)};
std::map<int, bilingual_str> input_errors;
- (void)SignTransaction(sign_transaction_tx_to, &provider, coins, fuzzed_data_provider.ConsumeIntegral<int>(), input_errors);
+ (void)SignTransaction(sign_transaction_tx_to, &provider, coins, {.sighash_type = fuzzed_data_provider.ConsumeIntegral<int>()}, input_errors);
}
}
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 5c50553e..39c691c3 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -481,7 +481,7 @@ std::pair<CMutableTransaction, CAmount> TestChain100Setup::CreateValidTransactio
// - Default signature hashing type
int nHashType = SIGHASH_ALL;
std::map<int, bilingual_str> input_errors;
- assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors));
+ assert(SignTransaction(mempool_txn, &keystore, input_coins, {.sighash_type = nHashType}, input_errors));
CAmount current_fee = inputs_amount - std::accumulate(outputs.begin(), outputs.end(), CAmount(0),
[](const CAmount& acc, const CTxOut& out) {
return acc + out.nValue;
@@ -498,7 +498,7 @@ std::pair<CMutableTransaction, CAmount> TestChain100Setup::CreateValidTransactio
mempool_txn.vout[fee_output.value()].nValue -= deduction;
// Re-sign since an output has changed
input_errors.clear();
- assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors));
+ assert(SignTransaction(mempool_txn, &keystore, input_coins, {.sighash_type = nHashType}, input_errors));
current_fee = target_fee;
}
}
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 92152e7c..911b247d 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -1285,7 +1285,7 @@ bool DescriptorScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const s
keys->Merge(std::move(*coin_keys));
}
- return ::SignTransaction(tx, keys.get(), coins, sighash, input_errors);
+ return ::SignTransaction(tx, keys.get(), coins, {.sighash_type = sighash}, input_errors);
}
SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const
@@ -1376,7 +1376,7 @@ std::optional<PSBTError> DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTran
}
}
- PSBTError res = SignPSBTInput(HidingSigningProvider(keys.get(), /*hide_secret=*/!options.sign, /*hide_origin=*/!options.bip32_derivs), psbtx, i, &txdata, options, nullptr);
+ PSBTError res = SignPSBTInput(HidingSigningProvider(keys.get(), /*hide_secret=*/!options.sign, /*hide_origin=*/!options.bip32_derivs), psbtx, i, &txdata, options, /*out_sigdata=*/nullptr);
if (res != PSBTError::OK && res != PSBTError::INCOMPLETE) {
return res;
}
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 54f45362..5cd47f67 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -55,7 +55,7 @@ static CMutableTransaction TestSimpleSpend(const CTransaction& from, uint32_t in
std::map<COutPoint, Coin> coins;
coins[mtx.vin[0].prevout].out = from.vout[index];
std::map<int, bilingual_str> input_errors;
- BOOST_CHECK(SignTransaction(mtx, &keystore, coins, SIGHASH_ALL, input_errors));
+ BOOST_CHECK(SignTransaction(mtx, &keystore, coins, {.sighash_type = SIGHASH_ALL}, input_errors));
return mtx;
}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.