Merge #14978: Factor out PSBT utilities from RPCs for use in GUI code; related refactoring.

102faad81 Factor out combine / finalize / extract PSBT helpers (Glenn Willen)
78b9893d0 Remove op== on PSBTs; check compatibility in Merge (Glenn Willen)
bd0dbe876 Switch away from exceptions in refactored tx code (Glenn Willen)
c6c3d42a7 Move PSBT definitions and code to separate files (Glenn Willen)
81cd95884 Factor BroadcastTransaction out of sendrawtransaction (Glenn Willen)
c734aaa15 Split DecodePSBT into Base64 and Raw versions (Glenn Willen)
162ffefd2 Add pf_invalid arg to std::string DecodeBase{32,64} (Glenn Willen)

Pull request description:

  * Move most PSBT definitions into psbt.h.
  * Move most PSBT RPC utilities into psbt.{h,cpp}.
  * Move wallet-touching PSBT RPC utilities (FillPSBT) into
      wallet/psbtwallet.{h,cpp}.
  * Switch exceptions from JSONRPCError() to new PSBTException class.
  * Split DecodePSBT into DecodeBase64PSBT (old behavior) and DecodeRawPSBT.
  * Add one new version of DecodeBase64 utility in strencodings.h (and
      corresponding DecodeBase32 for completeness).
  * Factor BroadcastTransaction utility function out of sendrawtransaction RPC
      handler in rpc/rawtransaction.cpp

  Note: For those keeping score at home wondering why refactor, this is in anticipation of (and developed in parallel with) a change to actually introduce GUI use of all this stuff, which is already under development and working-ish.

Tree-SHA512: 2197c448e657421f430943025357597e7b06c4c377d5d4b2622b9edea52a7193c48843dd731abb3a88ac4023a9c88d211991e0a9b740c22f2e1cbe72adefe390
This commit is contained in:
MeshCollider 2019-02-14 21:48:31 +13:00
commit 2452c6cc0a
No known key found for this signature in database
GPG key ID: D300116E1C875A3D
20 changed files with 1229 additions and 929 deletions

View file

@ -152,6 +152,7 @@ BITCOIN_CORE_H = \
netaddress.h \ netaddress.h \
netbase.h \ netbase.h \
netmessagemaker.h \ netmessagemaker.h \
node/transaction.h \
noui.h \ noui.h \
optional.h \ optional.h \
outputtype.h \ outputtype.h \
@ -161,6 +162,7 @@ BITCOIN_CORE_H = \
policy/rbf.h \ policy/rbf.h \
pow.h \ pow.h \
protocol.h \ protocol.h \
psbt.h \
random.h \ random.h \
reverse_iterator.h \ reverse_iterator.h \
reverselock.h \ reverselock.h \
@ -209,6 +211,7 @@ BITCOIN_CORE_H = \
wallet/db.h \ wallet/db.h \
wallet/feebumper.h \ wallet/feebumper.h \
wallet/fees.h \ wallet/fees.h \
wallet/psbtwallet.h \
wallet/rpcwallet.h \ wallet/rpcwallet.h \
wallet/wallet.h \ wallet/wallet.h \
wallet/walletdb.h \ wallet/walletdb.h \
@ -255,6 +258,7 @@ libbitcoin_server_a_SOURCES = \
miner.cpp \ miner.cpp \
net.cpp \ net.cpp \
net_processing.cpp \ net_processing.cpp \
node/transaction.cpp \
noui.cpp \ noui.cpp \
outputtype.cpp \ outputtype.cpp \
policy/fees.cpp \ policy/fees.cpp \
@ -308,6 +312,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/feebumper.cpp \ wallet/feebumper.cpp \
wallet/fees.cpp \ wallet/fees.cpp \
wallet/init.cpp \ wallet/init.cpp \
wallet/psbtwallet.cpp \
wallet/rpcdump.cpp \ wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \ wallet/rpcwallet.cpp \
wallet/wallet.cpp \ wallet/wallet.cpp \
@ -421,6 +426,7 @@ libbitcoin_common_a_SOURCES = \
netaddress.cpp \ netaddress.cpp \
netbase.cpp \ netbase.cpp \
policy/feerate.cpp \ policy/feerate.cpp \
psbt.cpp \
protocol.cpp \ protocol.cpp \
scheduler.cpp \ scheduler.cpp \
script/descriptor.cpp \ script/descriptor.cpp \

View file

@ -37,7 +37,11 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
*/ */
bool ParseHashStr(const std::string& strHex, uint256& result); bool ParseHashStr(const std::string& strHex, uint256& result);
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName); std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
NODISCARD bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
//! Decode a base64ed PSBT into a PartiallySignedTransaction
NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
NODISCARD bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, const std::string& raw_psbt, std::string& error);
int ParseSighashString(const UniValue& sighash); int ParseSighashString(const UniValue& sighash);
// core_write.cpp // core_write.cpp

View file

