Skip to content

Commit 2c52b59

Browse files
committed
Refactor rawtransaction's SignTransaction into generic SignTransaction function
1 parent 5e12a61 commit 2c52b59

File tree

4 files changed

+68
-42
lines changed

4 files changed

+68
-42
lines changed

src/rpc/rawtransaction_util.cpp

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -272,55 +272,27 @@ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
272272
{
273273
int nHashType = ParseSighashString(hashType);
274274

275-
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
276-
277275
// Script verification errors
278-
UniValue vErrors(UniValue::VARR);
279-
280-
// Use CTransaction for the constant parts of the
281-
// transaction to avoid rehashing.
282-
const CTransaction txConst(mtx);
283-
// Sign what we can:
284-
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
285-
CTxIn& txin = mtx.vin[i];
286-
auto coin = coins.find(txin.prevout);
287-
if (coin == coins.end() || coin->second.IsSpent()) {
288-
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
289-
continue;
290-
}
291-
const CScript& prevPubKey = coin->second.out.scriptPubKey;
292-
const CAmount& amount = coin->second.out.nValue;
293-
294-
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
295-
// Only sign SIGHASH_SINGLE if there's a corresponding output:
296-
if (!fHashSingle || (i < mtx.vout.size())) {
297-
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
298-
}
299-
300-
UpdateInput(txin, sigdata);
276+
std::map<int, std::string> input_errors;
301277

302-
// amount must be specified for valid segwit signature
303-
if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
304-
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
305-
}
278+
bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors);
279+
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
280+
}
306281

307-
ScriptError serror = SCRIPT_ERR_OK;
308-
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
309-
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
310-
// Unable to sign input and verification failed (possible attempt to partially sign).
311-
TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
312-
} else if (serror == SCRIPT_ERR_SIG_NULLFAIL) {
313-
// Verification failed (possibly due to insufficient signatures).
314-
TxInErrorToJSON(txin, vErrors, "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)");
315-
} else {
316-
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
317-
}
282+
void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result)
283+
{
284+
// Make errors UniValue
285+
UniValue vErrors(UniValue::VARR);
286+
for (const auto& err_pair : input_errors) {
287+
if (err_pair.second == "Missing amount") {
288+
// This particular error needs to be an exception for some reason
289+
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coins.at(mtx.vin.at(err_pair.first).prevout).out.ToString()));
318290
}
291+
TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second);
319292
}
320-
bool fComplete = vErrors.empty();
321293

322294
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
323-
result.pushKV("complete", fComplete);
295+
result.pushKV("complete", complete);
324296
if (!vErrors.empty()) {
325297
if (result.exists("errors")) {
326298
vErrors.push_backV(result["errors"].getValues());

src/rpc/rawtransaction_util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
77

88
#include <map>
9+
#include <string>
910

1011
class FillableSigningProvider;
1112
class UniValue;
@@ -24,6 +25,7 @@ class SigningProvider;
2425
* @param result JSON object where signed transaction results accumulate
2526
*/
2627
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result);
28+
void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result);
2729

2830
/**
2931
* Parse a prevtxs UniValue array and get the map of coins from it

src/script/sign.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,3 +465,51 @@ bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
465465
}
466466
return false;
467467
}
468+
469+
bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, int nHashType, std::map<int, std::string>& input_errors)
470+
{
471+
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
472+
473+
// Use CTransaction for the constant parts of the
474+
// transaction to avoid rehashing.
475+
const CTransaction txConst(mtx);
476+
// Sign what we can:
477+
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
478+
CTxIn& txin = mtx.vin[i];
479+
auto coin = coins.find(txin.prevout);
480+
if (coin == coins.end() || coin->second.IsSpent()) {
481+
input_errors[i] = "Input not found or already spent";
482+
continue;
483+
}
484+
const CScript& prevPubKey = coin->second.out.scriptPubKey;
485+
const CAmount& amount = coin->second.out.nValue;
486+
487+
SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
488+
// Only sign SIGHASH_SINGLE if there's a corresponding output:
489+
if (!fHashSingle || (i < mtx.vout.size())) {
490+
ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
491+
}
492+
493+
UpdateInput(txin, sigdata);
494+
495+
// amount must be specified for valid segwit signature
496+
if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
497+
input_errors[i] = "Missing amount";
498+
continue;
499+
}
500+
501+
ScriptError serror = SCRIPT_ERR_OK;
502+
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
503+
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
504+
// Unable to sign input and verification failed (possible attempt to partially sign).
505+
input_errors[i] = "Unable to sign input, invalid stack size (possibly missing key)";
506+
} else if (serror == SCRIPT_ERR_SIG_NULLFAIL) {
507+
// Verification failed (possibly due to insufficient signatures).
508+
input_errors[i] = "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)";
509+
} else {
510+
input_errors[i] = ScriptErrorString(serror);
511+
}
512+
}
513+
}
514+
return input_errors.empty();
515+
}

src/script/sign.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifndef BITCOIN_SCRIPT_SIGN_H
77
#define BITCOIN_SCRIPT_SIGN_H
88

9+
#include <coins.h>
910
#include <hash.h>
1011
#include <pubkey.h>
1112
#include <script/interpreter.h>
@@ -168,4 +169,7 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script);
168169
/** Check whether a scriptPubKey is known to be segwit. */
169170
bool IsSegWitOutput(const SigningProvider& provider, const CScript& script);
170171

172+
/** Sign the CMutableTransaction */
173+
bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* provider, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors);
174+
171175
#endif // BITCOIN_SCRIPT_SIGN_H

0 commit comments

Comments
 (0)