mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-05-17 23:56:39 +00:00
Refactor rawtransaction's SignTransaction into generic SignTransaction function
This commit is contained in:
parent
5e12a61044
commit
2c52b59d0a
@ -272,55 +272,27 @@ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
|
|||||||
{
|
{
|
||||||
int nHashType = ParseSighashString(hashType);
|
int nHashType = ParseSighashString(hashType);
|
||||||
|
|
||||||
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
|
||||||
|
|
||||||
// Script verification errors
|
// Script verification errors
|
||||||
|
std::map<int, std::string> input_errors;
|
||||||
|
|
||||||
|
bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors);
|
||||||
|
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result)
|
||||||
|
{
|
||||||
|
// Make errors UniValue
|
||||||
UniValue vErrors(UniValue::VARR);
|
UniValue vErrors(UniValue::VARR);
|
||||||
|
for (const auto& err_pair : input_errors) {
|
||||||
// Use CTransaction for the constant parts of the
|
if (err_pair.second == "Missing amount") {
|
||||||
// transaction to avoid rehashing.
|
// This particular error needs to be an exception for some reason
|
||||||
const CTransaction txConst(mtx);
|
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coins.at(mtx.vin.at(err_pair.first).prevout).out.ToString()));
|
||||||
// Sign what we can:
|
|
||||||
for (unsigned int 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.out.scriptPubKey;
|
|
||||||
const CAmount& amount = coin->second.out.nValue;
|
|
||||||
|
|
||||||
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, nHashType), prevPubKey, sigdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateInput(txin, sigdata);
|
|
||||||
|
|
||||||
// amount must be specified for valid segwit signature
|
|
||||||
if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
|
|
||||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptError serror = SCRIPT_ERR_OK;
|
|
||||||
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
|
|
||||||
if (serror == SCRIPT_ERR_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 if (serror == SCRIPT_ERR_SIG_NULLFAIL) {
|
|
||||||
// Verification failed (possibly due to insufficient signatures).
|
|
||||||
TxInErrorToJSON(txin, vErrors, "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)");
|
|
||||||
} else {
|
|
||||||
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second);
|
||||||
}
|
}
|
||||||
bool fComplete = vErrors.empty();
|
|
||||||
|
|
||||||
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
|
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
|
||||||
result.pushKV("complete", fComplete);
|
result.pushKV("complete", complete);
|
||||||
if (!vErrors.empty()) {
|
if (!vErrors.empty()) {
|
||||||
if (result.exists("errors")) {
|
if (result.exists("errors")) {
|
||||||
vErrors.push_backV(result["errors"].getValues());
|
vErrors.push_backV(result["errors"].getValues());
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
|
#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class FillableSigningProvider;
|
class FillableSigningProvider;
|
||||||
class UniValue;
|
class UniValue;
|
||||||
@ -24,6 +25,7 @@ class SigningProvider;
|
|||||||
* @param result JSON object where signed transaction results accumulate
|
* @param result JSON object where signed transaction results accumulate
|
||||||
*/
|
*/
|
||||||
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result);
|
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result);
|
||||||
|
void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, std::map<int, std::string>& input_errors, UniValue& result);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a prevtxs UniValue array and get the map of coins from it
|
* Parse a prevtxs UniValue array and get the map of coins from it
|
||||||
|
@ -465,3 +465,51 @@ bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, int nHashType, std::map<int, std::string>& input_errors)
|
||||||
|
{
|
||||||
|
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
||||||
|
|
||||||
|
// Use CTransaction for the constant parts of the
|
||||||
|
// transaction to avoid rehashing.
|
||||||
|
const CTransaction txConst(mtx);
|
||||||
|
// Sign what we can:
|
||||||
|
for (unsigned int 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()) {
|
||||||
|
input_errors[i] = "Input not found or already spent";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const CScript& prevPubKey = coin->second.out.scriptPubKey;
|
||||||
|
const CAmount& amount = coin->second.out.nValue;
|
||||||
|
|
||||||
|
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, nHashType), prevPubKey, sigdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateInput(txin, sigdata);
|
||||||
|
|
||||||
|
// amount must be specified for valid segwit signature
|
||||||
|
if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
|
||||||
|
input_errors[i] = "Missing amount";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptError serror = SCRIPT_ERR_OK;
|
||||||
|
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
|
||||||
|
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
|
||||||
|
// Unable to sign input and verification failed (possible attempt to partially sign).
|
||||||
|
input_errors[i] = "Unable to sign input, invalid stack size (possibly missing key)";
|
||||||
|
} else if (serror == SCRIPT_ERR_SIG_NULLFAIL) {
|
||||||
|
// Verification failed (possibly due to insufficient signatures).
|
||||||
|
input_errors[i] = "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)";
|
||||||
|
} else {
|
||||||
|
input_errors[i] = ScriptErrorString(serror);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return input_errors.empty();
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#ifndef BITCOIN_SCRIPT_SIGN_H
|
#ifndef BITCOIN_SCRIPT_SIGN_H
|
||||||
#define BITCOIN_SCRIPT_SIGN_H
|
#define BITCOIN_SCRIPT_SIGN_H
|
||||||
|
|
||||||
|
#include <coins.h>
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include <pubkey.h>
|
#include <pubkey.h>
|
||||||
#include <script/interpreter.h>
|
#include <script/interpreter.h>
|
||||||
@ -168,4 +169,7 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script);
|
|||||||
/** Check whether a scriptPubKey is known to be segwit. */
|
/** Check whether a scriptPubKey is known to be segwit. */
|
||||||
bool IsSegWitOutput(const SigningProvider& provider, const CScript& script);
|
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, std::string>& input_errors);
|
||||||
|
|
||||||
#endif // BITCOIN_SCRIPT_SIGN_H
|
#endif // BITCOIN_SCRIPT_SIGN_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user