@ -4,6 +4,7 @@
#include <core_io.h> #include <core_io.h>
#include <psbt.h>
#include <primitives/block.h> #include <primitives/block.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <script/script.h> #include <script/script.h>
@ -176,10 +177,20 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
return true; return true;
} }
bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error) bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
{ {
std::vector<unsigned char> tx_data = DecodeBase64(base64_tx.c_str()); bool invalid;
CDataStream ss_data(tx_data, SER_NETWORK, PROTOCOL_VERSION); std::string tx_data = DecodeBase64(base64_tx, &invalid);
if (invalid) {
error = "invalid base64";
return false;
}
return DecodeRawPSBT(psbt, tx_data, error);
}
bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data, std::string& error)
{
CDataStream ss_data(tx_data.data(), tx_data.data() + tx_data.size(), SER_NETWORK, PROTOCOL_VERSION);
try { try {
ss_data >> psbt; ss_data >> psbt;
if (!ss_data.empty()) { if (!ss_data.empty()) {

115
src/node/transaction.cpp Normal file
View file

@ -0,0 +1,115 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/validation.h>
#include <net.h>
#include <txmempool.h>
#include <validation.h>
#include <validationinterface.h>
#include <node/transaction.h>
#include <future>
const char* TransactionErrorString(const TransactionError err)
{
switch (err) {
case TransactionError::OK:
return "No error";
case TransactionError::MISSING_INPUTS:
return "Missing inputs";
case TransactionError::ALREADY_IN_CHAIN:
return "Transaction already in block chain";
case TransactionError::P2P_DISABLED:
return "Peer-to-peer functionality missing or disabled";
case TransactionError::MEMPOOL_REJECTED:
return "Transaction rejected by AcceptToMemoryPool";
case TransactionError::MEMPOOL_ERROR:
return "AcceptToMemoryPool failed";
case TransactionError::INVALID_PSBT:
return "PSBT is not sane";
case TransactionError::PSBT_MISMATCH:
return "PSBTs not compatible (different transactions)";
case TransactionError::SIGHASH_MISMATCH:
return "Specified sighash value does not match existing value";
case TransactionError::UNKNOWN_ERROR:
default: break;
}
return "Unknown error";
}
bool BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, TransactionError& error, std::string& err_string, const bool allowhighfees)
{
std::promise<void> promise;
hashTx = tx->GetHash();
CAmount nMaxRawTxFee = maxTxFee;
if (allowhighfees)
nMaxRawTxFee = 0;
{ // cs_main scope
LOCK(cs_main);
CCoinsViewCache &view = *pcoinsTip;
bool fHaveChain = false;
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
fHaveChain = !existingCoin.IsSpent();
}
bool fHaveMempool = mempool.exists(hashTx);
if (!fHaveMempool && !fHaveChain) {
// push to local node and sync with wallets
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) {
if (state.IsInvalid()) {
err_string = FormatStateMessage(state);
error = TransactionError::MEMPOOL_REJECTED;
return false;
} else {
if (fMissingInputs) {
error = TransactionError::MISSING_INPUTS;
return false;
}
err_string = FormatStateMessage(state);
error = TransactionError::MEMPOOL_ERROR;
return false;
}
} else {
// If wallet is enabled, ensure that the wallet has been made aware
// of the new transaction prior to returning. This prevents a race
// where a user might call sendrawtransaction with a transaction
// to/from their wallet, immediately call some wallet RPC, and get
// a stale result because callbacks have not yet been processed.
CallFunctionInValidationInterfaceQueue([&promise] {
promise.set_value();
});
}
} else if (fHaveChain) {
error = TransactionError::ALREADY_IN_CHAIN;
return false;
} else {
// Make sure we don't block forever if re-sending
// a transaction already in mempool.
promise.set_value();
}
} // cs_main
promise.get_future().wait();
if(!g_connman) {
error = TransactionError::P2P_DISABLED;
return false;
}
CInv inv(MSG_TX, hashTx);
g_connman->ForEachNode([&inv](CNode* pnode)
{
pnode->PushInventory(inv);
});
return true;
}

43
src/node/transaction.h Normal file
View file

@ -0,0 +1,43 @@
// Copyright (c) 2017-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_NODE_TRANSACTION_H
#define BITCOIN_NODE_TRANSACTION_H
#include <primitives/transaction.h>
#include <uint256.h>
enum class TransactionError {
OK = 0,
UNKNOWN_ERROR,
MISSING_INPUTS,
ALREADY_IN_CHAIN,
P2P_DISABLED,
MEMPOOL_REJECTED,
MEMPOOL_ERROR,
INVALID_PSBT,
PSBT_MISMATCH,
SIGHASH_MISMATCH,
ERROR_COUNT
};
#define TRANSACTION_ERR_LAST TransactionError::ERROR_COUNT
const char* TransactionErrorString(const TransactionError error);
/**
* Broadcast a transaction
*
* @param[in] tx the transaction to broadcast
* @param[out] &txid the txid of the transaction, if successfully broadcast
* @param[out] &error reference to UniValue to fill with error info on failure
* @param[out] &err_string reference to std::string to fill with error string if available
* @param[in] allowhighfees whether to allow fees exceeding maxTxFee
* return true on success, false on error (and fills in `error`)
*/
bool BroadcastTransaction(CTransactionRef tx, uint256& txid, TransactionError& error, std::string& err_string, bool allowhighfees = false);
#endif // BITCOIN_NODE_TRANSACTION_H

283
src/psbt.cpp Normal file
View file

@ -0,0 +1,283 @@
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <psbt.h>
#include <util/strencodings.h>
PartiallySignedTransaction::PartiallySignedTransaction(const CMutableTransaction& tx) : tx(tx)
{
inputs.resize(tx.vin.size());
outputs.resize(tx.vout.size());
}
bool PartiallySignedTransaction::IsNull() const
{
return !tx && inputs.empty() && outputs.empty() && unknown.empty();
}
bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
{
// Prohibited to merge two PSBTs over different transactions
if (tx->GetHash() != psbt.tx->GetHash()) {
return false;
}
for (unsigned int i = 0; i < inputs.size(); ++i) {
inputs[i].Merge(psbt.inputs[i]);
}
for (unsigned int i = 0; i < outputs.size(); ++i) {
outputs[i].Merge(psbt.outputs[i]);
}
unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
return true;
}
bool PartiallySignedTransaction::IsSane() const
{
for (PSBTInput input : inputs) {
if (!input.IsSane()) return false;
}
return true;
}
bool PSBTInput::IsNull() const
{
return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty();
}
void PSBTInput::FillSignatureData(SignatureData& sigdata) const
{
if (!final_script_sig.empty()) {
sigdata.scriptSig = final_script_sig;
sigdata.complete = true;
}
if (!final_script_witness.IsNull()) {
sigdata.scriptWitness = final_script_witness;
sigdata.complete = true;
}
if (sigdata.complete) {
return;
}
sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end());
if (!redeem_script.empty()) {
sigdata.redeem_script = redeem_script;
}
if (!witness_script.empty()) {
sigdata.witness_script = witness_script;
}
for (const auto& key_pair : hd_keypaths) {
sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
}
}
void PSBTInput::FromSignatureData(const SignatureData& sigdata)
{
if (sigdata.complete) {
partial_sigs.clear();
hd_keypaths.clear();
redeem_script.clear();
witness_script.clear();
if (!sigdata.scriptSig.empty()) {
final_script_sig = sigdata.scriptSig;
}
if (!sigdata.scriptWitness.IsNull()) {
final_script_witness = sigdata.scriptWitness;
}
return;
}
partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end());
if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
redeem_script = sigdata.redeem_script;
}
if (witness_script.empty() && !sigdata.witness_script.empty()) {
witness_script = sigdata.witness_script;
}
for (const auto& entry : sigdata.misc_pubkeys) {
hd_keypaths.emplace(entry.second);
}
}
void PSBTInput::Merge(const PSBTInput& input)
{
if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
witness_utxo = input.witness_utxo;
non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one.
}
partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
unknown.insert(input.unknown.begin(), input.unknown.end());
if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script;
if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
}
bool PSBTInput::IsSane() const
{
// Cannot have both witness and non-witness utxos
if (!witness_utxo.IsNull() && non_witness_utxo) return false;
// If we have a witness_script or a scriptWitness, we must also have a witness utxo
if (!witness_script.empty() && witness_utxo.IsNull()) return false;
if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false;
return true;
}
void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
{
if (!redeem_script.empty()) {
sigdata.redeem_script = redeem_script;
}
if (!witness_script.empty()) {
sigdata.witness_script = witness_script;
}
for (const auto& key_pair : hd_keypaths) {
sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
}
}
void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
{
if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
redeem_script = sigdata.redeem_script;
}
if (witness_script.empty() && !sigdata.witness_script.empty()) {
witness_script = sigdata.witness_script;
}
for (const auto& entry : sigdata.misc_pubkeys) {
hd_keypaths.emplace(entry.second);
}
}
bool PSBTOutput::IsNull() const
{
return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty();
}
void PSBTOutput::Merge(const PSBTOutput& output)
{
hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end());
unknown.insert(output.unknown.begin(), output.unknown.end());
if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
}
bool PSBTInputSigned(PSBTInput& input)
{
return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
}
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash)
{
PSBTInput& input = psbt.inputs.at(index);
const CMutableTransaction& tx = *psbt.tx;
if (PSBTInputSigned(input)) {
return true;
}
// Fill SignatureData with input info
SignatureData sigdata;
input.FillSignatureData(sigdata);
// Get UTXO
bool require_witness_sig = false;
CTxOut utxo;
// Verify input sanity, which checks that at most one of witness or non-witness utxos is provided.
if (!input.IsSane()) {
return false;
}
if (input.non_witness_utxo) {
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
COutPoint prevout = tx.vin[index].prevout;
if (input.non_witness_utxo->GetHash() != prevout.hash) {
return false;
}
utxo = input.non_witness_utxo->vout[prevout.n];
} else if (!input.witness_utxo.IsNull()) {
utxo = input.witness_utxo;
// When we're taking our information from a witness UTXO, we can't verify it is actually data from
// the output being spent. This is safe in case a witness signature is produced (which includes this
// information directly in the hash), but not for non-witness signatures. Remember that we require
// a witness signature in this situation.
require_witness_sig = true;
} else {
return false;
}
MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
sigdata.witness = false;
bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
// Verify that a witness signature was produced in case one was required.
if (require_witness_sig && !sigdata.witness) return false;
input.FromSignatureData(sigdata);
// If we have a witness signature, use the smaller witness UTXO.
if (sigdata.witness) {
input.witness_utxo = utxo;
input.non_witness_utxo = nullptr;
}
return sig_complete;
}
bool FinalizePSBT(PartiallySignedTransaction& psbtx)
{
// Finalize input signatures -- in case we have partial signatures that add up to a complete
// signature, but have not combined them yet (e.g. because the combiner that created this
// PartiallySignedTransaction did not understand them), this will combine them into a final
// script.
bool complete = true;
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
}
return complete;
}
bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransaction& result)
{
// It's not safe to extract a PSBT that isn't finalized, and there's no easy way to check
// whether a PSBT is finalized without finalizing it, so we just do this.
if (!FinalizePSBT(psbtx)) {
return false;
}
result = *psbtx.tx;
for (unsigned int i = 0; i < result.vin.size(); ++i) {
result.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
result.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness;
}
return true;
}
bool CombinePSBTs(PartiallySignedTransaction& out, TransactionError& error, const std::vector<PartiallySignedTransaction>& psbtxs)
{
out = psbtxs[0]; // Copy the first one
// Merge
for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
if (!out.Merge(*it)) {
error = TransactionError::PSBT_MISMATCH;
return false;
}
}
if (!out.IsSane()) {
error = TransactionError::INVALID_PSBT;
return false;
}
return true;
}

574
src/psbt.h Normal file
View file

