Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,39 @@ fi

AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h])

# FD_ZERO may be dependent on a declaration of memcpy, e.g. in SmartOS
# check that it fails to build without memcpy, then that it builds with
AC_MSG_CHECKING(FD_ZERO memcpy dependence)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <cstddef>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
]],[[
#if HAVE_SYS_SELECT_H
fd_set fds;
FD_ZERO(&fds);
#endif
]])],
[ AC_MSG_RESULT(no) ],
[
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <cstring>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
]], [[
#if HAVE_SYS_SELECT_H
fd_set fds;
FD_ZERO(&fds);
#endif
]])],
[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_CSTRING_DEPENDENT_FD_ZERO, 1, [Define this symbol if FD_ZERO is dependent of a memcpy declaration being available]) ],
[ AC_MSG_ERROR(failed with cstring include) ]
)
]
)

AC_CHECK_DECLS([getifaddrs, freeifaddrs],,,
[#include <sys/types.h>
#include <ifaddrs.h>]
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,7 @@ libdash_util_a_SOURCES = \
support/lockedpool.cpp \
chainparamsbase.cpp \
clientversion.cpp \
compat/glibc_sanity_fdelt.cpp \
compat/glibc_sanity.cpp \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
Expand Down
17 changes: 1 addition & 16 deletions src/compat/glibc_sanity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <cstddef>

#if defined(HAVE_SYS_SELECT_H)
#include <sys/select.h>
bool sanity_test_fdelt();
#endif

extern "C" void* memcpy(void* a, const void* b, size_t c);
Expand Down Expand Up @@ -41,21 +41,6 @@ bool sanity_test_memcpy()
}
return true;
}

#if defined(HAVE_SYS_SELECT_H)
// trigger: Call FD_SET to trigger __fdelt_chk. FORTIFY_SOURCE must be defined
// as >0 and optimizations must be set to at least -O2.
// test: Add a file descriptor to an empty fd_set. Verify that it has been
// correctly added.
bool sanity_test_fdelt()
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return FD_ISSET(0, &fds);
}
#endif

} // namespace

bool glibc_sanity_test()
Expand Down
26 changes: 26 additions & 0 deletions src/compat/glibc_sanity_fdelt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#if defined(HAVE_CONFIG_H)
#include <config/dash-config.h>
#endif

#if defined(HAVE_SYS_SELECT_H)
#ifdef HAVE_CSTRING_DEPENDENT_FD_ZERO
#include <cstring>
#endif
#include <sys/select.h>

// trigger: Call FD_SET to trigger __fdelt_chk. FORTIFY_SOURCE must be defined
// as >0 and optimizations must be set to at least -O2.
// test: Add a file descriptor to an empty fd_set. Verify that it has been
// correctly added.
bool sanity_test_fdelt()
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return FD_ISSET(0, &fds);
}
#endif
4 changes: 2 additions & 2 deletions src/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
out.pushKV("type", GetTxnOutputType(type));

CTxDestination address;
if (include_address && ExtractDestination(script, address)) {
if (include_address && ExtractDestination(script, address) && type != TX_PUBKEY) {
out.pushKV("address", EncodeDestination(address));
}
}
Expand All @@ -170,7 +170,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
if (fIncludeHex)
out.pushKV("hex", HexStr(scriptPubKey));

if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired) || type == TX_PUBKEY) {
out.pushKV("type", GetTxnOutputType(type));
return;
}
Expand Down
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createpsbt", 1, "outputs" },
{ "createpsbt", 2, "locktime" },
{ "combinepsbt", 0, "txs"},
{ "joinpsbts", 0, "txs"},
{ "finalizepsbt", 1, "extract"},
{ "converttopsbt", 1, "permitsigdata"},
{ "gettxout", 1, "n" },
Expand Down
139 changes: 139 additions & 0 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <chainparams.h>
#include <coins.h>
#include <consensus/validation.h>
#include <consensus/tx_verify.h>
#include <core_io.h>
#include <index/txindex.h>
#include <init.h>
Expand Down Expand Up @@ -38,6 +39,7 @@
#include <llmq/chainlocks.h>
#include <llmq/instantsend.h>

#include <numeric>
#include <stdint.h>

#include <univalue.h>
Expand Down Expand Up @@ -1392,6 +1394,141 @@ UniValue converttopsbt(const JSONRPCRequest& request)
return EncodeBase64(ssTx.str());
}

