mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 02:25:40 +01: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);
|
||||
|
||||
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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()) {
|
||||
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));
|
||||
}
|
||||
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).out.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());
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
class FillableSigningProvider;
|
||||
class UniValue;
|
||||
@ -24,6 +25,7 @@ class SigningProvider;
|
||||
* @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 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
|
||||
|
@ -465,3 +465,51 @@ 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, 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
|
||||
#define BITCOIN_SCRIPT_SIGN_H
|
||||
|
||||
#include <coins.h>
|
||||
#include <hash.h>
|
||||
#include <pubkey.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. */
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user