@ -0,0 +1,574 @@
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PSBT_H
#define BITCOIN_PSBT_H
#include <attributes.h>
#include <node/transaction.h>
#include <primitives/transaction.h>
#include <pubkey.h>
#include <script/sign.h>
// Magic bytes
static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
// Global types
static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00;
// Input types
static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00;
static constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01;
static constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02;
static constexpr uint8_t PSBT_IN_SIGHASH = 0x03;
static constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04;
static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05;
static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06;
static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08;
// Output types
static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01;
static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
// The separator is 0x00. Reading this in means that the unserializer can interpret it
// as a 0 length key which indicates that this is the separator. The separator has no value.
static constexpr uint8_t PSBT_SEPARATOR = 0x00;
/** A structure for PSBTs which contain per-input information */
struct PSBTInput
{
CTransactionRef non_witness_utxo;
CTxOut witness_utxo;
CScript redeem_script;
CScript witness_script;
CScript final_script_sig;
CScriptWitness final_script_witness;
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
std::map<CKeyID, SigPair> partial_sigs;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
int sighash_type = 0;
bool IsNull() const;
void FillSignatureData(SignatureData& sigdata) const;
void FromSignatureData(const SignatureData& sigdata);
void Merge(const PSBTInput& input);
bool IsSane() const;
PSBTInput() {}
template <typename Stream>
inline void Serialize(Stream& s) const {
// Write the utxo
// If there is a non-witness utxo, then don't add the witness one.
if (non_witness_utxo) {
SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
SerializeToVector(os, non_witness_utxo);
} else if (!witness_utxo.IsNull()) {
SerializeToVector(s, PSBT_IN_WITNESS_UTXO);
SerializeToVector(s, witness_utxo);
}
if (final_script_sig.empty() && final_script_witness.IsNull()) {
// Write any partial signatures
for (auto sig_pair : partial_sigs) {
SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first));
s << sig_pair.second.second;
}
// Write the sighash type
if (sighash_type > 0) {
SerializeToVector(s, PSBT_IN_SIGHASH);
SerializeToVector(s, sighash_type);
}
// Write the redeem script
if (!redeem_script.empty()) {
SerializeToVector(s, PSBT_IN_REDEEMSCRIPT);
s << redeem_script;
}
// Write the witness script
if (!witness_script.empty()) {
SerializeToVector(s, PSBT_IN_WITNESSSCRIPT);
s << witness_script;
}
// Write any hd keypaths
SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION);
}
// Write script sig
if (!final_script_sig.empty()) {
SerializeToVector(s, PSBT_IN_SCRIPTSIG);
s << final_script_sig;
}
// write script witness
if (!final_script_witness.IsNull()) {
SerializeToVector(s, PSBT_IN_SCRIPTWITNESS);
SerializeToVector(s, final_script_witness.stack);
}
// Write unknown things
for (auto& entry : unknown) {
s << entry.first;
s << entry.second;
}
s << PSBT_SEPARATOR;
}
template <typename Stream>
inline void Unserialize(Stream& s) {
// Read loop
bool found_sep = false;
while(!s.empty()) {
// Read
std::vector<unsigned char> key;
s >> key;
// the key is empty if that was actually a separator byte
// This is a special case for key lengths 0 as those are not allowed (except for separator)
if (key.empty()) {
found_sep = true;
break;
}
// First byte of key is the type
unsigned char type = key[0];
// Do stuff based on type
switch(type) {
case PSBT_IN_NON_WITNESS_UTXO:
{
if (non_witness_utxo) {
throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Non-witness utxo key is more than one byte type");
}
// Set the stream to unserialize with witness since this is always a valid network transaction
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() & ~SERIALIZE_TRANSACTION_NO_WITNESS);
UnserializeFromVector(os, non_witness_utxo);
break;
}
case PSBT_IN_WITNESS_UTXO:
if (!witness_utxo.IsNull()) {
throw std::ios_base::failure("Duplicate Key, input witness utxo already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Witness utxo key is more than one byte type");
}
UnserializeFromVector(s, witness_utxo);
break;
case PSBT_IN_PARTIAL_SIG:
{
// Make sure that the key is the size of pubkey + 1
if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey");
}
// Read in the pubkey from key
CPubKey pubkey(key.begin() + 1, key.end());
if (!pubkey.IsFullyValid()) {
throw std::ios_base::failure("Invalid pubkey");
}
if (partial_sigs.count(pubkey.GetID()) > 0) {
throw std::ios_base::failure("Duplicate Key, input partial signature for pubkey already provided");
}
// Read in the signature from value
std::vector<unsigned char> sig;
s >> sig;
// Add to list
partial_sigs.emplace(pubkey.GetID(), SigPair(pubkey, std::move(sig)));
break;
}
case PSBT_IN_SIGHASH:
if (sighash_type > 0) {
throw std::ios_base::failure("Duplicate Key, input sighash type already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Sighash type key is more than one byte type");
}
UnserializeFromVector(s, sighash_type);
break;
case PSBT_IN_REDEEMSCRIPT:
{
if (!redeem_script.empty()) {
throw std::ios_base::failure("Duplicate Key, input redeemScript already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Input redeemScript key is more than one byte type");
}
s >> redeem_script;
break;
}
case PSBT_IN_WITNESSSCRIPT:
{
if (!witness_script.empty()) {
throw std::ios_base::failure("Duplicate Key, input witnessScript already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Input witnessScript key is more than one byte type");
}
s >> witness_script;
break;
}
case PSBT_IN_BIP32_DERIVATION:
{
DeserializeHDKeypaths(s, key, hd_keypaths);
break;
}
case PSBT_IN_SCRIPTSIG:
{
if (!final_script_sig.empty()) {
throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Final scriptSig key is more than one byte type");
}
s >> final_script_sig;
break;
}
case PSBT_IN_SCRIPTWITNESS:
{
if (!final_script_witness.IsNull()) {
throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Final scriptWitness key is more than one byte type");
}
UnserializeFromVector(s, final_script_witness.stack);
break;
}
// Unknown stuff
default:
if (unknown.count(key) > 0) {
throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
}
// Read in the value
std::vector<unsigned char> val_bytes;
s >> val_bytes;
unknown.emplace(std::move(key), std::move(val_bytes));
break;
}
}
if (!found_sep) {
throw std::ios_base::failure("Separator is missing at the end of an input map");
}
}
template <typename Stream>
PSBTInput(deserialize_type, Stream& s) {
Unserialize(s);
}
};
/** A structure for PSBTs which contains per output information */
struct PSBTOutput
{
CScript redeem_script;
CScript witness_script;
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
bool IsNull() const;
void FillSignatureData(SignatureData& sigdata) const;
void FromSignatureData(const SignatureData& sigdata);
void Merge(const PSBTOutput& output);
bool IsSane() const;
PSBTOutput() {}
template <typename Stream>
inline void Serialize(Stream& s) const {
// Write the redeem script
if (!redeem_script.empty()) {
SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT);
s << redeem_script;
}
// Write the witness script
if (!witness_script.empty()) {
SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT);
s << witness_script;
}
// Write any hd keypaths
SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION);
// Write unknown things
for (auto& entry : unknown) {
s << entry.first;
s << entry.second;
}
s << PSBT_SEPARATOR;
}
template <typename Stream>
inline void Unserialize(Stream& s) {
// Read loop
bool found_sep = false;
while(!s.empty()) {
// Read
std::vector<unsigned char> key;
s >> key;
// the key is empty if that was actually a separator byte
// This is a special case for key lengths 0 as those are not allowed (except for separator)
if (key.empty()) {
found_sep = true;
break;
}
// First byte of key is the type
unsigned char type = key[0];
// Do stuff based on type
switch(type) {
case PSBT_OUT_REDEEMSCRIPT:
{
if (!redeem_script.empty()) {
throw std::ios_base::failure("Duplicate Key, output redeemScript already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Output redeemScript key is more than one byte type");
}
s >> redeem_script;
break;
}
case PSBT_OUT_WITNESSSCRIPT:
{
if (!witness_script.empty()) {
throw std::ios_base::failure("Duplicate Key, output witnessScript already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Output witnessScript key is more than one byte type");
}
s >> witness_script;
break;
}
case PSBT_OUT_BIP32_DERIVATION:
{
DeserializeHDKeypaths(s, key, hd_keypaths);
break;
}
// Unknown stuff
default: {
if (unknown.count(key) > 0) {
throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
}
// Read in the value
std::vector<unsigned char> val_bytes;
s >> val_bytes;
unknown.emplace(std::move(key), std::move(val_bytes));
break;
}
}
}
if (!found_sep) {
throw std::ios_base::failure("Separator is missing at the end of an output map");
}
}
template <typename Stream>
PSBTOutput(deserialize_type, Stream& s) {
Unserialize(s);
}
};
/** A version of CTransaction with the PSBT format*/
struct PartiallySignedTransaction
{
boost::optional<CMutableTransaction> tx;
std::vector<PSBTInput> inputs;
std::vector<PSBTOutput> outputs;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
bool IsNull() const;
/** Merge psbt into this. The two psbts must have the same underlying CTransaction (i.e. the
* same actual Bitcoin transaction.) Returns true if the merge succeeded, false otherwise. */
NODISCARD bool Merge(const PartiallySignedTransaction& psbt);
bool IsSane() const;
PartiallySignedTransaction() {}
PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {}
explicit PartiallySignedTransaction(const CMutableTransaction& tx);
template <typename Stream>
inline void Serialize(Stream& s) const {
// magic bytes
s << PSBT_MAGIC_BYTES;
// unsigned tx flag
SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX);
// Write serialized tx to a stream
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
SerializeToVector(os, *tx);
// Write the unknown things
for (auto& entry : unknown) {
s << entry.first;
s << entry.second;
}
// Separator
s << PSBT_SEPARATOR;
// Write inputs
for (const PSBTInput& input : inputs) {
s << input;
}
// Write outputs
for (const PSBTOutput& output : outputs) {
s << output;
}
}
template <typename Stream>
inline void Unserialize(Stream& s) {
// Read the magic bytes
uint8_t magic[5];
s >> magic;
if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) {
throw std::ios_base::failure("Invalid PSBT magic bytes");
}
// Read global data
bool found_sep = false;
while(!s.empty()) {
// Read
std::vector<unsigned char> key;
s >> key;
// the key is empty if that was actually a separator byte
// This is a special case for key lengths 0 as those are not allowed (except for separator)
if (key.empty()) {
found_sep = true;
break;
}
// First byte of key is the type
unsigned char type = key[0];
// Do stuff based on type
switch(type) {
case PSBT_GLOBAL_UNSIGNED_TX:
{
if (tx) {
throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Global unsigned tx key is more than one byte type");
}
CMutableTransaction mtx;
// Set the stream to serialize with non-witness since this should always be non-witness
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
UnserializeFromVector(os, mtx);
tx = std::move(mtx);
// Make sure that all scriptSigs and scriptWitnesses are empty
for (const CTxIn& txin : tx->vin) {
if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) {
throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses.");
}
}
break;
}
// Unknown stuff
default: {
if (unknown.count(key) > 0) {
throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
}
// Read in the value
std::vector<unsigned char> val_bytes;
s >> val_bytes;
unknown.emplace(std::move(key), std::move(val_bytes));
}
}
}
if (!found_sep) {
throw std::ios_base::failure("Separator is missing at the end of the global map");
}
// Make sure that we got an unsigned tx
if (!tx) {
throw std::ios_base::failure("No unsigned transcation was provided");
}
// Read input data
unsigned int i = 0;
while (!s.empty() && i < tx->vin.size()) {
PSBTInput input;
s >> input;
inputs.push_back(input);
// Make sure the non-witness utxo matches the outpoint
if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) {
throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash");
}
++i;
}
// Make sure that the number of inputs matches the number of inputs in the transaction
if (inputs.size() != tx->vin.size()) {
throw std::ios_base::failure("Inputs provided does not match the number of inputs in transaction.");
}
// Read output data
i = 0;
while (!s.empty() && i < tx->vout.size()) {
PSBTOutput output;
s >> output;
outputs.push_back(output);
++i;
}
// Make sure that the number of outputs matches the number of outputs in the transaction
if (outputs.size() != tx->vout.size()) {
throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction.");
}
// Sanity check
if (!IsSane()) {
throw std::ios_base::failure("PSBT is not sane.");
}
}
template <typename Stream>
PartiallySignedTransaction(deserialize_type, Stream& s) {
Unserialize(s);
}
};
/** Checks whether a PSBTInput is already signed. */
bool PSBTInputSigned(PSBTInput& input);
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL);
/**
* Finalizes a PSBT if possible, combining partial signatures.
*
* @param[in,out] &psbtx reference to PartiallySignedTransaction to finalize
* return True if the PSBT is now complete, false otherwise
*/
bool FinalizePSBT(PartiallySignedTransaction& psbtx);
/**
* Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized.
*
* @param[in] &psbtx reference to PartiallySignedTransaction
* @param[out] result CMutableTransaction representing the complete transaction, if successful
* @return True if we successfully extracted the transaction, false otherwise
*/
bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransaction& result);
/**
* Combines PSBTs with the same underlying transaction, resulting in a single PSBT with all partial signatures from each input.
*
* @param[out] &out the combined PSBT, if successful
* @param[out] &error reference to TransactionError to fill with error info on failure
* @param[in] psbtxs the PSBTs to combine
* @return True if we successfully combined the transactions, false if they were not compatible
*/
bool CombinePSBTs(PartiallySignedTransaction& out, TransactionError& error, const std::vector<PartiallySignedTransaction>& psbtxs);
#endif // BITCOIN_PSBT_H

