mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
Merge pull request #4229 from kittywhiskers/auxports
merge #16117, #18358, #17383, #21052, #14424, #15159, #14689, #14978, partial #16908, #14978, #13932: Auxillary Backports
This commit is contained in:
commit
7aebf156e9
68
configure.ac
68
configure.ac
@ -913,6 +913,22 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
)
|
||||
LDFLAGS="$TEMP_LDFLAGS"
|
||||
|
||||
dnl check for gmtime_r(), fallback to gmtime_s() if that is unavailable
|
||||
dnl fail if neither are available.
|
||||
AC_MSG_CHECKING(for gmtime_r)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <ctime>]],
|
||||
[[ gmtime_r((const time_t *) nullptr, (struct tm *) nullptr); ]])],
|
||||
[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GMTIME_R, 1, [Define this symbol if gmtime_r is available]) ],
|
||||
[ AC_MSG_RESULT(no);
|
||||
AC_MSG_CHECKING(for gmtime_s);
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <ctime>]],
|
||||
[[ gmtime_s((struct tm *) nullptr, (const time_t *) nullptr); ]])],
|
||||
[ AC_MSG_RESULT(yes)],
|
||||
[ AC_MSG_RESULT(no); AC_MSG_ERROR(Both gmtime_r and gmtime_s are unavailable) ]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
# Check for different ways of gathering OS randomness
|
||||
AC_MSG_CHECKING(for Linux getrandom syscall)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
|
||||
@ -1207,57 +1223,6 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
LIBS="$TEMP_LIBS"
|
||||
CPPFLAGS="$TEMP_CPPFLAGS"
|
||||
|
||||
dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however
|
||||
dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if
|
||||
dnl a working version is available, else fall back to sleep. sleep was removed
|
||||
dnl after 1.56.
|
||||
dnl If neither is available, abort.
|
||||
TEMP_LIBS="$LIBS"
|
||||
LIBS="$BOOST_LIBS $LIBS"
|
||||
TEMP_CPPFLAGS="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/version.hpp>
|
||||
]],[[
|
||||
#if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200)
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(0));
|
||||
#else
|
||||
choke me
|
||||
#endif
|
||||
]])],
|
||||
[boost_sleep=yes;
|
||||
AC_DEFINE(HAVE_WORKING_BOOST_SLEEP_FOR, 1, [Define this symbol if boost sleep_for works])],
|
||||
[boost_sleep=no])
|
||||
LIBS="$TEMP_LIBS"
|
||||
CPPFLAGS="$TEMP_CPPFLAGS"
|
||||
|
||||
if test x$boost_sleep != xyes; then
|
||||
TEMP_LIBS="$LIBS"
|
||||
LIBS="$BOOST_LIBS $LIBS"
|
||||
TEMP_CPPFLAGS="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
]],[[
|
||||
#if BOOST_VERSION <= 105600
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(0));
|
||||
#else
|
||||
choke me
|
||||
#endif
|
||||
]])],
|
||||
[boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP, 1, [Define this symbol if boost sleep works])],
|
||||
[boost_sleep=no])
|
||||
LIBS="$TEMP_LIBS"
|
||||
CPPFLAGS="$TEMP_CPPFLAGS"
|
||||
fi
|
||||
|
||||
if test x$boost_sleep != xyes; then
|
||||
AC_MSG_ERROR(No working boost sleep implementation found.)
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if test x$use_pkgconfig = xyes; then
|
||||
@ -1625,6 +1590,7 @@ AC_SUBST(HAVE_BUILTIN_PREFETCH)
|
||||
AC_SUBST(HAVE_MM_PREFETCH)
|
||||
AC_SUBST(HAVE_STRONG_GETAUXVAL)
|
||||
AC_SUBST(HAVE_WEAK_GETAUXVAL)
|
||||
AC_SUBST(HAVE_GMTIME_R)
|
||||
AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini])
|
||||
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
|
||||
AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile])])
|
||||
|
@ -220,12 +220,14 @@ BITCOIN_CORE_H = \
|
||||
netfulfilledman.h \
|
||||
netmessagemaker.h \
|
||||
node/coinstats.h \
|
||||
node/transaction.h \
|
||||
noui.h \
|
||||
policy/feerate.h \
|
||||
policy/fees.h \
|
||||
policy/policy.h \
|
||||
pow.h \
|
||||
protocol.h \
|
||||
psbt.h \
|
||||
random.h \
|
||||
reverse_iterator.h \
|
||||
reverselock.h \
|
||||
@ -271,6 +273,7 @@ BITCOIN_CORE_H = \
|
||||
util/fees.h \
|
||||
util/system.h \
|
||||
util/asmap.h \
|
||||
util/getuniquepath.h \
|
||||
util/macros.h \
|
||||
util/memory.h \
|
||||
util/moneystr.h \
|
||||
@ -289,6 +292,7 @@ BITCOIN_CORE_H = \
|
||||
wallet/crypter.h \
|
||||
wallet/db.h \
|
||||
wallet/fees.h \
|
||||
wallet/psbtwallet.h \
|
||||
wallet/rpcwallet.h \
|
||||
wallet/wallet.h \
|
||||
wallet/walletdb.h \
|
||||
@ -371,6 +375,7 @@ libdash_server_a_SOURCES = \
|
||||
netfulfilledman.cpp \
|
||||
net_processing.cpp \
|
||||
node/coinstats.cpp \
|
||||
node/transaction.cpp \
|
||||
noui.cpp \
|
||||
policy/fees.cpp \
|
||||
policy/policy.cpp \
|
||||
@ -428,6 +433,7 @@ libdash_wallet_a_SOURCES = \
|
||||
wallet/db.cpp \
|
||||
wallet/fees.cpp \
|
||||
wallet/init.cpp \
|
||||
wallet/psbtwallet.cpp \
|
||||
wallet/rpcdump.cpp \
|
||||
wallet/rpcwallet.cpp \
|
||||
wallet/wallet.cpp \
|
||||
@ -574,6 +580,7 @@ libdash_common_a_SOURCES = \
|
||||
netbase.cpp \
|
||||
net_permissions.cpp \
|
||||
policy/feerate.cpp \
|
||||
psbt.cpp \
|
||||
protocol.cpp \
|
||||
saltedhasher.cpp \
|
||||
scheduler.cpp \
|
||||
@ -612,6 +619,7 @@ libdash_util_a_SOURCES = \
|
||||
util/bytevectorhash.cpp \
|
||||
util/error.cpp \
|
||||
util/fees.cpp \
|
||||
util/getuniquepath.cpp \
|
||||
util/system.cpp \
|
||||
util/asmap.cpp \
|
||||
util/moneystr.cpp \
|
||||
|
@ -356,7 +356,7 @@ static void BLS_Verify_BatchedParallel(benchmark::Bench& bench)
|
||||
cancel = true;
|
||||
while (blsWorker.IsAsyncVerifyInProgress())
|
||||
{
|
||||
MilliSleep(100);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,7 +293,7 @@ void CCoinJoinClientSession::UnlockCoins()
|
||||
while (true) {
|
||||
TRY_LOCK(mixingWallet.cs_wallet, lockWallet);
|
||||
if (!lockWallet) {
|
||||
MilliSleep(50);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{50});
|
||||
continue;
|
||||
}
|
||||
for (const auto& outpoint : vecOutPointLocked)
|
||||
|
@ -36,9 +36,13 @@ std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDeco
|
||||
*/
|
||||
bool ParseHashStr(const std::string& strHex, uint256& result);
|
||||
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
|
||||
bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
|
||||
int ParseSighashString(const UniValue& sighash);
|
||||
|
||||
//! 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);
|
||||
|
||||
// core_write.cpp
|
||||
UniValue ValueFromAmount(const CAmount& amount);
|
||||
std::string FormatScript(const CScript& script);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <core_io.h>
|
||||
|
||||
#include <psbt.h>
|
||||
#include <primitives/block.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <script/script.h>
|
||||
@ -125,10 +126,20 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
|
||||
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());
|
||||
CDataStream ss_data(tx_data, SER_NETWORK, PROTOCOL_VERSION);
|
||||
bool invalid;
|
||||
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 {
|
||||
ss_data >> psbt;
|
||||
if (!ss_data.empty()) {
|
||||
|
@ -519,7 +519,7 @@ static int CommandLineRPC(int argc, char *argv[])
|
||||
}
|
||||
catch (const CConnectionFailed&) {
|
||||
if (fWait)
|
||||
MilliSleep(1000);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{1000});
|
||||
else
|
||||
throw;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ static void WaitForShutdown()
|
||||
{
|
||||
while (!ShutdownRequested())
|
||||
{
|
||||
MilliSleep(200);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{200});
|
||||
}
|
||||
Interrupt();
|
||||
}
|
||||
|
@ -531,7 +531,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC
|
||||
|
||||
// RETRIEVE TRANSACTION IN QUESTION
|
||||
|
||||
if (!GetTransaction(nCollateralHash, txCollateral, Params().GetConsensus(), nBlockHash, true)) {
|
||||
if (!GetTransaction(nCollateralHash, txCollateral, Params().GetConsensus(), nBlockHash)) {
|
||||
strError = strprintf("Can't find collateral tx %s", nCollateralHash.ToString());
|
||||
LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError);
|
||||
return false;
|
||||
|
@ -170,7 +170,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
|
||||
/* Deter brute-forcing
|
||||
If this results in a DoS the user really
|
||||
shouldn't have their RPC port exposed. */
|
||||
MilliSleep(250);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{250});
|
||||
|
||||
req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
|
||||
req->WriteReply(HTTP_UNAUTHORIZED);
|
||||
|
@ -208,7 +208,7 @@ void CDKGSessionHandler::WaitForNextPhase(QuorumPhase curPhase,
|
||||
throw AbortPhaseException();
|
||||
}
|
||||
if (!runWhileWaiting()) {
|
||||
MilliSleep(100);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,7 +238,7 @@ void CDKGSessionHandler::WaitForNewQuorum(const uint256& oldQuorumHash) const
|
||||
if (p.second != oldQuorumHash) {
|
||||
break;
|
||||
}
|
||||
MilliSleep(100);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
|
||||
LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s - done\n", __func__, params.name);
|
||||
@ -305,7 +305,7 @@ void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase,
|
||||
}
|
||||
}
|
||||
if (!runWhileWaiting()) {
|
||||
MilliSleep(100);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -581,7 +581,7 @@ bool CInstantSendManager::CheckCanLock(const COutPoint& outpoint, bool printDebu
|
||||
CTransactionRef tx;
|
||||
uint256 hashBlock;
|
||||
// this relies on enabled txindex and won't work if we ever try to remove the requirement for txindex for masternodes
|
||||
if (!GetTransaction(outpoint.hash, tx, params, hashBlock, false)) {
|
||||
if (!GetTransaction(outpoint.hash, tx, params, hashBlock)) {
|
||||
if (printDebug) {
|
||||
LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s: failed to find parent TX %s\n", __func__,
|
||||
txHash.ToString(), outpoint.hash.ToString());
|
||||
@ -647,7 +647,7 @@ void CInstantSendManager::HandleNewInputLockRecoveredSig(const CRecoveredSig& re
|
||||
|
||||
CTransactionRef tx;
|
||||
uint256 hashBlock;
|
||||
if (!GetTransaction(txid, tx, Params().GetConsensus(), hashBlock, true)) {
|
||||
if (!GetTransaction(txid, tx, Params().GetConsensus(), hashBlock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -54,8 +54,6 @@ static const bool DEFAULT_WHITELISTRELAY = true;
|
||||
/** Default for -whitelistforcerelay. */
|
||||
static const bool DEFAULT_WHITELISTFORCERELAY = false;
|
||||
|
||||
/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
|
||||
static const int PING_INTERVAL = 2 * 60;
|
||||
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
|
||||
static const int TIMEOUT_INTERVAL = 20 * 60;
|
||||
/** Minimum time between warnings printed to log. */
|
||||
@ -64,8 +62,6 @@ static const int WARNING_INTERVAL = 10 * 60;
|
||||
static const int FEELER_INTERVAL = 120;
|
||||
/** The maximum number of entries in an 'inv' protocol message */
|
||||
static const unsigned int MAX_INV_SZ = 50000;
|
||||
/** The maximum number of entries in a locator */
|
||||
static const unsigned int MAX_LOCATOR_SZ = 101;
|
||||
/** The maximum number of new addresses to accumulate before announcing. */
|
||||
static const unsigned int MAX_ADDR_TO_SEND = 1000;
|
||||
/** Maximum length of incoming protocol messages (no message over 3 MiB is currently acceptable). */
|
||||
|
@ -108,6 +108,47 @@ static constexpr int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
|
||||
/// Age after which a block is considered historical for purposes of rate
|
||||
/// limiting block relay. Set to one week, denominated in seconds.
|
||||
static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
|
||||
/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
|
||||
static const int PING_INTERVAL = 2 * 60;
|
||||
/** The maximum number of entries in a locator */
|
||||
static const unsigned int MAX_LOCATOR_SZ = 101;
|
||||
/** Number of blocks that can be requested at any given time from a single peer. */
|
||||
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
|
||||
/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
|
||||
static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
|
||||
/** Maximum depth of blocks we're willing to serve as compact blocks to peers
|
||||
* when requested. For older blocks, a regular BLOCK response will be sent. */
|
||||
static const int MAX_CMPCTBLOCK_DEPTH = 5;
|
||||
/** Maximum depth of blocks we're willing to respond to GETBLOCKTXN requests for. */
|
||||
static const int MAX_BLOCKTXN_DEPTH = 10;
|
||||
/** Size of the "block download window": how far ahead of our current height do we fetch?
|
||||
* Larger windows tolerate larger download speed differences between peer, but increase the potential
|
||||
* degree of disordering of blocks on disk (which make reindexing and pruning harder). We'll probably
|
||||
* want to make this a per-peer adaptive value at some point. */
|
||||
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
|
||||
/** Block download timeout base, expressed in millionths of the block interval (i.e. 10 min) */
|
||||
static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000;
|
||||
/** Additional block download timeout per parallel downloading peer (i.e. 5 min) */
|
||||
static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000;
|
||||
/** Maximum number of headers to announce when relaying blocks with headers message.*/
|
||||
static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;
|
||||
/** Maximum number of unconnecting headers announcements before DoS score */
|
||||
static const int MAX_UNCONNECTING_HEADERS = 10;
|
||||
/** Minimum blocks required to signal NODE_NETWORK_LIMITED */
|
||||
static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288;
|
||||
|
||||
/** Average delay between local address broadcasts in seconds. */
|
||||
static constexpr unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
|
||||
/** Average delay between peer address broadcasts in seconds. */
|
||||
static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
|
||||
/** Average delay between trickled inventory transmissions in seconds.
|
||||
* Blocks and whitelisted receivers bypass this, regular outbound peers get half this delay,
|
||||
* Masternode outbound peers get quarter this delay. */
|
||||
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
|
||||
/** Maximum number of inventory items to send per transmission.
|
||||
* Limits the impact of low-fee transaction floods.
|
||||
* We have 4 times smaller block times in Dash, so we need to push 4 times more invs per 1MB. */
|
||||
static constexpr unsigned int INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK = 4 * 7 * INVENTORY_BROADCAST_INTERVAL;
|
||||
|
||||
struct COrphanTx {
|
||||
// When modifying, adapt the copy of this definition in tests/DoS_tests.
|
||||
@ -124,19 +165,6 @@ size_t nMapOrphanTransactionsSize = 0;
|
||||
void EraseOrphansFor(NodeId peer);
|
||||
|
||||
|
||||
/** Average delay between local address broadcasts in seconds. */
|
||||
static constexpr unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 60 * 60;
|
||||
/** Average delay between peer address broadcasts in seconds. */
|
||||
static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30;
|
||||
/** Average delay between trickled inventory transmissions in seconds.
|
||||
* Blocks and whitelisted receivers bypass this, regular outbound peers get half this delay,
|
||||
* Masternode outbound peers get quarter this delay. */
|
||||
static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5;
|
||||
/** Maximum number of inventory items to send per transmission.
|
||||
* Limits the impact of low-fee transaction floods.
|
||||
* We have 4 times smaller block times in Dash, so we need to push 4 times more invs per 1MB. */
|
||||
static constexpr unsigned int INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK = 4 * 7 * INVENTORY_BROADCAST_INTERVAL;
|
||||
|
||||
// Internal stuff
|
||||
namespace {
|
||||
/** Number of nodes with fSyncStarted. */
|
||||
|
112
src/node/transaction.cpp
Normal file
112
src/node/transaction.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
// 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 <util/validation.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, const bool bypass_limits)
|
||||
{
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
g_connman->RelayTransaction(*tx);
|
||||
|
||||
return true;
|
||||
}
|
43
src/node/transaction.h
Normal file
43
src/node/transaction.h
Normal 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(const CTransactionRef tx, uint256& txid, TransactionError& error, std::string& err_string, const bool allowhighfees = false, const bool bypass_limits = false);
|
||||
|
||||
#endif // BITCOIN_NODE_TRANSACTION_H
|
240
src/psbt.cpp
Normal file
240
src/psbt.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
// 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>
|
||||
|
||||
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 PartiallySignedTransaction::AddInput(const CTxIn& txin, PSBTInput& psbtin)
|
||||
{
|
||||
if (std::find(tx->vin.begin(), tx->vin.end(), txin) != tx->vin.end()) {
|
||||
return false;
|
||||
}
|
||||
tx->vin.push_back(txin);
|
||||
psbtin.partial_sigs.clear();
|
||||
psbtin.final_script_sig.clear();
|
||||
inputs.push_back(psbtin);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PartiallySignedTransaction::AddOutput(const CTxOut& txout, const PSBTOutput& psbtout)
|
||||
{
|
||||
tx->vout.push_back(txout);
|
||||
outputs.push_back(psbtout);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PartiallySignedTransaction::GetInputUTXO(CTxOut& utxo, int input_index) const
|
||||
{
|
||||
PSBTInput input = inputs[input_index];
|
||||
int prevout_index = tx->vin[input_index].prevout.n;
|
||||
if (input.non_witness_utxo) {
|
||||
utxo = input.non_witness_utxo->vout[prevout_index];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PSBTInput::IsNull() const
|
||||
{
|
||||
return !non_witness_utxo && 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 (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_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 SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash)
|
||||
{
|
||||
// if this input has a final scriptsig, don't do anything with it
|
||||
if (!input.final_script_sig.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fill SignatureData with input info
|
||||
SignatureData sigdata;
|
||||
input.FillSignatureData(sigdata);
|
||||
|
||||
// Get UTXO
|
||||
CTxOut utxo;
|
||||
if (input.non_witness_utxo) {
|
||||
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
|
||||
if (input.non_witness_utxo->GetHash() != tx.vin[index].prevout.hash) return false;
|
||||
utxo = input.non_witness_utxo->vout[tx.vin[index].prevout.n];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
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 (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
PSBTInput& input = psbtx.inputs.at(i);
|
||||
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, i, 1);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
506
src/psbt.h
Normal file
506
src/psbt.h
Normal file
@ -0,0 +1,506 @@
|
||||
// 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_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_BIP32_DERIVATION = 0x06;
|
||||
static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
|
||||
|
||||
// Output types
|
||||
static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
|
||||
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;
|
||||
CScript redeem_script;
|
||||
CScript final_script_sig;
|
||||
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 (non_witness_utxo) {
|
||||
SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
|
||||
SerializeToVector(s, non_witness_utxo);
|
||||
}
|
||||
|
||||
if (final_script_sig.empty()) {
|
||||
// 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 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 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");
|
||||
}
|
||||
UnserializeFromVector(s, non_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_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;
|
||||
}
|
||||
// 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;
|
||||
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 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_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;
|
||||
bool AddInput(const CTxIn& txin, PSBTInput& psbtin);
|
||||
bool AddOutput(const CTxOut& txout, const PSBTOutput& psbtout);
|
||||
PartiallySignedTransaction() {}
|
||||
PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {}
|
||||
|
||||
/**
|
||||
* Finds the UTXO for a given input index
|
||||
*
|
||||
* @param[out] utxo The UTXO of the input if found
|
||||
* @param[in] input_index Index of the input to retrieve the UTXO of
|
||||
* @return Whether the UTXO for the specified input was found
|
||||
*/
|
||||
bool GetInputUTXO(CTxOut& utxo, int input_index) const;
|
||||
|
||||
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
|
||||
SerializeToVector(s, *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;
|
||||
UnserializeFromVector(s, mtx);
|
||||
tx = std::move(mtx);
|
||||
// Make sure that all scriptSigs are empty
|
||||
for (const CTxIn& txin : tx->vin) {
|
||||
if (!txin.scriptSig.empty()) {
|
||||
throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs.");
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
|
||||
bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, 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
|
@ -344,7 +344,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
|
||||
|
||||
CTransactionRef tx;
|
||||
uint256 hashBlock = uint256();
|
||||
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
|
||||
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock))
|
||||
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
|
||||
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
@ -2076,7 +2076,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
||||
for (const CTxIn& in : tx->vin) {
|
||||
CTransactionRef tx_in;
|
||||
uint256 hashBlock;
|
||||
if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock, false)) {
|
||||
if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock)) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, std::string("Unexpected internal error (tx index seems corrupt)"));
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,11 @@
|
||||
#include <key_io.h>
|
||||
#include <merkleblock.h>
|
||||
#include <net.h>
|
||||
#include <node/transaction.h>
|
||||
#include <policy/policy.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <psbt.h>
|
||||
#include <rpc/util.h>
|
||||
#include <rpc/rawtransaction.h>
|
||||
#include <rpc/server.h>
|
||||
#include <script/script.h>
|
||||
@ -40,7 +43,6 @@
|
||||
#include <llmq/quorums_commitment.h>
|
||||
#include <llmq/quorums_instantsend.h>
|
||||
|
||||
#include <future>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <univalue.h>
|
||||
@ -110,12 +112,11 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
|
||||
throw std::runtime_error(
|
||||
"getrawtransaction \"txid\" ( verbose \"blockhash\" )\n"
|
||||
|
||||
"\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
|
||||
"enabled, it also works for blockchain transactions. If the block which contains the transaction\n"
|
||||
"is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n"
|
||||
"provided, only that block will be searched and if the transaction is in the mempool or other\n"
|
||||
"blocks, or if this node does not have the given block available, the transaction will not be found.\n"
|
||||
"DEPRECATED: for now, it also works for transactions with unspent outputs.\n"
|
||||
"\nBy default this function only works for mempool transactions. When called with a blockhash\n"
|
||||
"argument, getrawtransaction will return the transaction if the specified block is available and\n"
|
||||
"the transaction is found in that block. When called without a blockhash argument, getrawtransaction\n"
|
||||
"will return the transaction if it is in the mempool, or if -txindex is enabled and the transaction\n"
|
||||
"is in a block in the blockchain.\n"
|
||||
|
||||
"\nReturn the raw transaction data.\n"
|
||||
"\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
|
||||
@ -219,7 +220,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
|
||||
|
||||
CTransactionRef tx;
|
||||
uint256 hash_block;
|
||||
if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, true, blockindex)) {
|
||||
if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, blockindex)) {
|
||||
std::string errmsg;
|
||||
if (blockindex) {
|
||||
if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) {
|
||||
@ -319,7 +320,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
|
||||
if (pblockindex == nullptr)
|
||||
{
|
||||
CTransactionRef tx;
|
||||
if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull())
|
||||
if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock) || hashBlock.IsNull())
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
|
||||
pblockindex = LookupBlockIndex(hashBlock);
|
||||
if (!pblockindex) {
|
||||
@ -1095,8 +1096,6 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
|
||||
);
|
||||
|
||||
std::promise<void> promise;
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL});
|
||||
|
||||
// parse hex string from parameter
|
||||
@ -1104,67 +1103,18 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||
if (!DecodeHexTx(mtx, request.params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
|
||||
const uint256& hashTx = tx->GetHash();
|
||||
|
||||
CAmount nMaxRawTxFee = maxTxFee;
|
||||
if (!request.params[1].isNull() && request.params[1].get_bool())
|
||||
nMaxRawTxFee = 0;
|
||||
|
||||
bool fBypassLimits = false;
|
||||
if (!request.params[3].isNull())
|
||||
fBypassLimits = request.params[3].get_bool();
|
||||
|
||||
{ // 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,
|
||||
fBypassLimits /* 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();
|
||||
bool allowhighfees = false;
|
||||
if (!request.params[1].isNull()) allowhighfees = request.params[1].get_bool();
|
||||
bool bypass_limits = false;
|
||||
if (!request.params[3].isNull()) bypass_limits = request.params[3].get_bool();
|
||||
uint256 txid;
|
||||
TransactionError err;
|
||||
std::string err_string;
|
||||
if (!BroadcastTransaction(tx, txid, err, err_string, allowhighfees, bypass_limits)) {
|
||||
throw JSONRPCTransactionError(err, err_string);
|
||||
}
|
||||
|
||||
} // cs_main
|
||||
|
||||
promise.get_future().wait();
|
||||
|
||||
if(!g_connman)
|
||||
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
|
||||
|
||||
g_connman->RelayTransaction(*tx);
|
||||
|
||||
return hashTx.GetHex();
|
||||
return txid.GetHex();
|
||||
}
|
||||
|
||||
static UniValue testmempoolaccept(const JSONRPCRequest& request)
|
||||
@ -1352,7 +1302,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
|
||||
// Unserialize the transactions
|
||||
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));
|
||||
}
|
||||
|
||||
@ -1522,23 +1472,16 @@ UniValue combinepsbt(const JSONRPCRequest& request)
|
||||
for (unsigned int i = 0; i < txs.size(); ++i) {
|
||||
PartiallySignedTransaction psbtx;
|
||||
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));
|
||||
}
|
||||
psbtxs.push_back(psbtx);
|
||||
}
|
||||
|
||||
PartiallySignedTransaction merged_psbt(psbtxs[0]); // Copy the first one
|
||||
|
||||
// Merge
|
||||
for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
|
||||
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");
|
||||
PartiallySignedTransaction merged_psbt;
|
||||
TransactionError error;
|
||||
if (!CombinePSBTs(merged_psbt, error, psbtxs)) {
|
||||
throw JSONRPCTransactionError(error);
|
||||
}
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
@ -1578,31 +1521,27 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
|
||||
// Unserialize the transactions
|
||||
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));
|
||||
}
|
||||
|
||||
// Get all of the previous transactions
|
||||
bool complete = true;
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
PSBTInput& input = psbtx.inputs.at(i);
|
||||
bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool());
|
||||
|
||||
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, i, 1);
|
||||
}
|
||||
CMutableTransaction mtx;
|
||||
bool complete = FinalizeAndExtractPSBT(psbtx, mtx);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
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) {
|
||||
CMutableTransaction mtx(*psbtx.tx);
|
||||
for (unsigned int i = 0; i < mtx.vin.size(); ++i) {
|
||||
mtx.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
|
||||
}
|
||||
ssTx << mtx;
|
||||
result.pushKV("hex", HexStr(ssTx));
|
||||
result_str = HexStr(ssTx.str());
|
||||
result.pushKV("hex", result_str);
|
||||
} else {
|
||||
ssTx << psbtx;
|
||||
result.pushKV("psbt", EncodeBase64(ssTx.str()));
|
||||
result_str = EncodeBase64(ssTx.str());
|
||||
result.pushKV("psbt", result_str);
|
||||
}
|
||||
result.pushKV("complete", complete);
|
||||
|
||||
|
@ -750,7 +750,7 @@ static UniValue verifyislock(const JSONRPCRequest& request)
|
||||
LOCK(cs_main);
|
||||
CTransactionRef tx;
|
||||
uint256 hash_block;
|
||||
if (GetTransaction(txid, tx, Params().GetConsensus(), hash_block, true) && !hash_block.IsNull()) {
|
||||
if (GetTransaction(txid, tx, Params().GetConsensus(), hash_block) && !hash_block.IsNull()) {
|
||||
pindexMined = LookupBlockIndex(hash_block);
|
||||
}
|
||||
}
|
||||
|
@ -309,7 +309,7 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
|
||||
// this reply will get back to the client.
|
||||
StartShutdown();
|
||||
if (jsonRequest.params[0].isNum()) {
|
||||
MilliSleep(jsonRequest.params[0].get_int());
|
||||
UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].get_int()});
|
||||
}
|
||||
return "Dash Core server stopping";
|
||||
}
|
||||
|
@ -92,3 +92,32 @@ UniValue DescribeAddress(const CTxDestination& dest)
|
||||
{
|
||||
return boost::apply_visitor(DescribeAddressVisitor(), dest);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,10 +5,12 @@
|
||||
#ifndef BITCOIN_RPC_UTIL_H
|
||||
#define BITCOIN_RPC_UTIL_H
|
||||
|
||||
#include <node/transaction.h>
|
||||
#include <pubkey.h>
|
||||
#include <script/standard.h>
|
||||
#include <univalue.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <rpc/protocol.h>
|
||||
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
@ -25,4 +27,7 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey
|
||||
|
||||
UniValue DescribeAddress(const CTxDestination& dest);
|
||||
|
||||
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
|
||||
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");
|
||||
|
||||
#endif // BITCOIN_RPC_UTIL_H
|
||||
|
@ -59,20 +59,25 @@ static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigd
|
||||
return provider.GetPubKey(address, pubkey);
|
||||
}
|
||||
|
||||
static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CKeyID& keyid, const CScript& scriptcode, SigVersion sigversion)
|
||||
static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion)
|
||||
{
|
||||
CKeyID keyid = pubkey.GetID();
|
||||
const auto it = sigdata.signatures.find(keyid);
|
||||
if (it != sigdata.signatures.end()) {
|
||||
sig_out = it->second.second;
|
||||
return true;
|
||||
}
|
||||
CPubKey pubkey;
|
||||
GetPubKey(provider, sigdata, keyid, pubkey);
|
||||
KeyOriginInfo info;
|
||||
if (provider.GetKeyOrigin(keyid, info)) {
|
||||
sigdata.misc_pubkeys.emplace(keyid, std::make_pair(pubkey, std::move(info)));
|
||||
}
|
||||
if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) {
|
||||
auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out));
|
||||
assert(i.second);
|
||||
return true;
|
||||
}
|
||||
// Could not make signature or signature not found, add keyid to missing
|
||||
sigdata.missing_sigs.push_back(keyid);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -100,23 +105,30 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
|
||||
case TX_NULL_DATA:
|
||||
return false;
|
||||
case TX_PUBKEY:
|
||||
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false;
|
||||
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
|
||||
ret.push_back(std::move(sig));
|
||||
return true;
|
||||
case TX_PUBKEYHASH: {
|
||||
CKeyID keyID = CKeyID(uint160(vSolutions[0]));
|
||||
if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false;
|
||||
ret.push_back(std::move(sig));
|
||||
CPubKey pubkey;
|
||||
GetPubKey(provider, sigdata, keyID, pubkey);
|
||||
if (!GetPubKey(provider, sigdata, keyID, pubkey)) {
|
||||
// Pubkey could not be found, add to missing
|
||||
sigdata.missing_pubkeys.push_back(keyID);
|
||||
return false;
|
||||
}
|
||||
if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false;
|
||||
ret.push_back(std::move(sig));
|
||||
ret.push_back(ToByteVector(pubkey));
|
||||
return true;
|
||||
}
|
||||
case TX_SCRIPTHASH:
|
||||
if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) {
|
||||
h160 = uint160(vSolutions[0]);
|
||||
if (GetCScript(provider, sigdata, h160, scriptRet)) {
|
||||
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
|
||||
return true;
|
||||
}
|
||||
// Could not find redeemScript, add to missing
|
||||
sigdata.missing_redeem_script = h160;
|
||||
return false;
|
||||
|
||||
case TX_MULTISIG: {
|
||||
@ -124,7 +136,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
|
||||
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
|
||||
for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
|
||||
CPubKey pubkey = CPubKey(vSolutions[i]);
|
||||
if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) {
|
||||
if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) {
|
||||
ret.push_back(std::move(sig));
|
||||
}
|
||||
}
|
||||
@ -186,33 +198,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
|
||||
return sigdata.complete;
|
||||
}
|
||||
|
||||
bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash)
|
||||
{
|
||||
// if this input has a final scriptsig, don't do anything with it
|
||||
if (!input.final_script_sig.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fill SignatureData with input info
|
||||
SignatureData sigdata;
|
||||
input.FillSignatureData(sigdata);
|
||||
|
||||
// Get UTXO
|
||||
CTxOut utxo;
|
||||
if (input.non_witness_utxo) {
|
||||
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
|
||||
if (input.non_witness_utxo->GetHash() != tx.vin[index].prevout.hash) return false;
|
||||
utxo = input.non_witness_utxo->vout[tx.vin[index].prevout.n];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
|
||||
bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
|
||||
input.FromSignatureData(sigdata);
|
||||
return sig_complete;
|
||||
}
|
||||
|
||||
class SignatureExtractorChecker final : public BaseSignatureChecker
|
||||
{
|
||||
private:
|
||||
@ -414,125 +399,6 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
|
||||
return false;
|
||||
}
|
||||
|
||||
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 && 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 (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_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 HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
|
||||
{
|
||||
|
@ -107,34 +107,15 @@ struct SignatureData {
|
||||
CScript redeem_script; ///< The redeemScript (if any) for the input
|
||||
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig.
|
||||
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;
|
||||
std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found
|
||||
std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found
|
||||
uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any)
|
||||
|
||||
SignatureData() {}
|
||||
explicit SignatureData(const CScript& script) : scriptSig(script) {}
|
||||
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_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_BIP32_DERIVATION = 0x06;
|
||||
static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
|
||||
|
||||
// Output types
|
||||
static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
|
||||
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 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.
|
||||
template<typename Stream, typename... X>
|
||||
@ -208,450 +189,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;
|
||||
CScript redeem_script;
|
||||
CScript final_script_sig;
|
||||
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 (non_witness_utxo) {
|
||||
SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
|
||||
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion());
|
||||
SerializeToVector(os, non_witness_utxo);
|
||||
}
|
||||
|
||||
if (final_script_sig.empty()) {
|
||||
// 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 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 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());
|
||||
UnserializeFromVector(os, non_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_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;
|
||||
}
|
||||
// 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;
|
||||
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 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_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) {}
|
||||
|
||||
// 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());
|
||||
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());
|
||||
UnserializeFromVector(os, mtx);
|
||||
tx = std::move(mtx);
|
||||
// Make sure that all scriptSigs are empty
|
||||
for (const CTxIn& txin : tx->vin) {
|
||||
if (!txin.scriptSig.empty()) {
|
||||
throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs.");
|
||||
}
|
||||
}
|
||||
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. */
|
||||
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata);
|
||||
|
||||
@ -659,9 +196,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 CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
|
||||
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
|
||||
bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash = SIGHASH_ALL);
|
||||
|
||||
/** Extract signature data from a transaction input, and insert it. */
|
||||
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout);
|
||||
void UpdateInput(CTxIn& input, const SignatureData& data);
|
||||
|
@ -372,7 +372,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks)
|
||||
CCheckQueueControl<FakeCheck> control(queue.get());
|
||||
// While sleeping, no other thread should execute to this point
|
||||
auto observed = ++nThreads;
|
||||
MilliSleep(10);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{10});
|
||||
fails += observed != nThreads;
|
||||
});
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
//
|
||||
#include <fs.h>
|
||||
#include <test/test_dash.h>
|
||||
#include <util/getuniquepath.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
@ -51,6 +52,21 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream)
|
||||
file >> input_buffer;
|
||||
BOOST_CHECK_EQUAL(input_buffer, "bitcoin");
|
||||
}
|
||||
{
|
||||
fs::path p1 = GetUniquePath(tmpfolder);
|
||||
fs::path p2 = GetUniquePath(tmpfolder);
|
||||
fs::path p3 = GetUniquePath(tmpfolder);
|
||||
|
||||
// Ensure that the parent path is always the same.
|
||||
BOOST_CHECK_EQUAL(tmpfolder, p1.parent_path());
|
||||
BOOST_CHECK_EQUAL(tmpfolder, p2.parent_path());
|
||||
BOOST_CHECK_EQUAL(tmpfolder, p3.parent_path());
|
||||
|
||||
// Ensure that generated paths are actually different.
|
||||
BOOST_CHECK(p1 != p2);
|
||||
BOOST_CHECK(p2 != p3);
|
||||
BOOST_CHECK(p1 != p3);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <random.h>
|
||||
#include <scheduler.h>
|
||||
#include <util/time.h>
|
||||
|
||||
#include <test/test_dash.h>
|
||||
|
||||
@ -26,18 +27,6 @@ static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delt
|
||||
}
|
||||
}
|
||||
|
||||
static void MicroSleep(uint64_t n)
|
||||
{
|
||||
#if defined(HAVE_WORKING_BOOST_SLEEP_FOR)
|
||||
boost::this_thread::sleep_for(boost::chrono::microseconds(n));
|
||||
#elif defined(HAVE_WORKING_BOOST_SLEEP)
|
||||
boost::this_thread::sleep(boost::posix_time::microseconds(n));
|
||||
#else
|
||||
//should never get here
|
||||
#error missing boost sleep implementation
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(manythreads)
|
||||
{
|
||||
// Stress test: hundreds of microsecond-scheduled tasks,
|
||||
@ -84,7 +73,7 @@ BOOST_AUTO_TEST_CASE(manythreads)
|
||||
for (int i = 0; i < 5; i++)
|
||||
microThreads.create_thread(boost::bind(&CScheduler::serviceQueue, µTasks));
|
||||
|
||||
MicroSleep(600);
|
||||
UninterruptibleSleep(std::chrono::microseconds{600});
|
||||
now = boost::chrono::system_clock::now();
|
||||
|
||||
// More threads and more tasks:
|
||||
|
@ -149,7 +149,7 @@ TestChainSetup::TestChainSetup(int blockCount) : TestingSetup(CBaseChainParams::
|
||||
int64_t time_start = GetTimeMillis();
|
||||
while (!g_txindex->BlockUntilSyncedToCurrentChain()) {
|
||||
assert(time_start + timeout_ms > GetTimeMillis());
|
||||
MilliSleep(100);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
|
||||
int64_t time_start = GetTimeMillis();
|
||||
while (!txindex.BlockUntilSyncedToCurrentChain()) {
|
||||
BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
|
||||
MilliSleep(100);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
|
||||
// Check that txindex excludes genesis block transactions.
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <clientversion.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <sync.h>
|
||||
#include <util/getuniquepath.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
#include <util/moneystr.h>
|
||||
@ -757,7 +758,7 @@ BOOST_AUTO_TEST_CASE(util_time_GetTime)
|
||||
SetMockTime(111);
|
||||
// Check that mock time does not change after a sleep
|
||||
for (const auto& num_sleep : {0, 1}) {
|
||||
MilliSleep(num_sleep);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{num_sleep});
|
||||
BOOST_CHECK_EQUAL(111, GetTime()); // Deprecated time getter
|
||||
BOOST_CHECK_EQUAL(111, GetTime<std::chrono::seconds>().count());
|
||||
BOOST_CHECK_EQUAL(111000, GetTime<std::chrono::milliseconds>().count());
|
||||
@ -768,7 +769,7 @@ BOOST_AUTO_TEST_CASE(util_time_GetTime)
|
||||
// Check that system time changes after a sleep
|
||||
const auto ms_0 = GetTime<std::chrono::milliseconds>();
|
||||
const auto us_0 = GetTime<std::chrono::microseconds>();
|
||||
MilliSleep(1);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{1});
|
||||
BOOST_CHECK(ms_0 < GetTime<std::chrono::milliseconds>());
|
||||
BOOST_CHECK(us_0 < GetTime<std::chrono::microseconds>());
|
||||
}
|
||||
@ -1175,7 +1176,7 @@ BOOST_AUTO_TEST_CASE(test_DirIsWritable)
|
||||
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
|
||||
|
||||
// Should not be able to write to a non-existent dir.
|
||||
tmpdirname = tmpdirname / fs::unique_path();
|
||||
tmpdirname = GetUniquePath(tmpdirname);
|
||||
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), false);
|
||||
|
||||
fs::create_directory(tmpdirname);
|
||||
|
@ -169,7 +169,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
|
||||
|
||||
threads.join_all();
|
||||
while (GetMainSignals().CallbacksPending() > 0) {
|
||||
MilliSleep(100);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
|
||||
UnregisterValidationInterface(&sub);
|
||||
|
10
src/util/getuniquepath.cpp
Normal file
10
src/util/getuniquepath.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include <random.h>
|
||||
#include <fs.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
fs::path GetUniquePath(const fs::path& base)
|
||||
{
|
||||
FastRandomContext rnd;
|
||||
fs::path tmpFile = base / HexStr(rnd.randbytes(8));
|
||||
return tmpFile;
|
||||
}
|
19
src/util/getuniquepath.h
Normal file
19
src/util/getuniquepath.h
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2021 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_UTIL_GETUNIQUEPATH_H
|
||||
#define BITCOIN_UTIL_GETUNIQUEPATH_H
|
||||
|
||||
#include <fs.h>
|
||||
|
||||
/**
|
||||
* Helper function for getting a unique path
|
||||
*
|
||||
* @param[in] base Base path
|
||||
* @returns base joined with a random 8-character long string.
|
||||
* @post Returned path is unique with high probability.
|
||||
*/
|
||||
fs::path GetUniquePath(const fs::path& base);
|
||||
|
||||
#endif // BITCOIN_UTIL_GETUNIQUEPATH_H
|
@ -12,6 +12,7 @@
|
||||
#include <random.h>
|
||||
#include <serialize.h>
|
||||
#include <stacktraces.h>
|
||||
#include <util/getuniquepath.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
@ -196,7 +197,7 @@ void ReleaseDirectoryLocks()
|
||||
|
||||
bool DirIsWritable(const fs::path& directory)
|
||||
{
|
||||
fs::path tmpFile = directory / fs::unique_path();
|
||||
fs::path tmpFile = GetUniquePath(directory);
|
||||
|
||||
FILE* file = fsbridge::fopen(tmpFile, "a");
|
||||
if (!file) return false;
|
||||
@ -1252,7 +1253,7 @@ void RenameThreadPool(ctpl::thread_pool& tp, const char* baseName)
|
||||
|
||||
do {
|
||||
// Always sleep to let all threads acquire locks
|
||||
MilliSleep(10);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{10});
|
||||
// `doneCnt` should be at least `futures.size()` if tp size was increased (for whatever reason),
|
||||
// or at least `tp.size()` if tp size was decreased and queue was cleared
|
||||
// (which can happen on `stop()` if we were not fast enough to get all jobs to their threads).
|
||||
|
@ -11,10 +11,13 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <ctime>
|
||||
#include <thread>
|
||||
|
||||
#include <tinyformat.h>
|
||||
|
||||
void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }
|
||||
|
||||
static std::atomic<int64_t> nMockTime(0); //!< For unit testing
|
||||
|
||||
int64_t GetTime()
|
||||
@ -70,31 +73,13 @@ int64_t GetSystemTimeInSeconds()
|
||||
return GetTimeMicros()/1000000;
|
||||
}
|
||||
|
||||
void MilliSleep(int64_t n)
|
||||
{
|
||||
|
||||
/**
|
||||
* Boost's sleep_for was uninterruptible when backed by nanosleep from 1.50
|
||||
* until fixed in 1.52. Use the deprecated sleep method for the broken case.
|
||||
* See: https://svn.boost.org/trac/boost/ticket/7238
|
||||
*/
|
||||
#if defined(HAVE_WORKING_BOOST_SLEEP_FOR)
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(n));
|
||||
#elif defined(HAVE_WORKING_BOOST_SLEEP)
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(n));
|
||||
#else
|
||||
//should never get here
|
||||
#error missing boost sleep implementation
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string FormatISO8601DateTime(int64_t nTime) {
|
||||
struct tm ts;
|
||||
time_t time_val = nTime;
|
||||
#ifdef _MSC_VER
|
||||
gmtime_s(&ts, &time_val);
|
||||
#else
|
||||
#ifdef HAVE_GMTIME_R
|
||||
gmtime_r(&time_val, &ts);
|
||||
#else
|
||||
gmtime_s(&ts, &time_val);
|
||||
#endif
|
||||
return strprintf("%04i-%02i-%02iT%02i:%02i:%02iZ", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
|
||||
}
|
||||
@ -102,10 +87,10 @@ std::string FormatISO8601DateTime(int64_t nTime) {
|
||||
std::string FormatISO8601Date(int64_t nTime) {
|
||||
struct tm ts;
|
||||
time_t time_val = nTime;
|
||||
#ifdef _MSC_VER
|
||||
gmtime_s(&ts, &time_val);
|
||||
#else
|
||||
#ifdef HAVE_GMTIME_R
|
||||
gmtime_r(&time_val, &ts);
|
||||
#else
|
||||
gmtime_s(&ts, &time_val);
|
||||
#endif
|
||||
return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday);
|
||||
}
|
||||
@ -113,10 +98,10 @@ std::string FormatISO8601Date(int64_t nTime) {
|
||||
std::string FormatISO8601Time(int64_t nTime) {
|
||||
struct tm ts;
|
||||
time_t time_val = nTime;
|
||||
#ifdef _MSC_VER
|
||||
gmtime_s(&ts, &time_val);
|
||||
#else
|
||||
#ifdef HAVE_GMTIME_R
|
||||
gmtime_r(&time_val, &ts);
|
||||
#else
|
||||
gmtime_s(&ts, &time_val);
|
||||
#endif
|
||||
return strprintf("%02i:%02i:%02iZ", ts.tm_hour, ts.tm_min, ts.tm_sec);
|
||||
}
|
||||
|
@ -10,6 +10,16 @@
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
void UninterruptibleSleep(const std::chrono::microseconds& n);
|
||||
|
||||
/**
|
||||
* Helper to count the seconds of a duration.
|
||||
*
|
||||
* All durations should be using std::chrono and calling this should generally be avoided in code. Though, it is still
|
||||
* preferred to an inline t.count() to protect against a reliance on the exact type of t.
|
||||
*/
|
||||
inline int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
* Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
|
||||
@ -28,8 +38,6 @@ void SetMockTime(int64_t nMockTimeIn);
|
||||
/** For testing */
|
||||
int64_t GetMockTime();
|
||||
|
||||
void MilliSleep(int64_t n);
|
||||
|
||||
/** Return system time (or mocked time, if set) */
|
||||
template <typename T>
|
||||
T GetTime();
|
||||
|
@ -67,6 +67,19 @@
|
||||
#define MICRO 0.000001
|
||||
#define MILLI 0.001
|
||||
|
||||
/** Maximum kilobytes for transactions to store for processing during reorg */
|
||||
static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
|
||||
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
|
||||
static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
|
||||
/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
|
||||
static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
|
||||
/** Time to wait (in seconds) between writing blocks/block index to disk. */
|
||||
static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
|
||||
/** Time to wait (in seconds) between flushing chainstate to disk. */
|
||||
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
|
||||
/** Maximum age of our tip in seconds for us to be considered current for fee estimation */
|
||||
static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60;
|
||||
|
||||
/**
|
||||
* Global state
|
||||
*/
|
||||
@ -974,13 +987,11 @@ bool GetAddressUnspent(uint160 addressHash, int type,
|
||||
* Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock.
|
||||
* If blockIndex is provided, the transaction is fetched from the corresponding block.
|
||||
*/
|
||||
bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, bool fAllowSlow, CBlockIndex* blockIndex)
|
||||
bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, const CBlockIndex* const block_index)
|
||||
{
|
||||
CBlockIndex* pindexSlow = blockIndex;
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
if (!blockIndex) {
|
||||
if (!block_index) {
|
||||
CTransactionRef ptx = mempool.get(hash);
|
||||
if (ptx) {
|
||||
txOut = ptx;
|
||||
@ -990,20 +1001,13 @@ bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus
|
||||
if (g_txindex) {
|
||||
return g_txindex->FindTx(hash, hashBlock, txOut);
|
||||
}
|
||||
|
||||
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
||||
const Coin& coin = AccessByTxid(*pcoinsTip, hash);
|
||||
if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
|
||||
}
|
||||
}
|
||||
|
||||
if (pindexSlow) {
|
||||
} else {
|
||||
CBlock block;
|
||||
if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) {
|
||||
if (ReadBlockFromDisk(block, block_index, consensusParams)) {
|
||||
for (const auto& tx : block.vtx) {
|
||||
if (tx->GetHash() == hash) {
|
||||
txOut = tx;
|
||||
hashBlock = pindexSlow->GetBlockHash();
|
||||
hashBlock = block_index->GetBlockHash();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -66,50 +66,19 @@ static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25;
|
||||
static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101;
|
||||
/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */
|
||||
static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336;
|
||||
/** Maximum kilobytes for transactions to store for processing during reorg */
|
||||
static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
|
||||
/** The maximum size of a blk?????.dat file (since 0.8) */
|
||||
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
|
||||
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
|
||||
static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
|
||||
/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
|
||||
static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
|
||||
|
||||
/** Maximum number of dedicated script-checking threads allowed */
|
||||
static const int MAX_SCRIPTCHECK_THREADS = 15;
|
||||
/** -par default (number of script-checking threads, 0 = auto) */
|
||||
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
|
||||
/** Number of blocks that can be requested at any given time from a single peer. */
|
||||
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
|
||||
/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
|
||||
static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
|
||||
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
|
||||
* less than this number, we reached its tip. Changing this value is a protocol upgrade. */
|
||||
static const unsigned int MAX_HEADERS_RESULTS = 2000;
|
||||
/** Maximum depth of blocks we're willing to serve as compact blocks to peers
|
||||
* when requested. For older blocks, a regular BLOCK response will be sent. */
|
||||
static const int MAX_CMPCTBLOCK_DEPTH = 5;
|
||||
/** Maximum depth of blocks we're willing to respond to GETBLOCKTXN requests for. */
|
||||
static const int MAX_BLOCKTXN_DEPTH = 10;
|
||||
/** Size of the "block download window": how far ahead of our current height do we fetch?
|
||||
* Larger windows tolerate larger download speed differences between peer, but increase the potential
|
||||
* degree of disordering of blocks on disk (which make reindexing and pruning harder). We'll probably
|
||||
* want to make this a per-peer adaptive value at some point. */
|
||||
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
|
||||
/** Time to wait (in seconds) between writing blocks/block index to disk. */
|
||||
static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
|
||||
/** Time to wait (in seconds) between flushing chainstate to disk. */
|
||||
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
|
||||
/** Maximum length of reject messages. */
|
||||
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
|
||||
/** Block download timeout base, expressed in millionths of the block interval (i.e. 2.5 min) */
|
||||
static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000;
|
||||
/** Additional block download timeout per parallel downloading peer (i.e. 1.25 min) */
|
||||
static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000;
|
||||
|
||||
static const int64_t DEFAULT_MAX_TIP_AGE = 6 * 60 * 60; // ~144 blocks behind -> 2 x fork detection time, was 24 * 60 * 60 in bitcoin
|
||||
/** Maximum age of our tip in seconds for us to be considered current for fee estimation */
|
||||
static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60;
|
||||
|
||||
/** Default for -permitbaremultisig */
|
||||
static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
|
||||
@ -125,14 +94,22 @@ static const bool DEFAULT_PERSIST_MEMPOOL = true;
|
||||
/** Default for -syncmempool */
|
||||
static const bool DEFAULT_SYNC_MEMPOOL = true;
|
||||
|
||||
/** Maximum number of headers to announce when relaying blocks with headers message.*/
|
||||
static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;
|
||||
|
||||
/** Maximum number of unconnecting headers announcements before DoS score */
|
||||
static const int MAX_UNCONNECTING_HEADERS = 10;
|
||||
|
||||
/** Default for -stopatheight */
|
||||
static const int DEFAULT_STOPATHEIGHT = 0;
|
||||
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
|
||||
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
|
||||
static const signed int DEFAULT_CHECKBLOCKS = 6;
|
||||
static const unsigned int DEFAULT_CHECKLEVEL = 3;
|
||||
|
||||
// Require that user allocate at least 945MB for block & undo files (blk???.dat and rev???.dat)
|
||||
// At 2MB per block, 288 blocks = 576MB.
|
||||
// Add 15% for Undo data = 662MB
|
||||
// Add 20% for Orphan block rate = 794MB
|
||||
// We want the low water mark after pruning to be at least 794 MB and since we prune in
|
||||
// full block file chunks, we need the high water mark which triggers the prune to be
|
||||
// one 128MB block file + added 15% undo data = 147MB greater for a total of 941MB
|
||||
// Setting the target to > than 945MB will make it likely we can respect the target.
|
||||
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 945 * 1024 * 1024;
|
||||
|
||||
struct BlockHasher
|
||||
{
|
||||
@ -200,23 +177,6 @@ extern bool fHavePruned;
|
||||
extern bool fPruneMode;
|
||||
/** Number of MiB of block files that we're trying to stay below. */
|
||||
extern uint64_t nPruneTarget;
|
||||
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
|
||||
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
|
||||
/** Minimum blocks required to signal NODE_NETWORK_LIMITED */
|
||||
static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288;
|
||||
|
||||
static const signed int DEFAULT_CHECKBLOCKS = 6;
|
||||
static const unsigned int DEFAULT_CHECKLEVEL = 3;
|
||||
|
||||
// Require that user allocate at least 945MB for block & undo files (blk???.dat and rev???.dat)
|
||||
// At 2MB per block, 288 blocks = 576MB.
|
||||
// Add 15% for Undo data = 662MB
|
||||
// Add 20% for Orphan block rate = 794MB
|
||||
// We want the low water mark after pruning to be at least 794 MB and since we prune in
|
||||
// full block file chunks, we need the high water mark which triggers the prune to be
|
||||
// one 128MB block file + added 15% undo data = 147MB greater for a total of 941MB
|
||||
// Setting the target to > than 945MB will make it likely we can respect the target.
|
||||
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 945 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* Process an incoming block. This only returns after the best known valid
|
||||
@ -278,7 +238,7 @@ void StopScriptCheckWorkerThreads();
|
||||
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
|
||||
bool IsInitialBlockDownload();
|
||||
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
|
||||
bool GetTransaction(const uint256& hash, CTransactionRef& tx, const Consensus::Params& params, uint256& hashBlock, bool fAllowSlow = false, CBlockIndex* blockIndex = nullptr);
|
||||
bool GetTransaction(const uint256& hash, CTransactionRef& tx, const Consensus::Params& params, uint256& hashBlock, const CBlockIndex* const blockIndex = nullptr);
|
||||
/**
|
||||
* Find the best known block, and make it the tip of the block chain
|
||||
*
|
||||
|
@ -708,7 +708,7 @@ bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
|
||||
return fSuccess;
|
||||
}
|
||||
}
|
||||
MilliSleep(100);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
}
|
||||
|
||||
@ -838,7 +838,7 @@ bool BerkeleyDatabase::Backup(const std::string& strDest)
|
||||
}
|
||||
}
|
||||
}
|
||||
MilliSleep(100);
|
||||
UninterruptibleSleep(std::chrono::milliseconds{100});
|
||||
}
|
||||
}
|
||||
|
||||
|
50
src/wallet/psbtwallet.cpp
Normal file
50
src/wallet/psbtwallet.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
// 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);
|
||||
|
||||
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.tx, input, 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
36
src/wallet/psbtwallet.h
Normal 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
|
@ -11,6 +11,7 @@
|
||||
#include <httpserver.h>
|
||||
#include <keepass.h>
|
||||
#include <net.h>
|
||||
#include <node/transaction.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <policy/fees.h>
|
||||
#include <rpc/mining.h>
|
||||
@ -26,6 +27,7 @@
|
||||
#include <util/validation.h>
|
||||
#include <validation.h>
|
||||
#include <wallet/coincontrol.h>
|
||||
#include <wallet/psbtwallet.h>
|
||||
#include <wallet/rpcwallet.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <wallet/walletdb.h>
|
||||
@ -4396,49 +4398,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, const CTransaction* txConst, 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 < txConst->vin.size(); ++i) {
|
||||
const CTxIn& txin = txConst->vin[i];
|
||||
PSBTInput& input = psbtx.inputs.at(i);
|
||||
|
||||
// If we don't know about this input, skip it and let someone else deal with it
|
||||
const uint256& txhash = txin.prevout.hash;
|
||||
const auto it = pwallet->mapWallet.find(txhash);
|
||||
if (it != pwallet->mapWallet.end()) {
|
||||
const CWalletTx& wtx = it->second;
|
||||
CTxOut utxo = wtx.tx->vout[txin.prevout.n];
|
||||
// Update both UTXOs from the wallet.
|
||||
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.tx, input, 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 < txConst->vout.size(); ++i) {
|
||||
const CTxOut& out = txConst->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);
|
||||
@ -4483,7 +4442,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));
|
||||
}
|
||||
|
||||
@ -4497,7 +4456,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, &txConst, 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);
|
||||
@ -4605,7 +4568,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, &txConst, 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);
|
||||
|
@ -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, const CTransaction* txConst, int sighash_type = 1, bool sign = true, bool bip32derivs = false);
|
||||
#endif //BITCOIN_WALLET_RPCWALLET_H
|
||||
|
@ -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>
|
||||
@ -58,7 +59,9 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
|
||||
const CTransaction txConst(*psbtx.tx);
|
||||
|
||||
// Fill transaction with our data
|
||||
FillPSBT(&m_wallet, psbtx, &txConst, 1, false, true);
|
||||
TransactionError err;
|
||||
bool complete = true;
|
||||
FillPSBT(&m_wallet, psbtx, err, complete, false, true);
|
||||
|
||||
// Get the final tx
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
@ -41,7 +41,8 @@ class RESTTest (BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 2
|
||||
self.extra_args = [["-rest"], []]
|
||||
# TODO: remove -txindex. Currently required for getrawtransaction call.
|
||||
self.extra_args = [["-rest", "-txindex"], []]
|
||||
|
||||
def test_rest_request(self, uri, http_method='GET', req_type=ReqType.JSON, body='', status=200, ret_type=RetType.JSON):
|
||||
rest_uri = '/rest' + uri
|
||||
|
@ -16,6 +16,8 @@ class PSBTTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = False
|
||||
self.num_nodes = 3
|
||||
# TODO: remove -txindex. Currently required for getrawtransaction call.
|
||||
self.extra_args = [[], ["-txindex"], ["-txindex"]]
|
||||
|
||||
def run_test(self):
|
||||
# Create and fund a raw tx for sending 10 BTC
|
||||
@ -179,6 +181,13 @@ class PSBTTest(BitcoinTestFramework):
|
||||
extracted = self.nodes[2].finalizepsbt(extractor['extract'], True)['hex']
|
||||
assert_equal(extracted, extractor['result'])
|
||||
|
||||
# Test that psbts with p2pkh outputs are created properly
|
||||
p2pkh = self.nodes[0].getnewaddress()
|
||||
psbt = self.nodes[1].walletcreatefundedpsbt([], [{p2pkh : 1}], 0, {"includeWatching" : True}, True)
|
||||
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__':
|
||||
PSBTTest().main()
|
||||
|
@ -42,6 +42,8 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 3
|
||||
# TODO: remove -txindex. Currently required for getrawtransaction call.
|
||||
self.extra_args = [["-txindex"], ["-txindex"], ["-txindex"]]
|
||||
|
||||
def setup_network(self, split=False):
|
||||
super().setup_network()
|
||||
|
@ -18,7 +18,8 @@ from test_framework.util import assert_equal, assert_raises_rpc_error, connect_n
|
||||
class AbandonConflictTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 2
|
||||
self.extra_args = [["-minrelaytxfee=0.00001"], []]
|
||||
# TODO: remove -txindex. Currently required for getrawtransaction call.
|
||||
self.extra_args = [["-minrelaytxfee=0.00001", "-txindex"], []]
|
||||
|
||||
def run_test(self):
|
||||
self.nodes[1].generate(100)
|
||||
|
@ -21,7 +21,8 @@ class WalletTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 4
|
||||
self.setup_clean_chain = True
|
||||
self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)]
|
||||
# TODO: remove -txindex. Currently required for getrawtransaction call.
|
||||
self.extra_args = [['-txindex', '-usehd={:d}'.format(i%2==0)] for i in range(4)]
|
||||
|
||||
def setup_network(self):
|
||||
self.add_nodes(4, self.extra_args)
|
||||
|
Loading…
Reference in New Issue
Block a user