UniValue utxoupdatepsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
RPCHelpMan{"utxoupdatepsbt",
"\nUpdates a PSBT with UTXOs retrieved from the UTXO set or the mempool.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
},
RPCResult {
" \"psbt\" (string) The base64-encoded partially signed transaction with inputs updated\n"
},
RPCExamples {
HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
}}.ToString());
}

RPCTypeCheck(request.params, {UniValue::VSTR}, true);

// Unserialize the transactions
PartiallySignedTransaction psbtx;
std::string error;
if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}

// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
LOCK2(cs_main, mempool.cs);
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view

for (const CTxIn& txin : psbtx.tx->vin) {
view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
}

view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
}

// Fill the inputs
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
PSBTInput& input = psbtx.inputs.at(i);

if (input.non_witness_utxo) {
continue;
}
Comment on lines +1443 to +1445
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

13932: hmmm... doesn't this mean that in our case utxoupdatepsbt simply does nothing all the time?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it exists only to exist but I presume that its absence may be an unexpected API deviation so it was added in despite practically being a NOP.


const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout);

std::vector<std::vector<unsigned char>> solutions_data;
Solver(coin.out.scriptPubKey, solutions_data);
}

CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
return EncodeBase64(ssTx.str());
}

UniValue joinpsbts(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
RPCHelpMan{"joinpsbts",
"\nJoins multiple distinct PSBTs with different inputs and outputs into one PSBT with inputs and outputs from all of the PSBTs\n"
"No input in any of the PSBTs can be in more than one of the PSBTs.\n",
{
{"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of base64 strings of partially signed transactions",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
}}
},
RPCResult {
" \"psbt\" (string) The base64-encoded partially signed transaction\n"
},
RPCExamples {
HelpExampleCli("joinpsbts", "\"psbt\"")
}}.ToString());
}

RPCTypeCheck(request.params, {UniValue::VARR}, true);

// Unserialize the transactions
std::vector<PartiallySignedTransaction> psbtxs;
UniValue txs = request.params[0].get_array();

if (txs.size() <= 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "At least two PSBTs are required to join PSBTs.");
}

int32_t best_version = 1;
uint32_t best_locktime = 0xffffffff;
for (unsigned int i = 0; i < txs.size(); ++i) {
PartiallySignedTransaction psbtx;
std::string error;
if (!DecodeBase64PSBT(psbtx, txs[i].get_str(), error)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
psbtxs.push_back(psbtx);
// Choose the highest version number
if (psbtx.tx->nVersion > best_version) {
best_version = psbtx.tx->nVersion;
}
// Choose the lowest lock time
if (psbtx.tx->nLockTime < best_locktime) {
best_locktime = psbtx.tx->nLockTime;
}
}

// Create a blank psbt where everything will be added
PartiallySignedTransaction merged_psbt;
merged_psbt.tx = CMutableTransaction();
merged_psbt.tx->nVersion = best_version;
merged_psbt.tx->nLockTime = best_locktime;

// Merge
for (auto& psbt : psbtxs) {
for (unsigned int i = 0; i < psbt.tx->vin.size(); ++i) {
if (!merged_psbt.AddInput(psbt.tx->vin[i], psbt.inputs[i])) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input %s:%d exists in multiple PSBTs", psbt.tx->vin[i].prevout.hash.ToString().c_str(), psbt.tx->vin[i].prevout.n));
}
}
for (unsigned int i = 0; i < psbt.tx->vout.size(); ++i) {
merged_psbt.AddOutput(psbt.tx->vout[i], psbt.outputs[i]);
}
merged_psbt.unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
}

CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << merged_psbt;
return EncodeBase64(ssTx.str());
}

// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
Expand All @@ -1409,6 +1546,8 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
{ "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime"} },
{ "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata"} },
{ "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt"} },
{ "rawtransactions", "joinpsbts", &joinpsbts, {"txs"} },

{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
Expand Down
11 changes: 5 additions & 6 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2649,15 +2649,14 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
if (nUpgraded > 0)
AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
}
std::string strMessage = strprintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__,
std::string strMessage = strprintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo) evodb_cache=%.1fMiB%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize());
strMessage += strprintf(" evodb_cache=%.1fMiB", evoDb->GetMemoryUsage() * (1.0 / (1<<20)));
if (!warningMessages.empty())
strMessage += strprintf(" warning='%s'", warningMessages);
LogPrintf("%s\n", strMessage);
GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
evoDb->GetMemoryUsage() * (1.0 / (1<<20)),
!warningMessages.empty() ? strprintf(" warning='%s'", warningMessages) : "");

}

/** Disconnect m_chain's tip.
Expand Down
Loading