View file

@ -13,10 +13,11 @@
#include <key_io.h> #include <key_io.h>
#include <keystore.h> #include <keystore.h>
#include <merkleblock.h> #include <merkleblock.h>
#include <net.h> #include <node/transaction.h>
#include <policy/policy.h> #include <policy/policy.h>
#include <policy/rbf.h> #include <policy/rbf.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <psbt.h>
#include <rpc/rawtransaction.h> #include <rpc/rawtransaction.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/util.h> #include <rpc/util.h>
@ -24,13 +25,11 @@
#include <script/script_error.h> #include <script/script_error.h>
#include <script/sign.h> #include <script/sign.h>
#include <script/standard.h> #include <script/standard.h>
#include <txmempool.h>
#include <uint256.h> #include <uint256.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <validation.h> #include <validation.h>
#include <validationinterface.h> #include <validationinterface.h>
#include <future>
#include <stdint.h> #include <stdint.h>
#include <univalue.h> #include <univalue.h>
@ -1041,8 +1040,6 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
}, },
}.ToString()); }.ToString());
std::promise<void> promise;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
// parse hex string from parameter // parse hex string from parameter
@ -1050,67 +1047,17 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
if (!DecodeHexTx(mtx, request.params[0].get_str())) if (!DecodeHexTx(mtx, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
CTransactionRef tx(MakeTransactionRef(std::move(mtx))); CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
const uint256& hashTx = tx->GetHash();
CAmount nMaxRawTxFee = maxTxFee; bool allowhighfees = false;
if (!request.params[1].isNull() && request.params[1].get_bool()) if (!request.params[1].isNull()) allowhighfees = request.params[1].get_bool();
nMaxRawTxFee = 0; uint256 txid;
TransactionError err;
{ // cs_main scope std::string err_string;
LOCK(cs_main); if (!BroadcastTransaction(tx, txid, err, err_string, allowhighfees)) {
CCoinsViewCache &view = *pcoinsTip; throw JSONRPCTransactionError(err, err_string);
bool fHaveChain = false;
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
fHaveChain = !existingCoin.IsSpent();
}
bool fHaveMempool = mempool.exists(hashTx);
if (!fHaveMempool && !fHaveChain) {
// push to local node and sync with wallets
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state));
} else {
if (fMissingInputs) {
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
}
throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state));
}
} else {
// If wallet is enabled, ensure that the wallet has been made aware
// of the new transaction prior to returning. This prevents a race
// where a user might call sendrawtransaction with a transaction
// to/from their wallet, immediately call some wallet RPC, and get
// a stale result because callbacks have not yet been processed.
CallFunctionInValidationInterfaceQueue([&promise] {
promise.set_value();
});
}
} else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
} else {
// Make sure we don't block forever if re-sending
// a transaction already in mempool.
promise.set_value();
} }
} // cs_main return txid.GetHex();
promise.get_future().wait();
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
CInv inv(MSG_TX, hashTx);
g_connman->ForEachNode([&inv](CNode* pnode)
{
pnode->PushInventory(inv);
});
return hashTx.GetHex();
} }
static UniValue testmempoolaccept(const JSONRPCRequest& request) static UniValue testmempoolaccept(const JSONRPCRequest& request)
@ -1323,7 +1270,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
// Unserialize the transactions // Unserialize the transactions
PartiallySignedTransaction psbtx; PartiallySignedTransaction psbtx;
std::string error; std::string error;
if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) { if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
} }
@ -1524,23 +1471,16 @@ UniValue combinepsbt(const JSONRPCRequest& request)
for (unsigned int i = 0; i < txs.size(); ++i) { for (unsigned int i = 0; i < txs.size(); ++i) {
PartiallySignedTransaction psbtx; PartiallySignedTransaction psbtx;
std::string error; std::string error;
if (!DecodePSBT(psbtx, txs[i].get_str(), error)) { if (!DecodeBase64PSBT(psbtx, txs[i].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
} }
psbtxs.push_back(psbtx); psbtxs.push_back(psbtx);
} }
PartiallySignedTransaction merged_psbt(psbtxs[0]); // Copy the first one PartiallySignedTransaction merged_psbt;
TransactionError error;
// Merge if (!CombinePSBTs(merged_psbt, error, psbtxs)) {
for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) { throw JSONRPCTransactionError(error);
if (*it != merged_psbt) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "PSBTs do not refer to the same transactions.");
}
merged_psbt.Merge(*it);
}
if (!merged_psbt.IsSane()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Merged PSBT is inconsistent");
} }
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
@ -1581,33 +1521,27 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
// Unserialize the transactions // Unserialize the transactions
PartiallySignedTransaction psbtx; PartiallySignedTransaction psbtx;
std::string error; std::string error;
if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) { if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
} }
// Finalize input signatures -- in case we have partial signatures that add up to a complete bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool());
// signature, but have not combined them yet (e.g. because the combiner that created this
// PartiallySignedTransaction did not understand them), this will combine them into a final CMutableTransaction mtx;
// script. bool complete = FinalizeAndExtractPSBT(psbtx, mtx);
bool complete = true;
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
}
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()); std::string result_str;
if (complete && extract) { if (complete && extract) {
CMutableTransaction mtx(*psbtx.tx);
for (unsigned int i = 0; i < mtx.vin.size(); ++i) {
mtx.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
mtx.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness;
}
ssTx << mtx; ssTx << mtx;
result.pushKV("hex", HexStr(ssTx.str())); result_str = HexStr(ssTx.str());
result.pushKV("hex", result_str);
} else { } else {
ssTx << psbtx; ssTx << psbtx;
result.pushKV("psbt", EncodeBase64(ssTx.str())); result_str = EncodeBase64(ssTx.str());
result.pushKV("psbt", result_str);
} }
result.pushKV("complete", complete); result.pushKV("complete", complete);

