diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/psbtwallet.cpp | 62 | ||||
-rw-r--r-- | src/wallet/psbtwallet.h | 36 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 70 | ||||
-rw-r--r-- | src/wallet/rpcwallet.h | 1 | ||||
-rw-r--r-- | src/wallet/test/psbt_wallet_tests.cpp | 5 |
5 files changed, 115 insertions, 59 deletions
diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp new file mode 100644 index 0000000000..761e7b7dd7 --- /dev/null +++ b/src/wallet/psbtwallet.cpp @@ -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; +} diff --git a/src/wallet/psbtwallet.h b/src/wallet/psbtwallet.h new file mode 100644 index 0000000000..b679f5c6ba --- /dev/null +++ b/src/wallet/psbtwallet.h @@ -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 diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e836f50792..9e362cf167 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -13,6 +13,7 @@ #include <validation.h> #include <key_io.h> #include <net.h> +#include <node/transaction.h> #include <outputtype.h> #include <policy/feerate.h> #include <policy/fees.h> @@ -30,6 +31,7 @@ #include <util/moneystr.h> #include <wallet/coincontrol.h> #include <wallet/feebumper.h> +#include <wallet/psbtwallet.h> #include <wallet/rpcwallet.h> #include <wallet/wallet.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)); } -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) { std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); @@ -4036,7 +3984,7 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request) // Unserialize the transaction PartiallySignedTransaction psbtx; 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)); } @@ -4046,7 +3994,11 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request) // Fill transaction with our data and also sign 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 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); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); @@ -4160,7 +4112,11 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) // Fill transaction with out data but don't sign 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 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index abd7750874..58053bde59 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -30,5 +30,4 @@ bool EnsureWalletIsAvailable(CWallet *, bool avoidException); UniValue getaddressinfo(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 diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp index 9918eeb89f..e89d4121bc 100644 --- a/src/wallet/test/psbt_wallet_tests.cpp +++ b/src/wallet/test/psbt_wallet_tests.cpp @@ -5,6 +5,7 @@ #include <key_io.h> #include <script/sign.h> #include <util/strencodings.h> +#include <wallet/psbtwallet.h> #include <wallet/rpcwallet.h> #include <wallet/wallet.h> #include <univalue.h> @@ -60,7 +61,9 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test) ssData >> psbtx; // 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 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); |