diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h --- a/src/rpc/rawtransaction_util.h +++ b/src/rpc/rawtransaction_util.h @@ -6,6 +6,7 @@ #define BITCOIN_RPC_RAWTRANSACTION_UTIL_H #include +#include class FillableSigningProvider; class CChainParams; @@ -28,6 +29,10 @@ void SignTransaction(CMutableTransaction &mtx, const SigningProvider *keystore, const std::map &coins, const UniValue &hashType, UniValue &result); +void SignTransactionResultToJSON(CMutableTransaction &mtx, bool complete, + const std::map &coins, + std::map &input_errors, + UniValue &result); /** * Parse a prevtxs UniValue array and get the map of coins from it diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -263,64 +263,35 @@ "Signature must use SIGHASH_FORKID"); } - // Script verification errors. - UniValue vErrors(UniValue::VARR); - - // Use CTransaction for the constant parts of the transaction to avoid - // rehashing. - const CTransaction txConst(mtx); - // Sign what we can: - for (size_t i = 0; i < mtx.vin.size(); i++) { - CTxIn &txin = mtx.vin[i]; - auto coin = coins.find(txin.prevout); - if (coin == coins.end() || coin->second.IsSpent()) { - TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); - continue; - } - - const CScript &prevPubKey = coin->second.GetTxOut().scriptPubKey; - const Amount amount = coin->second.GetTxOut().nValue; - - SignatureData sigdata = - DataFromTransaction(mtx, i, coin->second.GetTxOut()); - // Only sign SIGHASH_SINGLE if there's a corresponding output: - if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) || - (i < mtx.vout.size())) { - ProduceSignature(*keystore, - MutableTransactionSignatureCreator(&mtx, i, amount, - sigHashType), - prevPubKey, sigdata); - } - - UpdateInput(txin, sigdata); + // Script verification errors + std::map input_errors; - // amount must be specified for valid signature - if (amount == MAX_MONEY) { - throw JSONRPCError(RPC_TYPE_ERROR, - strprintf("Missing amount for %s", - coin->second.GetTxOut().ToString())); - } + bool complete = + SignTransaction(mtx, keystore, coins, sigHashType, input_errors); + SignTransactionResultToJSON(mtx, complete, coins, input_errors, result); +} - ScriptError serror = ScriptError::OK; - if (!VerifyScript( - txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, - TransactionSignatureChecker(&txConst, i, amount), &serror)) { - if (serror == ScriptError::INVALID_STACK_OPERATION) { - // Unable to sign input and verification failed (possible - // attempt to partially sign). - TxInErrorToJSON(txin, vErrors, - "Unable to sign input, invalid stack size " - "(possibly missing key)"); - } else { - TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); - } +void SignTransactionResultToJSON(CMutableTransaction &mtx, bool complete, + const std::map &coins, + std::map &input_errors, + UniValue &result) { + // Make errors UniValue + UniValue vErrors(UniValue::VARR); + for (const auto &err_pair : input_errors) { + if (err_pair.second == "Missing amount") { + // This particular error needs to be an exception for some reason + throw JSONRPCError( + RPC_TYPE_ERROR, + strprintf("Missing amount for %s", + coins.at(mtx.vin.at(err_pair.first).prevout) + .GetTxOut() + .ToString())); } + TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second); } - bool fComplete = vErrors.empty(); - result.pushKV("hex", EncodeHexTx(CTransaction(mtx))); - result.pushKV("complete", fComplete); + result.pushKV("complete", complete); if (!vErrors.empty()) { if (result.exists("errors")) { vErrors.push_backV(result["errors"].getValues()); diff --git a/src/script/sign.h b/src/script/sign.h --- a/src/script/sign.h +++ b/src/script/sign.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_SCRIPT_SIGN_H #define BITCOIN_SCRIPT_SIGN_H +#include #include #include #include