View file

@ -141,6 +141,34 @@ unsigned int ParseConfirmTarget(const UniValue& value)
return (unsigned int)target; return (unsigned int)target;
} }
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)
{
switch (terr) {
case TransactionError::MEMPOOL_REJECTED:
return RPC_TRANSACTION_REJECTED;
case TransactionError::ALREADY_IN_CHAIN:
return RPC_TRANSACTION_ALREADY_IN_CHAIN;
case TransactionError::P2P_DISABLED:
return RPC_CLIENT_P2P_DISABLED;
case TransactionError::INVALID_PSBT:
case TransactionError::PSBT_MISMATCH:
return RPC_INVALID_PARAMETER;
case TransactionError::SIGHASH_MISMATCH:
return RPC_DESERIALIZATION_ERROR;
default: break;
}
return RPC_TRANSACTION_ERROR;
}
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string)
{
if (err_string.length() > 0) {
return JSONRPCError(RPCErrorFromTransactionError(terr), err_string);
} else {
return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr));
}
}
struct Section { struct Section {
Section(const std::string& left, const std::string& right) Section(const std::string& left, const std::string& right)
: m_left{left}, m_right{right} {} : m_left{left}, m_right{right} {}

View file

@ -5,6 +5,7 @@
#ifndef BITCOIN_RPC_UTIL_H #ifndef BITCOIN_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H #define BITCOIN_RPC_UTIL_H
#include <node/transaction.h>
#include <pubkey.h> #include <pubkey.h>
#include <script/standard.h> #include <script/standard.h>
#include <univalue.h> #include <univalue.h>
@ -33,6 +34,9 @@ UniValue DescribeAddress(const CTxDestination& dest);
//! Parse a confirm target option and raise an RPC error if it is invalid. //! Parse a confirm target option and raise an RPC error if it is invalid.
unsigned int ParseConfirmTarget(const UniValue& value); unsigned int ParseConfirmTarget(const UniValue& value);
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");
struct RPCArg { struct RPCArg {
enum class Type { enum class Type {
OBJ, OBJ,

View file

@ -232,67 +232,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
return sigdata.complete; return sigdata.complete;
} }
bool PSBTInputSigned(PSBTInput& input)
{
return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
}
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash)
{
PSBTInput& input = psbt.inputs.at(index);
const CMutableTransaction& tx = *psbt.tx;
if (PSBTInputSigned(input)) {
return true;
}
// Fill SignatureData with input info
SignatureData sigdata;
input.FillSignatureData(sigdata);
// Get UTXO
bool require_witness_sig = false;
CTxOut utxo;
// Verify input sanity, which checks that at most one of witness or non-witness utxos is provided.
if (!input.IsSane()) {
return false;
}
if (input.non_witness_utxo) {
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
COutPoint prevout = tx.vin[index].prevout;
if (input.non_witness_utxo->GetHash() != prevout.hash) {
return false;
}
utxo = input.non_witness_utxo->vout[prevout.n];
} else if (!input.witness_utxo.IsNull()) {
utxo = input.witness_utxo;
// When we're taking our information from a witness UTXO, we can't verify it is actually data from
// the output being spent. This is safe in case a witness signature is produced (which includes this
// information directly in the hash), but not for non-witness signatures. Remember that we require
// a witness signature in this situation.
require_witness_sig = true;
} else {
return false;
}
MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
sigdata.witness = false;
bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
// Verify that a witness signature was produced in case one was required.
if (require_witness_sig && !sigdata.witness) return false;
input.FromSignatureData(sigdata);
// If we have a witness signature, use the smaller witness UTXO.
if (sigdata.witness) {
input.witness_utxo = utxo;
input.non_witness_utxo = nullptr;
}
return sig_complete;
}
class SignatureExtractorChecker final : public BaseSignatureChecker class SignatureExtractorChecker final : public BaseSignatureChecker
{ {
private: private:
@ -509,166 +448,6 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
return false; return false;
} }
PartiallySignedTransaction::PartiallySignedTransaction(const CMutableTransaction& tx) : tx(tx)
{
inputs.resize(tx.vin.size());
outputs.resize(tx.vout.size());
}
bool PartiallySignedTransaction::IsNull() const
{
return !tx && inputs.empty() && outputs.empty() && unknown.empty();
}
void PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
{
for (unsigned int i = 0; i < inputs.size(); ++i) {
inputs[i].Merge(psbt.inputs[i]);
}
for (unsigned int i = 0; i < outputs.size(); ++i) {
outputs[i].Merge(psbt.outputs[i]);
}
unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
}
bool PartiallySignedTransaction::IsSane() const
{
for (PSBTInput input : inputs) {
if (!input.IsSane()) return false;
}
return true;
}
bool PSBTInput::IsNull() const
{
return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty();
}
void PSBTInput::FillSignatureData(SignatureData& sigdata) const
{
if (!final_script_sig.empty()) {
sigdata.scriptSig = final_script_sig;
sigdata.complete = true;
}
if (!final_script_witness.IsNull()) {
sigdata.scriptWitness = final_script_witness;
sigdata.complete = true;
}
if (sigdata.complete) {
return;
}
sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end());
if (!redeem_script.empty()) {
sigdata.redeem_script = redeem_script;
}
if (!witness_script.empty()) {
sigdata.witness_script = witness_script;
}
for (const auto& key_pair : hd_keypaths) {
sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
}
}
void PSBTInput::FromSignatureData(const SignatureData& sigdata)
{
if (sigdata.complete) {
partial_sigs.clear();
hd_keypaths.clear();
redeem_script.clear();
witness_script.clear();
if (!sigdata.scriptSig.empty()) {
final_script_sig = sigdata.scriptSig;
}
if (!sigdata.scriptWitness.IsNull()) {
final_script_witness = sigdata.scriptWitness;
}
return;
}
partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end());
if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
redeem_script = sigdata.redeem_script;
}
if (witness_script.empty() && !sigdata.witness_script.empty()) {
witness_script = sigdata.witness_script;
}
for (const auto& entry : sigdata.misc_pubkeys) {
hd_keypaths.emplace(entry.second);
}
}
void PSBTInput::Merge(const PSBTInput& input)
{
if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
witness_utxo = input.witness_utxo;
non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one.
}
partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
unknown.insert(input.unknown.begin(), input.unknown.end());
if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script;
if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
}
bool PSBTInput::IsSane() const
{
// Cannot have both witness and non-witness utxos
if (!witness_utxo.IsNull() && non_witness_utxo) return false;
// If we have a witness_script or a scriptWitness, we must also have a witness utxo
if (!witness_script.empty() && witness_utxo.IsNull()) return false;
if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false;
return true;
}
void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
{
if (!redeem_script.empty()) {
sigdata.redeem_script = redeem_script;
}
if (!witness_script.empty()) {
sigdata.witness_script = witness_script;
}
for (const auto& key_pair : hd_keypaths) {
sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
}
}
void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
{
if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
redeem_script = sigdata.redeem_script;
}
if (witness_script.empty() && !sigdata.witness_script.empty()) {
witness_script = sigdata.witness_script;
}
for (const auto& entry : sigdata.misc_pubkeys) {
hd_keypaths.emplace(entry.second);
}
}
bool PSBTOutput::IsNull() const
{
return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty();
}
void PSBTOutput::Merge(const PSBTOutput& output)
{
hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end());
unknown.insert(output.unknown.begin(), output.unknown.end());
if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
}
bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
{ {
return m_provider->GetCScript(scriptid, script); return m_provider->GetCScript(scriptid, script);

View file

@ -123,32 +123,6 @@ struct SignatureData {
void MergeSignatureData(SignatureData sigdata); void MergeSignatureData(SignatureData sigdata);
}; };
// Magic bytes
static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
// Global types
static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00;
// Input types
static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00;
static constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01;
static constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02;
static constexpr uint8_t PSBT_IN_SIGHASH = 0x03;
static constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04;
static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05;
static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06;
static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08;
// Output types
static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01;
static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
// The separator is 0x00. Reading this in means that the unserializer can interpret it
// as a 0 length key which indicates that this is the separator. The separator has no value.
static constexpr uint8_t PSBT_SEPARATOR = 0x00;
// Takes a stream and multiple arguments and serializes them as if first serialized into a vector and then into the stream // Takes a stream and multiple arguments and serializes them as if first serialized into a vector and then into the stream
// The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other. // The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other.
template<typename Stream, typename... X> template<typename Stream, typename... X>
@ -223,514 +197,6 @@ void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_k
} }
} }
/** A structure for PSBTs which contain per-input information */
struct PSBTInput
{
CTransactionRef non_witness_utxo;
CTxOut witness_utxo;
CScript redeem_script;
CScript witness_script;
CScript final_script_sig;
CScriptWitness final_script_witness;
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
std::map<CKeyID, SigPair> partial_sigs;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
int sighash_type = 0;
bool IsNull() const;
void FillSignatureData(SignatureData& sigdata) const;
void FromSignatureData(const SignatureData& sigdata);
void Merge(const PSBTInput& input);
bool IsSane() const;
PSBTInput() {}
template <typename Stream>
inline void Serialize(Stream& s) const {
// Write the utxo
// If there is a non-witness utxo, then don't add the witness one.
if (non_witness_utxo) {
SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
SerializeToVector(os, non_witness_utxo);
} else if (!witness_utxo.IsNull()) {
SerializeToVector(s, PSBT_IN_WITNESS_UTXO);
SerializeToVector(s, witness_utxo);
}
if (final_script_sig.empty() && final_script_witness.IsNull()) {
// Write any partial signatures
for (auto sig_pair : partial_sigs) {
SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first));
s << sig_pair.second.second;
}
// Write the sighash type
if (sighash_type > 0) {
SerializeToVector(s, PSBT_IN_SIGHASH);
SerializeToVector(s, sighash_type);
}
// Write the redeem script
if (!redeem_script.empty()) {
SerializeToVector(s, PSBT_IN_REDEEMSCRIPT);
s << redeem_script;
}
// Write the witness script
if (!witness_script.empty()) {
SerializeToVector(s, PSBT_IN_WITNESSSCRIPT);
s << witness_script;
}
// Write any hd keypaths
SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION);
}
// Write script sig
if (!final_script_sig.empty()) {
SerializeToVector(s, PSBT_IN_SCRIPTSIG);
s << final_script_sig;
}
// write script witness
if (!final_script_witness.IsNull()) {
SerializeToVector(s, PSBT_IN_SCRIPTWITNESS);
SerializeToVector(s, final_script_witness.stack);
}
// Write unknown things
for (auto& entry : unknown) {
s << entry.first;
s << entry.second;
}
s << PSBT_SEPARATOR;
}
template <typename Stream>
inline void Unserialize(Stream& s) {
// Read loop
bool found_sep = false;
while(!s.empty()) {
// Read
std::vector<unsigned char> key;
s >> key;
// the key is empty if that was actually a separator byte
// This is a special case for key lengths 0 as those are not allowed (except for separator)
if (key.empty()) {
found_sep = true;
break;
}
// First byte of key is the type
unsigned char type = key[0];
// Do stuff based on type
switch(type) {
case PSBT_IN_NON_WITNESS_UTXO:
{
if (non_witness_utxo) {
throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Non-witness utxo key is more than one byte type");
}
// Set the stream to unserialize with witness since this is always a valid network transaction
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() & ~SERIALIZE_TRANSACTION_NO_WITNESS);
UnserializeFromVector(os, non_witness_utxo);
break;
}
case PSBT_IN_WITNESS_UTXO:
if (!witness_utxo.IsNull()) {
throw std::ios_base::failure("Duplicate Key, input witness utxo already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Witness utxo key is more than one byte type");
}
UnserializeFromVector(s, witness_utxo);
break;
case PSBT_IN_PARTIAL_SIG:
{
// Make sure that the key is the size of pubkey + 1
if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey");
}
// Read in the pubkey from key
CPubKey pubkey(key.begin() + 1, key.end());
if (!pubkey.IsFullyValid()) {
throw std::ios_base::failure("Invalid pubkey");
}
if (partial_sigs.count(pubkey.GetID()) > 0) {
throw std::ios_base::failure("Duplicate Key, input partial signature for pubkey already provided");
}
// Read in the signature from value
std::vector<unsigned char> sig;
s >> sig;
// Add to list
partial_sigs.emplace(pubkey.GetID(), SigPair(pubkey, std::move(sig)));
break;
}
case PSBT_IN_SIGHASH:
if (sighash_type > 0) {
throw std::ios_base::failure("Duplicate Key, input sighash type already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Sighash type key is more than one byte type");
}
UnserializeFromVector(s, sighash_type);
break;
case PSBT_IN_REDEEMSCRIPT:
{
if (!redeem_script.empty()) {
throw std::ios_base::failure("Duplicate Key, input redeemScript already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Input redeemScript key is more than one byte type");
}
s >> redeem_script;
break;
}
case PSBT_IN_WITNESSSCRIPT:
{
if (!witness_script.empty()) {
throw std::ios_base::failure("Duplicate Key, input witnessScript already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Input witnessScript key is more than one byte type");
}
s >> witness_script;
break;
}
case PSBT_IN_BIP32_DERIVATION:
{
DeserializeHDKeypaths(s, key, hd_keypaths);
break;
}
case PSBT_IN_SCRIPTSIG:
{
if (!final_script_sig.empty()) {
throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Final scriptSig key is more than one byte type");
}
s >> final_script_sig;
break;
}
case PSBT_IN_SCRIPTWITNESS:
{
if (!final_script_witness.IsNull()) {
throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Final scriptWitness key is more than one byte type");
}
UnserializeFromVector(s, final_script_witness.stack);
break;
}
// Unknown stuff
default:
if (unknown.count(key) > 0) {
throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
}
// Read in the value
std::vector<unsigned char> val_bytes;
s >> val_bytes;
unknown.emplace(std::move(key), std::move(val_bytes));
break;
}
}
if (!found_sep) {
throw std::ios_base::failure("Separator is missing at the end of an input map");
}
}
template <typename Stream>
PSBTInput(deserialize_type, Stream& s) {
Unserialize(s);
}
};
/** A structure for PSBTs which contains per output information */
struct PSBTOutput
{
CScript redeem_script;
CScript witness_script;
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
bool IsNull() const;
void FillSignatureData(SignatureData& sigdata) const;
void FromSignatureData(const SignatureData& sigdata);
void Merge(const PSBTOutput& output);
bool IsSane() const;
PSBTOutput() {}
template <typename Stream>
inline void Serialize(Stream& s) const {
// Write the redeem script
if (!redeem_script.empty()) {
SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT);
s << redeem_script;
}
// Write the witness script
if (!witness_script.empty()) {
SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT);
s << witness_script;
}
// Write any hd keypaths
SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION);
// Write unknown things
for (auto& entry : unknown) {
s << entry.first;
s << entry.second;
}
s << PSBT_SEPARATOR;
}
template <typename Stream>
inline void Unserialize(Stream& s) {
// Read loop
bool found_sep = false;
while(!s.empty()) {
// Read
std::vector<unsigned char> key;
s >> key;
// the key is empty if that was actually a separator byte
// This is a special case for key lengths 0 as those are not allowed (except for separator)
if (key.empty()) {
found_sep = true;
break;
}
// First byte of key is the type
unsigned char type = key[0];
// Do stuff based on type
switch(type) {
case PSBT_OUT_REDEEMSCRIPT:
{
if (!redeem_script.empty()) {
throw std::ios_base::failure("Duplicate Key, output redeemScript already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Output redeemScript key is more than one byte type");
}
s >> redeem_script;
break;
}
case PSBT_OUT_WITNESSSCRIPT:
{
if (!witness_script.empty()) {
throw std::ios_base::failure("Duplicate Key, output witnessScript already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Output witnessScript key is more than one byte type");
}
s >> witness_script;
break;
}
case PSBT_OUT_BIP32_DERIVATION:
{
DeserializeHDKeypaths(s, key, hd_keypaths);
break;
}
// Unknown stuff
default: {
if (unknown.count(key) > 0) {
throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
}
// Read in the value
std::vector<unsigned char> val_bytes;
s >> val_bytes;
unknown.emplace(std::move(key), std::move(val_bytes));
break;
}
}
}
if (!found_sep) {
throw std::ios_base::failure("Separator is missing at the end of an output map");
}
}
template <typename Stream>
PSBTOutput(deserialize_type, Stream& s) {
Unserialize(s);
}
};
/** A version of CTransaction with the PSBT format*/
struct PartiallySignedTransaction
{
boost::optional<CMutableTransaction> tx;
std::vector<PSBTInput> inputs;
std::vector<PSBTOutput> outputs;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
bool IsNull() const;
void Merge(const PartiallySignedTransaction& psbt);
bool IsSane() const;
PartiallySignedTransaction() {}
PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {}
explicit PartiallySignedTransaction(const CMutableTransaction& tx);
// Only checks if they refer to the same transaction
friend bool operator==(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b)
{
return a.tx->GetHash() == b.tx->GetHash();
}
friend bool operator!=(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b)
{
return !(a == b);
}
template <typename Stream>
inline void Serialize(Stream& s) const {
// magic bytes
s << PSBT_MAGIC_BYTES;
// unsigned tx flag
SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX);
// Write serialized tx to a stream
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
SerializeToVector(os, *tx);
// Write the unknown things
for (auto& entry : unknown) {
s << entry.first;
s << entry.second;
}
// Separator
s << PSBT_SEPARATOR;
// Write inputs
for (const PSBTInput& input : inputs) {
s << input;
}
// Write outputs
for (const PSBTOutput& output : outputs) {
s << output;
}
}
template <typename Stream>
inline void Unserialize(Stream& s) {
// Read the magic bytes
uint8_t magic[5];
s >> magic;
if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) {
throw std::ios_base::failure("Invalid PSBT magic bytes");
}
// Read global data
bool found_sep = false;
while(!s.empty()) {
// Read
std::vector<unsigned char> key;
s >> key;
// the key is empty if that was actually a separator byte
// This is a special case for key lengths 0 as those are not allowed (except for separator)
if (key.empty()) {
found_sep = true;
break;
}
// First byte of key is the type
unsigned char type = key[0];
// Do stuff based on type
switch(type) {
case PSBT_GLOBAL_UNSIGNED_TX:
{
if (tx) {
throw std::ios_base::failure("Duplicate Key, unsigned tx already provided");
} else if (key.size() != 1) {
throw std::ios_base::failure("Global unsigned tx key is more than one byte type");
}
CMutableTransaction mtx;
// Set the stream to serialize with non-witness since this should always be non-witness
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
UnserializeFromVector(os, mtx);
tx = std::move(mtx);
// Make sure that all scriptSigs and scriptWitnesses are empty
for (const CTxIn& txin : tx->vin) {
if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) {
throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses.");
}
}
break;
}
// Unknown stuff
default: {
if (unknown.count(key) > 0) {
throw std::ios_base::failure("Duplicate Key, key for unknown value already provided");
}
// Read in the value
std::vector<unsigned char> val_bytes;
s >> val_bytes;
unknown.emplace(std::move(key), std::move(val_bytes));
}
}
}
if (!found_sep) {
throw std::ios_base::failure("Separator is missing at the end of the global map");
}
// Make sure that we got an unsigned tx
if (!tx) {
throw std::ios_base::failure("No unsigned transcation was provided");
}
// Read input data
unsigned int i = 0;
while (!s.empty() && i < tx->vin.size()) {
PSBTInput input;
s >> input;
inputs.push_back(input);
// Make sure the non-witness utxo matches the outpoint
if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) {
throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash");
}
++i;
}
// Make sure that the number of inputs matches the number of inputs in the transaction
if (inputs.size() != tx->vin.size()) {
throw std::ios_base::failure("Inputs provided does not match the number of inputs in transaction.");
}
// Read output data
i = 0;
while (!s.empty() && i < tx->vout.size()) {
PSBTOutput output;
s >> output;
outputs.push_back(output);
++i;
}
// Make sure that the number of outputs matches the number of outputs in the transaction
if (outputs.size() != tx->vout.size()) {
throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction.");
}
// Sanity check
if (!IsSane()) {
throw std::ios_base::failure("PSBT is not sane.");
}
}
template <typename Stream>
PartiallySignedTransaction(deserialize_type, Stream& s) {
Unserialize(s);
}
};
/** Produce a script signature using a generic signature creator. */ /** Produce a script signature using a generic signature creator. */
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata);
@ -738,12 +204,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType); bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType); bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
/** Checks whether a PSBTInput is already signed. */
bool PSBTInputSigned(PSBTInput& input);
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL);
/** Extract signature data from a transaction input, and insert it. */ /** Extract signature data from a transaction input, and insert it. */
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout); SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout);
void UpdateInput(CTxIn& input, const SignatureData& data); void UpdateInput(CTxIn& input, const SignatureData& data);

