diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index eb281ef10..3997de484 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -1,117 +1,119 @@ // 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 #include #include #include #include #include #include #include #include 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 Config &config, const CTransactionRef tx, TxId &txid, TransactionError &error, std::string &err_string, const bool allowhighfees) { std::promise promise; txid = tx->GetId(); Amount nMaxRawTxFee = maxTxFee; if (allowhighfees) { nMaxRawTxFee = Amount::zero(); } { // 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(txid, o)); fHaveChain = !existingCoin.IsSpent(); } bool fHaveMempool = g_mempool.exists(txid); if (!fHaveMempool && !fHaveChain) { // Push to local node and sync with wallets. CValidationState state; bool fMissingInputs; if (!AcceptToMemoryPool(config, g_mempool, state, std::move(tx), &fMissingInputs, false /* bypass_limits */, nMaxRawTxFee)) { if (state.IsInvalid()) { err_string = FormatStateMessage(state); error = TransactionError::MEMPOOL_REJECTED; return false; } 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, txid); g_connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); return true; } diff --git a/src/node/transaction.h b/src/node/transaction.h index dad94689e..ded943966 100644 --- a/src/node/transaction.h +++ b/src/node/transaction.h @@ -1,47 +1,48 @@ // 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 class Config; struct TxId; 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(const Config &config, CTransactionRef tx, TxId &txid, TransactionError &error, std::string &err_string, bool allowhighfees = false); #endif // BITCOIN_NODE_TRANSACTION_H diff --git a/src/psbt.cpp b/src/psbt.cpp index 5dd6f2e8e..23d043b10 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -1,178 +1,229 @@ // 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 #include PartiallySignedTransaction::PartiallySignedTransaction(const CTransaction &txIn) : tx(txIn) { inputs.resize(txIn.vin.size()); outputs.resize(txIn.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->GetId() != psbt.tx->GetId()) { return false; } for (size_t i = 0; i < inputs.size(); ++i) { inputs[i].Merge(psbt.inputs[i]); } for (size_t 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 utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty(); } void PSBTInput::FillSignatureData(SignatureData &sigdata) const { if (!final_script_sig.empty()) { sigdata.scriptSig = final_script_sig; 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; } 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(); if (!sigdata.scriptSig.empty()) { final_script_sig = sigdata.scriptSig; } return; } partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end()); if (redeem_script.empty() && !sigdata.redeem_script.empty()) { redeem_script = sigdata.redeem_script; } for (const auto &entry : sigdata.misc_pubkeys) { hd_keypaths.emplace(entry.second); } } void PSBTInput::Merge(const PSBTInput &input) { if (utxo.IsNull() && !input.utxo.IsNull()) { utxo = input.utxo; } 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 (final_script_sig.empty() && !input.final_script_sig.empty()) { final_script_sig = input.final_script_sig; } } bool PSBTInput::IsSane() const { return true; } void PSBTOutput::FillSignatureData(SignatureData &sigdata) const { if (!redeem_script.empty()) { sigdata.redeem_script = redeem_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; } for (const auto &entry : sigdata.misc_pubkeys) { hd_keypaths.emplace(entry.second); } } bool PSBTOutput::IsNull() const { return redeem_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; } } bool PSBTInputSigned(PSBTInput &input) { return !input.final_script_sig.empty(); } bool SignPSBTInput(const SigningProvider &provider, PartiallySignedTransaction &psbt, int index, SigHashType 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 CTxOut utxo; // Verify input sanity if (!input.IsSane()) { return false; } if (input.utxo.IsNull()) { return false; } utxo = input.utxo; MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash); bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata); input.FromSignatureData(sigdata); 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 (size_t i = 0; i < psbtx.tx->vin.size(); ++i) { + complete &= + SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SigHashType()); + } + + 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 (size_t i = 0; i < result.vin.size(); ++i) { + result.vin[i].scriptSig = psbtx.inputs[i].final_script_sig; + } + return true; +} + +bool CombinePSBTs(PartiallySignedTransaction &out, TransactionError &error, + const std::vector &psbtxs) { + // Copy the first one + out = psbtxs[0]; + + // 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; +} diff --git a/src/psbt.h b/src/psbt.h index 4beb4221a..b262d3d5f 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -1,482 +1,516 @@ // 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 #include #include #include #include