View file

@ -141,7 +141,7 @@ std::string EncodeBase64(const std::string& str)
return EncodeBase64((const unsigned char*)str.c_str(), str.size()); return EncodeBase64((const unsigned char*)str.c_str(), str.size());
} }
std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid) std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid)
{ {
static const int decode64_table[256] = static const int decode64_table[256] =
{ {
@ -183,14 +183,14 @@ std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
++p; ++p;
} }
valid = valid && (p - e) % 4 == 0 && p - q < 4; valid = valid && (p - e) % 4 == 0 && p - q < 4;
if (pfInvalid) *pfInvalid = !valid; if (pf_invalid) *pf_invalid = !valid;
return ret; return ret;
} }
std::string DecodeBase64(const std::string& str) std::string DecodeBase64(const std::string& str, bool* pf_invalid)
{ {
std::vector<unsigned char> vchRet = DecodeBase64(str.c_str()); std::vector<unsigned char> vchRet = DecodeBase64(str.c_str(), pf_invalid);
return std::string((const char*)vchRet.data(), vchRet.size()); return std::string((const char*)vchRet.data(), vchRet.size());
} }
@ -210,7 +210,7 @@ std::string EncodeBase32(const std::string& str)
return EncodeBase32((const unsigned char*)str.c_str(), str.size()); return EncodeBase32((const unsigned char*)str.c_str(), str.size());
} }
std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid) std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid)
{ {
static const int decode32_table[256] = static const int decode32_table[256] =
{ {
@ -252,14 +252,14 @@ std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid)
++p; ++p;
} }
valid = valid && (p - e) % 8 == 0 && p - q < 8; valid = valid && (p - e) % 8 == 0 && p - q < 8;
if (pfInvalid) *pfInvalid = !valid; if (pf_invalid) *pf_invalid = !valid;
return ret; return ret;
} }
std::string DecodeBase32(const std::string& str) std::string DecodeBase32(const std::string& str, bool* pf_invalid)
{ {
std::vector<unsigned char> vchRet = DecodeBase32(str.c_str()); std::vector<unsigned char> vchRet = DecodeBase32(str.c_str(), pf_invalid);
return std::string((const char*)vchRet.data(), vchRet.size()); return std::string((const char*)vchRet.data(), vchRet.size());
} }

View file

@ -44,12 +44,12 @@ bool IsHex(const std::string& str);
* Return true if the string is a hex number, optionally prefixed with "0x" * Return true if the string is a hex number, optionally prefixed with "0x"
*/ */
bool IsHexNumber(const std::string& str); bool IsHexNumber(const std::string& str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = nullptr); std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase64(const std::string& str); std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr);
std::string EncodeBase64(const unsigned char* pch, size_t len); std::string EncodeBase64(const unsigned char* pch, size_t len);
std::string EncodeBase64(const std::string& str); std::string EncodeBase64(const std::string& str);
std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid = nullptr); std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase32(const std::string& str); std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr);
std::string EncodeBase32(const unsigned char* pch, size_t len); std::string EncodeBase32(const unsigned char* pch, size_t len);
std::string EncodeBase32(const std::string& str); std::string EncodeBase32(const std::string& str);

62
src/wallet/psbtwallet.cpp Normal file
View file

@ -0,0 +1,62 @@
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <wallet/psbtwallet.h>
bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, TransactionError& error, bool& complete, int sighash_type, bool sign, bool bip32derivs)
{
LOCK(pwallet->cs_wallet);
// Get all of the previous transactions
complete = true;
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
if (PSBTInputSigned(input)) {
continue;
}
// Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
if (!input.IsSane()) {
error = TransactionError::INVALID_PSBT;
return false;
}
// If we have no utxo, grab it from the wallet.
if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
const uint256& txhash = txin.prevout.hash;
const auto it = pwallet->mapWallet.find(txhash);
if (it != pwallet->mapWallet.end()) {
const CWalletTx& wtx = it->second;
// We only need the non_witness_utxo, which is a superset of the witness_utxo.
// The signing code will switch to the smaller witness_utxo if this is ok.
input.non_witness_utxo = wtx.tx;
}
}
// Get the Sighash type
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
error = TransactionError::SIGHASH_MISMATCH;
return false;
}
complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type);
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
const CTxOut& out = psbtx.tx->vout.at(i);
PSBTOutput& psbt_out = psbtx.outputs.at(i);
// Fill a SignatureData with output info
SignatureData sigdata;
psbt_out.FillSignatureData(sigdata);
MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1);
ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata);
psbt_out.FromSignatureData(sigdata);
}
return true;
}

36
src/wallet/psbtwallet.h Normal file
View file

@ -0,0 +1,36 @@
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_WALLET_PSBTWALLET_H
#define BITCOIN_WALLET_PSBTWALLET_H
#include <node/transaction.h>
#include <psbt.h>
#include <primitives/transaction.h>
#include <wallet/wallet.h>
/**
* Fills out a PSBT with information from the wallet. Fills in UTXOs if we have
* them. Tries to sign if sign=true. Sets `complete` if the PSBT is now complete
* (i.e. has all required signatures or signature-parts, and is ready to
* finalize.) Sets `error` and returns false if something goes wrong.
*
* @param[in] pwallet pointer to a wallet
* @param[in] &psbtx reference to PartiallySignedTransaction to fill in
* @param[out] &error reference to UniValue to fill with error info on failure
* @param[out] &complete indicates whether the PSBT is now complete
* @param[in] sighash_type the sighash type to use when signing (if PSBT does not specify)
* @param[in] sign whether to sign or not
* @param[in] bip32derivs whether to fill in bip32 derivation information if available
* return true on success, false on error (and fills in `error`)
*/
bool FillPSBT(const CWallet* pwallet,
PartiallySignedTransaction& psbtx,
TransactionError& error,
bool& complete,
int sighash_type = 1 /* SIGHASH_ALL */,
bool sign = true,
bool bip32derivs = false);
#endif // BITCOIN_WALLET_PSBTWALLET_H

View file

@ -13,6 +13,7 @@
#include <validation.h> #include <validation.h>
#include <key_io.h> #include <key_io.h>
#include <net.h> #include <net.h>
#include <node/transaction.h>
#include <outputtype.h> #include <outputtype.h>
#include <policy/feerate.h> #include <policy/feerate.h>
#include <policy/fees.h> #include <policy/fees.h>
@ -30,6 +31,7 @@
#include <util/moneystr.h> #include <util/moneystr.h>
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/feebumper.h> #include <wallet/feebumper.h>
#include <wallet/psbtwallet.h>
#include <wallet/rpcwallet.h> #include <wallet/rpcwallet.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/walletdb.h> #include <wallet/walletdb.h>
@ -3938,60 +3940,6 @@ void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubK
hd_keypaths.emplace(vchPubKey, std::move(info)); hd_keypaths.emplace(vchPubKey, std::move(info));
} }
bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs)
{
LOCK(pwallet->cs_wallet);
// Get all of the previous transactions
bool complete = true;
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
if (PSBTInputSigned(input)) {
continue;
}
// Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
if (!input.IsSane()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "PSBT input is not sane.");
}
// If we have no utxo, grab it from the wallet.
if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
const uint256& txhash = txin.prevout.hash;
const auto it = pwallet->mapWallet.find(txhash);
if (it != pwallet->mapWallet.end()) {
const CWalletTx& wtx = it->second;
// We only need the non_witness_utxo, which is a superset of the witness_utxo.
// The signing code will switch to the smaller witness_utxo if this is ok.
input.non_witness_utxo = wtx.tx;
}
}
// Get the Sighash type
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Specified Sighash and sighash in PSBT do not match.");
}
complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type);
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
const CTxOut& out = psbtx.tx->vout.at(i);
PSBTOutput& psbt_out = psbtx.outputs.at(i);
// Fill a SignatureData with output info
SignatureData sigdata;
psbt_out.FillSignatureData(sigdata);
MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1);
ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata);
psbt_out.FromSignatureData(sigdata);
}
return complete;
}
UniValue walletprocesspsbt(const JSONRPCRequest& request) UniValue walletprocesspsbt(const JSONRPCRequest& request)
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@ -4036,7 +3984,7 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
// Unserialize the transaction // Unserialize the transaction
PartiallySignedTransaction psbtx; PartiallySignedTransaction psbtx;
std::string error; std::string error;
if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) { if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
} }
@ -4046,7 +3994,11 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
// Fill transaction with our data and also sign // Fill transaction with our data and also sign
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool(); bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool(); bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool();
bool complete = FillPSBT(pwallet, psbtx, nHashType, sign, bip32derivs); bool complete = true;
TransactionError err;
if (!FillPSBT(pwallet, psbtx, err, complete, nHashType, sign, bip32derivs)) {
throw JSONRPCTransactionError(err);
}
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@ -4160,7 +4112,11 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
// Fill transaction with out data but don't sign // Fill transaction with out data but don't sign
bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool(); bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool();
FillPSBT(pwallet, psbtx, 1, false, bip32derivs); bool complete = true;
TransactionError err;
if (!FillPSBT(pwallet, psbtx, err, complete, 1, false, bip32derivs)) {
throw JSONRPCTransactionError(err);
}
// Serialize the PSBT // Serialize the PSBT
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);

View file

@ -30,5 +30,4 @@ bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
UniValue getaddressinfo(const JSONRPCRequest& request); UniValue getaddressinfo(const JSONRPCRequest& request);
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request); UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);
bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false);
#endif //BITCOIN_WALLET_RPCWALLET_H #endif //BITCOIN_WALLET_RPCWALLET_H

View file

@ -5,6 +5,7 @@
#include <key_io.h> #include <key_io.h>
#include <script/sign.h> #include <script/sign.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <wallet/psbtwallet.h>
#include <wallet/rpcwallet.h> #include <wallet/rpcwallet.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <univalue.h> #include <univalue.h>
@ -60,7 +61,9 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
ssData >> psbtx; ssData >> psbtx;
// Fill transaction with our data // Fill transaction with our data
FillPSBT(&m_wallet, psbtx, SIGHASH_ALL, false, true); TransactionError err;
bool complete = true;
FillPSBT(&m_wallet, psbtx, err, complete, SIGHASH_ALL, false, true);
// Get the final tx // Get the final tx
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);

View file

@ -293,5 +293,8 @@ class PSBTTest(BitcoinTestFramework):
psbt = self.nodes[1].walletcreatefundedpsbt([], [{p2pkh : 1}], 0, {"includeWatching" : True}, True) psbt = self.nodes[1].walletcreatefundedpsbt([], [{p2pkh : 1}], 0, {"includeWatching" : True}, True)
self.nodes[0].decodepsbt(psbt['psbt']) self.nodes[0].decodepsbt(psbt['psbt'])
# Test decoding error: invalid base64
assert_raises_rpc_error(-22, "TX decode failed invalid base64", self.nodes[0].decodepsbt, ";definitely not base64;")
if __name__ == '__main__': if __name__ == '__main__':
PSBTTest().main() PSBTTest().main()