aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.qttest.include3
-rw-r--r--src/addrman.cpp8
-rw-r--r--src/bench/bench_bitcoin.cpp4
-rw-r--r--src/bitcoin-chainstate.cpp2
-rw-r--r--src/index/coinstatsindex.cpp17
-rw-r--r--src/index/coinstatsindex.h2
-rw-r--r--src/init.cpp9
-rw-r--r--src/init/common.cpp2
-rw-r--r--src/leveldb/util/env_posix.cc4
-rw-r--r--src/leveldb/util/env_windows.cc4
-rw-r--r--src/net_processing.cpp7
-rw-r--r--src/node/blockstorage.cpp59
-rw-r--r--src/node/blockstorage.h11
-rw-r--r--src/node/miner.cpp17
-rw-r--r--src/node/miner.h1
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/guiutil.cpp33
-rw-r--r--src/qt/paymentserver.cpp23
-rw-r--r--src/qt/test/optiontests.cpp31
-rw-r--r--src/qt/test/optiontests.h25
-rw-r--r--src/qt/test/test_main.cpp5
-rw-r--r--src/rpc/blockchain.cpp8
-rw-r--r--src/script/descriptor.cpp68
-rw-r--r--src/script/script.h3
-rw-r--r--src/script/sign.cpp23
-rw-r--r--src/script/standard.cpp72
-rw-r--r--src/script/standard.h4
-rw-r--r--src/test/coinstatsindex_tests.cpp61
-rw-r--r--src/test/fs_tests.cpp6
-rw-r--r--src/test/fuzz/script_format.cpp5
-rw-r--r--src/test/getarg_tests.cpp130
-rw-r--r--src/test/util/setup_common.cpp3
-rw-r--r--src/test/util/validation.cpp6
-rw-r--r--src/test/util/validation.h8
-rw-r--r--src/test/util_tests.cpp49
-rw-r--r--src/test/validation_chainstate_tests.cpp3
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp2
-rw-r--r--src/util/readwritefile.cpp4
-rw-r--r--src/util/system.cpp15
-rw-r--r--src/util/system.h22
-rw-r--r--src/validation.cpp76
-rw-r--r--src/validation.h8
-rw-r--r--src/validationinterface.h1
-rw-r--r--src/wallet/coinselection.cpp2
-rw-r--r--src/wallet/rpc/spend.cpp6
-rw-r--r--src/wallet/rpc/transactions.cpp2
-rw-r--r--src/wallet/spend.cpp1
-rw-r--r--src/wallet/sqlite.cpp67
-rw-r--r--src/wallet/test/wallet_tests.cpp4
-rw-r--r--src/wallet/transaction.h1
50 files changed, 653 insertions, 276 deletions
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 8e6fa2eb0d..29c322fbc2 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -7,6 +7,7 @@ TESTS += qt/test/test_bitcoin-qt
TEST_QT_MOC_CPP = \
qt/test/moc_apptests.cpp \
+ qt/test/moc_optiontests.cpp \
qt/test/moc_rpcnestedtests.cpp \
qt/test/moc_uritests.cpp
@@ -19,6 +20,7 @@ endif # ENABLE_WALLET
TEST_QT_H = \
qt/test/addressbooktests.h \
qt/test/apptests.h \
+ qt/test/optiontests.h \
qt/test/rpcnestedtests.h \
qt/test/uritests.h \
qt/test/util.h \
@@ -30,6 +32,7 @@ qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_
qt_test_test_bitcoin_qt_SOURCES = \
init/bitcoin-qt.cpp \
qt/test/apptests.cpp \
+ qt/test/optiontests.cpp \
qt/test/rpcnestedtests.cpp \
qt/test/test_main.cpp \
qt/test/uritests.cpp \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index f91a979934..2fd8143c1c 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -246,12 +246,18 @@ void AddrManImpl::Unserialize(Stream& s_)
uint8_t compat;
s >> compat;
+ if (compat < INCOMPATIBILITY_BASE) {
+ throw std::ios_base::failure(strprintf(
+ "Corrupted addrman database: The compat value (%u) "
+ "is lower than the expected minimum value %u.",
+ compat, INCOMPATIBILITY_BASE));
+ }
const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
if (lowest_compatible > FILE_FORMAT) {
throw InvalidAddrManVersionError(strprintf(
"Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
"but the maximum supported by this version of %s is %u.",
- uint8_t{format}, uint8_t{lowest_compatible}, PACKAGE_NAME, uint8_t{FILE_FORMAT}));
+ uint8_t{format}, lowest_compatible, PACKAGE_NAME, uint8_t{FILE_FORMAT}));
}
s >> nKey;
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 3f8bff4bcf..d6f9c0f8b5 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -109,8 +109,8 @@ int main(int argc, char** argv)
args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
args.is_list_only = argsman.GetBoolArg("-list", false);
args.min_time = std::chrono::milliseconds(argsman.GetIntArg("-min_time", DEFAULT_MIN_TIME_MS));
- args.output_csv = fs::PathFromString(argsman.GetArg("-output_csv", ""));
- args.output_json = fs::PathFromString(argsman.GetArg("-output_json", ""));
+ args.output_csv = argsman.GetPathArg("-output_csv");
+ args.output_json = argsman.GetPathArg("-output_json");
args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
benchmark::BenchRunner::RunAll(args);
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index f93197350d..72b8fefcc7 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -256,7 +256,7 @@ epilogue:
}
GetMainSignals().UnregisterBackgroundSignalScheduler();
- UnloadBlockIndex(nullptr, chainman);
+ WITH_LOCK(::cs_main, UnloadBlockIndex(nullptr, chainman));
init::UnsetGlobals();
}
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index a1c8a5937c..386eb67ce9 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -228,10 +228,9 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
m_muhash.Finalize(out);
value.second.muhash = out;
- CDBBatch batch(*m_db);
- batch.Write(DBHeightKey(pindex->nHeight), value);
- batch.Write(DB_MUHASH, m_muhash);
- return m_db->WriteBatch(batch);
+ // Intentionally do not update DB_MUHASH here so it stays in sync with
+ // DB_BEST_BLOCK, and the index is not corrupted if there is an unclean shutdown.
+ return m_db->Write(DBHeightKey(pindex->nHeight), value);
}
static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
@@ -388,6 +387,14 @@ bool CoinStatsIndex::Init()
return true;
}
+bool CoinStatsIndex::CommitInternal(CDBBatch& batch)
+{
+ // DB_MUHASH should always be committed in a batch together with DB_BEST_BLOCK
+ // to prevent an inconsistent state of the DB.
+ batch.Write(DB_MUHASH, m_muhash);
+ return BaseIndex::CommitInternal(batch);
+}
+
// Reverse a single block as part of a reorg
bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex)
{
@@ -489,5 +496,5 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
Assert(m_total_unspendables_scripts == read_out.second.total_unspendables_scripts);
Assert(m_total_unspendables_unclaimed_rewards == read_out.second.total_unspendables_unclaimed_rewards);
- return m_db->Write(DB_MUHASH, m_muhash);
+ return true;
}
diff --git a/src/index/coinstatsindex.h b/src/index/coinstatsindex.h
index d2a6c9c964..24190ac137 100644
--- a/src/index/coinstatsindex.h
+++ b/src/index/coinstatsindex.h
@@ -39,6 +39,8 @@ private:
protected:
bool Init() override;
+ bool CommitInternal(CDBBatch& batch) override;
+
bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override;
diff --git a/src/init.cpp b/src/init.cpp
index a3d53c3fae..bad402e56e 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -135,7 +135,7 @@ static const char* BITCOIN_PID_FILENAME = "bitcoind.pid";
static fs::path GetPidFile(const ArgsManager& args)
{
- return AbsPathForConfigVal(fs::PathFromString(args.GetArg("-pid", BITCOIN_PID_FILENAME)));
+ return AbsPathForConfigVal(args.GetPathArg("-pid", BITCOIN_PID_FILENAME));
}
[[nodiscard]] static bool CreatePidFile(const ArgsManager& args)
@@ -462,7 +462,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2pacceptincoming", "If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-onlynet=<net>", "Make automatic outgoing connections only through network <net> (" + Join(GetNetworkNames(), ", ") + "). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-onlynet=<net>", "Make automatic outbound connections only to network <net> (" + Join(GetNetworkNames(), ", ") + "). Inbound and manual connections are not affected by this option. It can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -1229,10 +1229,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// Read asmap file if configured
std::vector<bool> asmap;
if (args.IsArgSet("-asmap")) {
- fs::path asmap_path = fs::PathFromString(args.GetArg("-asmap", ""));
- if (asmap_path.empty()) {
- asmap_path = fs::PathFromString(DEFAULT_ASMAP_FILENAME);
- }
+ fs::path asmap_path = args.GetPathArg("-asmap", DEFAULT_ASMAP_FILENAME);
if (!asmap_path.is_absolute()) {
asmap_path = gArgs.GetDataDirNet() / asmap_path;
}
diff --git a/src/init/common.cpp b/src/init/common.cpp
index 38c60366e3..688471b35d 100644
--- a/src/init/common.cpp
+++ b/src/init/common.cpp
@@ -81,7 +81,7 @@ void AddLoggingArgs(ArgsManager& argsman)
void SetLoggingOptions(const ArgsManager& args)
{
LogInstance().m_print_to_file = !args.IsArgNegated("-debuglogfile");
- LogInstance().m_file_path = AbsPathForConfigVal(fs::PathFromString(args.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)));
+ LogInstance().m_file_path = AbsPathForConfigVal(args.GetPathArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", !args.GetBoolArg("-daemon", false));
LogInstance().m_log_timestamps = args.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
LogInstance().m_log_time_micros = args.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc
index 9f5863a0f3..18626b327c 100644
--- a/src/leveldb/util/env_posix.cc
+++ b/src/leveldb/util/env_posix.cc
@@ -850,7 +850,7 @@ class SingletonEnv {
public:
SingletonEnv() {
#if !defined(NDEBUG)
- env_initialized_.store(true, std::memory_order::memory_order_relaxed);
+ env_initialized_.store(true, std::memory_order_relaxed);
#endif // !defined(NDEBUG)
static_assert(sizeof(env_storage_) >= sizeof(EnvType),
"env_storage_ will not fit the Env");
@@ -867,7 +867,7 @@ class SingletonEnv {
static void AssertEnvNotInitialized() {
#if !defined(NDEBUG)
- assert(!env_initialized_.load(std::memory_order::memory_order_relaxed));
+ assert(!env_initialized_.load(std::memory_order_relaxed));
#endif // !defined(NDEBUG)
}
diff --git a/src/leveldb/util/env_windows.cc b/src/leveldb/util/env_windows.cc
index 1834206562..4dcba222a1 100644
--- a/src/leveldb/util/env_windows.cc
+++ b/src/leveldb/util/env_windows.cc
@@ -798,7 +798,7 @@ class SingletonEnv {
public:
SingletonEnv() {
#if !defined(NDEBUG)
- env_initialized_.store(true, std::memory_order::memory_order_relaxed);
+ env_initialized_.store(true, std::memory_order_relaxed);
#endif // !defined(NDEBUG)
static_assert(sizeof(env_storage_) >= sizeof(EnvType),
"env_storage_ will not fit the Env");
@@ -815,7 +815,7 @@ class SingletonEnv {
static void AssertEnvNotInitialized() {
#if !defined(NDEBUG)
- assert(!env_initialized_.load(std::memory_order::memory_order_relaxed));
+ assert(!env_initialized_.load(std::memory_order_relaxed));
#endif // !defined(NDEBUG)
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index aee1e4c30c..59cd83e493 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -415,7 +415,7 @@ private:
void RelayAddress(NodeId originator, const CAddress& addr, bool fReachable);
/** Send `feefilter` message. */
- void MaybeSendFeefilter(CNode& node, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void MaybeSendFeefilter(CNode& node, std::chrono::microseconds current_time);
const CChainParams& m_chainparams;
CConnman& m_connman;
@@ -4506,8 +4506,6 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds current_time)
{
- AssertLockHeld(cs_main);
-
if (m_ignore_incoming_txs) return;
if (!pto.m_tx_relay) return;
if (pto.GetCommonVersion() < FEEFILTER_VERSION) return;
@@ -5054,8 +5052,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (!vGetData.empty())
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
-
- MaybeSendFeefilter(*pto, current_time);
} // release cs_main
+ MaybeSendFeefilter(*pto, current_time);
return true;
}
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 8a99130fd0..7392830261 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -32,35 +32,39 @@ static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq();
static FlatFileSeq UndoFileSeq();
-CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
+CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash)
+{
+ AssertLockHeld(cs_main);
+ BlockMap::iterator it = m_block_index.find(hash);
+ return it == m_block_index.end() ? nullptr : &it->second;
+}
+
+const CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
{
AssertLockHeld(cs_main);
BlockMap::const_iterator it = m_block_index.find(hash);
- return it == m_block_index.end() ? nullptr : it->second;
+ return it == m_block_index.end() ? nullptr : &it->second;
}
CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
{
AssertLockHeld(cs_main);
- // Check for duplicate
- uint256 hash = block.GetHash();
- BlockMap::iterator it = m_block_index.find(hash);
- if (it != m_block_index.end()) {
- return it->second;
+ auto [mi, inserted] = m_block_index.try_emplace(block.GetHash(), block);
+ if (!inserted) {
+ return &mi->second;
}
+ CBlockIndex* pindexNew = &(*mi).second;
- // Construct new block index object
- CBlockIndex* pindexNew = new CBlockIndex(block);
// We assign the sequence id to blocks only when the full data is available,
// to avoid miners withholding blocks but broadcasting headers, to get a
// competitive advantage.
pindexNew->nSequenceId = 0;
- BlockMap::iterator mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
+
pindexNew->phashBlock = &((*mi).first);
BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
if (miPrev != m_block_index.end()) {
- pindexNew->pprev = (*miPrev).second;
+ pindexNew->pprev = &(*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
pindexNew->BuildSkip();
}
@@ -80,8 +84,8 @@ void BlockManager::PruneOneBlockFile(const int fileNumber)
AssertLockHeld(cs_main);
LOCK(cs_LastBlockFile);
- for (const auto& entry : m_block_index) {
- CBlockIndex* pindex = entry.second;
+ for (auto& entry : m_block_index) {
+ CBlockIndex* pindex = &entry.second;
if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA;
pindex->nStatus &= ~BLOCK_HAVE_UNDO;
@@ -199,18 +203,13 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
return nullptr;
}
- // Return existing
- BlockMap::iterator mi = m_block_index.find(hash);
- if (mi != m_block_index.end()) {
- return (*mi).second;
+ // Return existing or create new
+ auto [mi, inserted] = m_block_index.try_emplace(hash);
+ CBlockIndex* pindex = &(*mi).second;
+ if (inserted) {
+ pindex->phashBlock = &((*mi).first);
}
-
- // Create new
- CBlockIndex* pindexNew = new CBlockIndex();
- mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
- pindexNew->phashBlock = &((*mi).first);
-
- return pindexNew;
+ return pindex;
}
bool BlockManager::LoadBlockIndex(
@@ -224,8 +223,8 @@ bool BlockManager::LoadBlockIndex(
// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
vSortedByHeight.reserve(m_block_index.size());
- for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
- CBlockIndex* pindex = item.second;
+ for (auto& [_, block_index] : m_block_index) {
+ CBlockIndex* pindex = &block_index;
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
}
sort(vSortedByHeight.begin(), vSortedByHeight.end());
@@ -327,10 +326,6 @@ void BlockManager::Unload()
{
m_blocks_unlinked.clear();
- for (const BlockMap::value_type& entry : m_block_index) {
- delete entry.second;
- }
-
m_block_index.clear();
m_blockfile_info.clear();
@@ -386,8 +381,8 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
- for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
- CBlockIndex* pindex = item.second;
+ for (const auto& [_, block_index] : m_block_index) {
+ const CBlockIndex* pindex = &block_index;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile);
}
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 42e46797d2..12224f7a5d 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_NODE_BLOCKSTORAGE_H
#define BITCOIN_NODE_BLOCKSTORAGE_H
+#include <chain.h>
#include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <sync.h>
@@ -20,7 +21,6 @@ class ArgsManager;
class BlockValidationState;
class CBlock;
class CBlockFileInfo;
-class CBlockIndex;
class CBlockUndo;
class CChain;
class CChainParams;
@@ -52,7 +52,11 @@ extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget;
-typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
+// Because validation code takes pointers to the map's CBlockIndex objects, if
+// we ever switch to another associative container, we need to either use a
+// container that has stable addressing (true of all std associative
+// containers), or make the key a `std::unique_ptr<CBlockIndex>`
+using BlockMap = std::unordered_map<uint256, CBlockIndex, BlockHasher>;
struct CBlockIndexWorkComparator {
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
@@ -144,7 +148,8 @@ public:
//! Mark one block file as pruned (modify associated database entries)
void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ const CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Get block file info entry for one block file */
CBlockFileInfo* GetBlockFileInfo(size_t n);
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index 7fe10ecabc..54afbb8839 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -97,7 +97,6 @@ void BlockAssembler::resetBlock()
// Reserve space for coinbase tx
nBlockWeight = 4000;
nBlockSigOpsCost = 400;
- fIncludeWitness = false;
// These counters do not include coinbase tx
nBlockTx = 0;
@@ -137,17 +136,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblock->nTime = GetAdjustedTime();
m_lock_time_cutoff = pindexPrev->GetMedianTimePast();
- // Decide whether to include witness transactions
- // This is only needed in case the witness softfork activation is reverted
- // (which would require a very deep reorganization).
- // Note that the mempool would accept transactions with witness data before
- // the deployment is active, but we would only ever mine blocks after activation
- // unless there is a massive block reorganization with the witness softfork
- // not activated.
- // TODO: replace this with a call to main to assess validity of a mempool
- // transaction (which in most cases can be a no-op).
- fIncludeWitness = DeploymentActiveAfter(pindexPrev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT);
-
int nPackagesSelected = 0;
int nDescendantsUpdated = 0;
addPackageTxs(nPackagesSelected, nDescendantsUpdated);
@@ -215,17 +203,12 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost
// Perform transaction-level checks before adding to block:
// - transaction finality (locktime)
-// - premature witness (in case segwit transactions are added to mempool before
-// segwit activation)
bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) const
{
for (CTxMemPool::txiter it : package) {
if (!IsFinalTx(it->GetTx(), nHeight, m_lock_time_cutoff)) {
return false;
}
- if (!fIncludeWitness && it->GetTx().HasWitness()) {
- return false;
- }
}
return true;
}
diff --git a/src/node/miner.h b/src/node/miner.h
index c96da874a7..97c55f2864 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -132,7 +132,6 @@ private:
std::unique_ptr<CBlockTemplate> pblocktemplate;
// Configuration parameters for the block size
- bool fIncludeWitness;
unsigned int nBlockMaxWeight;
CFeeRate blockMinFeeRate;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index d6f80e1558..c6b884e40a 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -587,7 +587,7 @@ int GuiMain(int argc, char* argv[])
return EXIT_FAILURE;
}
#ifdef ENABLE_WALLET
- // Parse URIs on command line -- this can affect Params()
+ // Parse URIs on command line
PaymentServer::ipcParseCommandLine(argc, argv);
#endif
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 9565fa508f..3108c93d7c 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -713,23 +713,18 @@ QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction
QString formatDurationStr(std::chrono::seconds dur)
{
- const auto secs = count_seconds(dur);
- QStringList strList;
- int days = secs / 86400;
- int hours = (secs % 86400) / 3600;
- int mins = (secs % 3600) / 60;
- int seconds = secs % 60;
-
- if (days)
- strList.append(QObject::tr("%1 d").arg(days));
- if (hours)
- strList.append(QObject::tr("%1 h").arg(hours));
- if (mins)
- strList.append(QObject::tr("%1 m").arg(mins));
- if (seconds || (!days && !hours && !mins))
- strList.append(QObject::tr("%1 s").arg(seconds));
-
- return strList.join(" ");
+ using days = std::chrono::duration<int, std::ratio<86400>>; // can remove this line after C++20
+ const auto d{std::chrono::duration_cast<days>(dur)};
+ const auto h{std::chrono::duration_cast<std::chrono::hours>(dur - d)};
+ const auto m{std::chrono::duration_cast<std::chrono::minutes>(dur - d - h)};
+ const auto s{std::chrono::duration_cast<std::chrono::seconds>(dur - d - h - m)};
+ QStringList str_list;
+ if (auto d2{d.count()}) str_list.append(QObject::tr("%1 d").arg(d2));
+ if (auto h2{h.count()}) str_list.append(QObject::tr("%1 h").arg(h2));
+ if (auto m2{m.count()}) str_list.append(QObject::tr("%1 m").arg(m2));
+ const auto s2{s.count()};
+ if (s2 || str_list.empty()) str_list.append(QObject::tr("%1 s").arg(s2));
+ return str_list.join(" ");
}
QString formatServicesStr(quint64 mask)
@@ -888,11 +883,7 @@ void PolishProgressDialog(QProgressDialog* dialog)
int TextWidth(const QFontMetrics& fm, const QString& text)
{
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
return fm.horizontalAdvance(text);
-#else
- return fm.width(text);
-#endif
}
void LogQtInfo()
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index cb09035b45..c82f0683fc 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -78,32 +78,11 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
for (int i = 1; i < argc; i++)
{
QString arg(argv[i]);
- if (arg.startsWith("-"))
- continue;
+ if (arg.startsWith("-")) continue;
- // If the bitcoin: URI contains a payment request, we are not able to detect the
- // network as that would require fetching and parsing the payment request.
- // That means clicking such an URI which contains a testnet payment request
- // will start a mainnet instance and throw a "wrong network" error.
if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
{
- if (savedPaymentRequests.contains(arg)) continue;
savedPaymentRequests.insert(arg);
-
- SendCoinsRecipient r;
- if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
- {
- auto tempChainParams = CreateChainParams(gArgs, CBaseChainParams::MAIN);
-
- if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- SelectParams(CBaseChainParams::MAIN);
- } else {
- tempChainParams = CreateChainParams(gArgs, CBaseChainParams::TESTNET);
- if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- SelectParams(CBaseChainParams::TESTNET);
- }
- }
- }
}
}
}
diff --git a/src/qt/test/optiontests.cpp b/src/qt/test/optiontests.cpp
new file mode 100644
index 0000000000..51894e1915
--- /dev/null
+++ b/src/qt/test/optiontests.cpp
@@ -0,0 +1,31 @@
+// Copyright (c) 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 <qt/bitcoin.h>
+#include <qt/test/optiontests.h>
+#include <test/util/setup_common.h>
+#include <util/system.h>
+
+#include <QSettings>
+#include <QTest>
+
+#include <univalue.h>
+
+//! Entry point for BitcoinApplication tests.
+void OptionTests::optionTests()
+{
+ // Test regression https://github.com/bitcoin/bitcoin/issues/24457. Ensure
+ // that setting integer prune value doesn't cause an exception to be thrown
+ // in the OptionsModel constructor
+ gArgs.LockSettings([&](util::Settings& settings) {
+ settings.forced_settings.erase("prune");
+ settings.rw_settings["prune"] = 3814;
+ });
+ gArgs.WriteSettingsFile();
+ OptionsModel{};
+ gArgs.LockSettings([&](util::Settings& settings) {
+ settings.rw_settings.erase("prune");
+ });
+ gArgs.WriteSettingsFile();
+}
diff --git a/src/qt/test/optiontests.h b/src/qt/test/optiontests.h
new file mode 100644
index 0000000000..779d4cc209
--- /dev/null
+++ b/src/qt/test/optiontests.h
@@ -0,0 +1,25 @@
+// Copyright (c) 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.
+
+#ifndef BITCOIN_QT_TEST_OPTIONTESTS_H
+#define BITCOIN_QT_TEST_OPTIONTESTS_H
+
+#include <qt/optionsmodel.h>
+
+#include <QObject>
+
+class OptionTests : public QObject
+{
+ Q_OBJECT
+public:
+ explicit OptionTests(interfaces::Node& node) : m_node(node) {}
+
+private Q_SLOTS:
+ void optionTests();
+
+private:
+ interfaces::Node& m_node;
+};
+
+#endif // BITCOIN_QT_TEST_OPTIONTESTS_H
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 10b7e2ffe7..07d256f05a 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -10,6 +10,7 @@
#include <interfaces/node.h>
#include <qt/bitcoin.h>
#include <qt/test/apptests.h>
+#include <qt/test/optiontests.h>
#include <qt/test/rpcnestedtests.h>
#include <qt/test/uritests.h>
#include <test/util/setup_common.h>
@@ -89,6 +90,10 @@ int main(int argc, char* argv[])
if (QTest::qExec(&app_tests) != 0) {
fInvalid = true;
}
+ OptionTests options_tests(app.node());
+ if (QTest::qExec(&options_tests) != 0) {
+ fInvalid = true;
+ }
URITests test1;
if (QTest::qExec(&test1) != 0) {
fInvalid = true;
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 9817c80cbd..86dfbbae35 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1753,10 +1753,10 @@ static RPCHelpMan getchaintips()
std::set<const CBlockIndex*> setOrphans;
std::set<const CBlockIndex*> setPrevs;
- for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) {
- if (!active_chain.Contains(item.second)) {
- setOrphans.insert(item.second);
- setPrevs.insert(item.second->pprev);
+ for (const auto& [_, block_index] : chainman.BlockIndex()) {
+ if (!active_chain.Contains(&block_index)) {
+ setOrphans.insert(&block_index);
+ setPrevs.insert(block_index.pprev);
}
}
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 798c4b3ea0..23540f6aef 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -802,6 +802,30 @@ public:
bool IsSingleType() const final { return true; }
};
+/** A parsed (sorted)multi_a(...) descriptor. Always uses x-only pubkeys. */
+class MultiADescriptor final : public DescriptorImpl
+{
+ const int m_threshold;
+ const bool m_sorted;
+protected:
+ std::string ToStringExtra() const override { return strprintf("%i", m_threshold); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override {
+ CScript ret;
+ std::vector<XOnlyPubKey> xkeys;
+ for (const auto& key : keys) xkeys.emplace_back(key);
+ if (m_sorted) std::sort(xkeys.begin(), xkeys.end());
+ ret << ToByteVector(xkeys[0]) << OP_CHECKSIG;
+ for (size_t i = 1; i < keys.size(); ++i) {
+ ret << ToByteVector(xkeys[i]) << OP_CHECKSIGADD;
+ }
+ ret << m_threshold << OP_NUMEQUAL;
+ return Vector(std::move(ret));
+ }
+public:
+ MultiADescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers, bool sorted = false) : DescriptorImpl(std::move(providers), sorted ? "sortedmulti_a" : "multi_a"), m_threshold(threshold), m_sorted(sorted) {}
+ bool IsSingleType() const final { return true; }
+};
+
/** A parsed sh(...) descriptor. */
class SHDescriptor final : public DescriptorImpl
{
@@ -1040,7 +1064,6 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
using namespace spanparsing;
auto expr = Expr(sp);
- bool sorted_multi = false;
if (Func("pk", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
if (!pubkey) return nullptr;
@@ -1065,7 +1088,12 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
error = "Can only have combo() at top level";
return nullptr;
}
- if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr))) {
+ const bool multi = Func("multi", expr);
+ const bool sortedmulti = !multi && Func("sortedmulti", expr);
+ const bool multi_a = !(multi || sortedmulti) && Func("multi_a", expr);
+ const bool sortedmulti_a = !(multi || sortedmulti || multi_a) && Func("sortedmulti_a", expr);
+ if (((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && (multi || sortedmulti)) ||
+ (ctx == ParseScriptContext::P2TR && (multi_a || sortedmulti_a))) {
auto threshold = Expr(expr);
uint32_t thres;
std::vector<std::unique_ptr<PubkeyProvider>> providers;
@@ -1086,9 +1114,12 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
providers.emplace_back(std::move(pk));
key_exp_index++;
}
- if (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTISIG) {
+ if ((multi || sortedmulti) && (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTISIG)) {
error = strprintf("Cannot have %u keys in multisig; must have between 1 and %d keys, inclusive", providers.size(), MAX_PUBKEYS_PER_MULTISIG);
return nullptr;
+ } else if ((multi_a || sortedmulti_a) && (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTI_A)) {
+ error = strprintf("Cannot have %u keys in multi_a; must have between 1 and %d keys, inclusive", providers.size(), MAX_PUBKEYS_PER_MULTI_A);
+ return nullptr;
} else if (thres < 1) {
error = strprintf("Multisig threshold cannot be %d, must be at least 1", thres);
return nullptr;
@@ -1109,10 +1140,17 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
return nullptr;
}
}
- return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sorted_multi);
- } else if (Func("sortedmulti", expr) || Func("multi", expr)) {
+ if (multi || sortedmulti) {
+ return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sortedmulti);
+ } else {
+ return std::make_unique<MultiADescriptor>(thres, std::move(providers), sortedmulti_a);
+ }
+ } else if (multi || sortedmulti) {
error = "Can only have multi/sortedmulti at top level, in sh(), or in wsh()";
return nullptr;
+ } else if (multi_a || sortedmulti_a) {
+ error = "Can only have multi_a/sortedmulti_a inside tr()";
+ return nullptr;
}
if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wpkh", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ParseScriptContext::P2WPKH, out, error);
@@ -1257,6 +1295,21 @@ std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseS
return key_provider;
}
+std::unique_ptr<DescriptorImpl> InferMultiA(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
+{
+ auto match = MatchMultiA(script);
+ if (!match) return {};
+ std::vector<std::unique_ptr<PubkeyProvider>> keys;
+ keys.reserve(match->second.size());
+ for (const auto keyspan : match->second) {
+ if (keyspan.size() != 32) return {};
+ auto key = InferXOnlyPubkey(XOnlyPubKey{keyspan}, ctx, provider);
+ if (!key) return {};
+ keys.push_back(std::move(key));
+ }
+ return std::make_unique<MultiADescriptor>(match->first, std::move(keys));
+}
+
std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
{
if (ctx == ParseScriptContext::P2TR && script.size() == 34 && script[0] == 32 && script[33] == OP_CHECKSIG) {
@@ -1264,6 +1317,11 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
return std::make_unique<PKDescriptor>(InferXOnlyPubkey(key, ctx, provider), true);
}
+ if (ctx == ParseScriptContext::P2TR) {
+ auto ret = InferMultiA(script, ctx, provider);
+ if (ret) return ret;
+ }
+
std::vector<std::vector<unsigned char>> data;
TxoutType txntype = Solver(script, data);
diff --git a/src/script/script.h b/src/script/script.h
index 8b7a7bb7b3..a89c987306 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -29,6 +29,9 @@ static const int MAX_OPS_PER_SCRIPT = 201;
// Maximum number of public keys per multisig
static const int MAX_PUBKEYS_PER_MULTISIG = 20;
+/** The limit of keys in OP_CHECKSIGADD-based scripts. It is due to the stack limit in BIP342. */
+static constexpr unsigned int MAX_PUBKEYS_PER_MULTI_A = 999;
+
// Maximum script length in bytes
static const int MAX_SCRIPT_SIZE = 10000;
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 371a937bc8..2e5c49e0b6 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -174,6 +174,29 @@ static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatu
result = Vector(std::move(sig));
return true;
}
+ return false;
+ }
+
+ // multi_a scripts (<key> OP_CHECKSIG <key> OP_CHECKSIGADD <key> OP_CHECKSIGADD <k> OP_NUMEQUAL)
+ if (auto match = MatchMultiA(script)) {
+ std::vector<std::vector<unsigned char>> sigs;
+ int good_sigs = 0;
+ for (size_t i = 0; i < match->second.size(); ++i) {
+ XOnlyPubKey pubkey{*(match->second.rbegin() + i)};
+ std::vector<unsigned char> sig;
+ bool good_sig = CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion);
+ if (good_sig && good_sigs < match->first) {
+ ++good_sigs;
+ sigs.push_back(std::move(sig));
+ } else {
+ sigs.emplace_back();
+ }
+ }
+ if (good_sigs == match->first) {
+ result = std::move(sigs);
+ return true;
+ }
+ return false;
}
return false;
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 7e32754d6c..b77c78769f 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -96,51 +96,83 @@ static constexpr bool IsPushdataOp(opcodetype opcode)
return opcode > OP_FALSE && opcode <= OP_PUSHDATA4;
}
-static constexpr bool IsValidMultisigKeyCount(int n_keys)
-{
- return n_keys > 0 && n_keys <= MAX_PUBKEYS_PER_MULTISIG;
-}
-
-static bool GetMultisigKeyCount(opcodetype opcode, valtype data, int& count)
+/** Retrieve a minimally-encoded number in range [min,max] from an (opcode, data) pair,
+ * whether it's OP_n or through a push. */
+static std::optional<int> GetScriptNumber(opcodetype opcode, valtype data, int min, int max)
{
+ int count;
if (IsSmallInteger(opcode)) {
count = CScript::DecodeOP_N(opcode);
- return IsValidMultisigKeyCount(count);
- }
-
- if (IsPushdataOp(opcode)) {
- if (!CheckMinimalPush(data, opcode)) return false;
+ } else if (IsPushdataOp(opcode)) {
+ if (!CheckMinimalPush(data, opcode)) return {};
try {
count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
- return IsValidMultisigKeyCount(count);
} catch (const scriptnum_error&) {
- return false;
+ return {};
}
+ } else {
+ return {};
}
-
- return false;
+ if (count < min || count > max) return {};
+ return count;
}
static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
{
opcodetype opcode;
valtype data;
- int num_keys;
CScript::const_iterator it = script.begin();
if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
- if (!script.GetOp(it, opcode, data) || !GetMultisigKeyCount(opcode, data, required_sigs)) return false;
+ if (!script.GetOp(it, opcode, data)) return false;
+ auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);
+ if (!req_sigs) return false;
+ required_sigs = *req_sigs;
while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
pubkeys.emplace_back(std::move(data));
}
- if (!GetMultisigKeyCount(opcode, data, num_keys)) return false;
-
- if (pubkeys.size() != static_cast<unsigned long>(num_keys) || num_keys < required_sigs) return false;
+ auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);
+ if (!num_keys) return false;
+ if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;
return (it + 1 == script.end());
}
+std::optional<std::pair<int, std::vector<Span<const unsigned char>>>> MatchMultiA(const CScript& script)
+{
+ std::vector<Span<const unsigned char>> keyspans;
+
+ // Redundant, but very fast and selective test.
+ if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {};
+
+ // Parse keys
+ auto it = script.begin();
+ while (script.end() - it >= 34) {
+ if (*it != 32) return {};
+ ++it;
+ keyspans.emplace_back(&*it, 32);
+ it += 32;
+ if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {};
+ ++it;
+ }
+ if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};
+
+ // Parse threshold.
+ opcodetype opcode;
+ std::vector<unsigned char> data;
+ if (!script.GetOp(it, opcode, data)) return {};
+ if (it == script.end()) return {};
+ if (*it != OP_NUMEQUAL) return {};
+ ++it;
+ if (it != script.end()) return {};
+ auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());
+ if (!threshold) return {};
+
+ // Construct result.
+ return std::pair{*threshold, std::move(keyspans)};
+}
+
TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
{
vSolutionsRet.clear();
diff --git a/src/script/standard.h b/src/script/standard.h
index eb50421768..75bfe2db38 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -191,6 +191,10 @@ CScript GetScriptForDestination(const CTxDestination& dest);
/** Generate a P2PK script for the given pubkey. */
CScript GetScriptForRawPubKey(const CPubKey& pubkey);
+/** Determine if script is a "multi_a" script. Returns (threshold, keyspans) if so, and nullopt otherwise.
+ * The keyspans refer to bytes in the passed script. */
+std::optional<std::pair<int, std::vector<Span<const unsigned char>>>> MatchMultiA(const CScript& script LIFETIMEBOUND);
+
/** Generate a multisig script. */
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index 92de4ec7ba..5b73481bc1 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -2,8 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <chainparams.h>
#include <index/coinstatsindex.h>
#include <test/util/setup_common.h>
+#include <test/util/validation.h>
#include <util/time.h>
#include <validation.h>
@@ -16,6 +18,17 @@ using node::CoinStatsHashType;
BOOST_AUTO_TEST_SUITE(coinstatsindex_tests)
+static void IndexWaitSynced(BaseIndex& index)
+{
+ // Allow the CoinStatsIndex to catch up with the block index that is syncing
+ // in a background thread.
+ const auto timeout = GetTime<std::chrono::seconds>() + 120s;
+ while (!index.BlockUntilSyncedToCurrentChain()) {
+ BOOST_REQUIRE(timeout > GetTime<std::chrono::milliseconds>());
+ UninterruptibleSleep(100ms);
+ }
+}
+
BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
{
CoinStatsIndex coin_stats_index{1 << 20, true};
@@ -36,13 +49,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
BOOST_REQUIRE(coin_stats_index.Start(m_node.chainman->ActiveChainstate()));
- // Allow the CoinStatsIndex to catch up with the block index that is syncing
- // in a background thread.
- const auto timeout = GetTime<std::chrono::seconds>() + 120s;
- while (!coin_stats_index.BlockUntilSyncedToCurrentChain()) {
- BOOST_REQUIRE(timeout > GetTime<std::chrono::milliseconds>());
- UninterruptibleSleep(100ms);
- }
+ IndexWaitSynced(coin_stats_index);
// Check that CoinStatsIndex works for genesis block.
const CBlockIndex* genesis_block_index;
@@ -78,4 +85,44 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
// Rest of shutdown sequence and destructors happen in ~TestingSetup()
}
+// Test shutdown between BlockConnected and ChainStateFlushed notifications,
+// make sure index is not corrupted and is able to reload.
+BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup)
+{
+ CChainState& chainstate = Assert(m_node.chainman)->ActiveChainstate();
+ const CChainParams& params = Params();
+ {
+ CoinStatsIndex index{1 << 20};
+ BOOST_REQUIRE(index.Start(chainstate));
+ IndexWaitSynced(index);
+ std::shared_ptr<const CBlock> new_block;
+ CBlockIndex* new_block_index = nullptr;
+ {
+ const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
+ const CBlock block = this->CreateBlock({}, script_pub_key, chainstate);
+
+ new_block = std::make_shared<CBlock>(block);
+
+ LOCK(cs_main);
+ BlockValidationState state;
+ BOOST_CHECK(CheckBlock(block, state, params.GetConsensus()));
+ BOOST_CHECK(chainstate.AcceptBlock(new_block, state, &new_block_index, true, nullptr, nullptr));
+ CCoinsViewCache view(&chainstate.CoinsTip());
+ BOOST_CHECK(chainstate.ConnectBlock(block, state, new_block_index, view));
+ }
+ // Send block connected notification, then stop the index without
+ // sending a chainstate flushed notification. Prior to #24138, this
+ // would cause the index to be corrupted and fail to reload.
+ ValidationInterfaceTest::BlockConnected(index, new_block, new_block_index);
+ index.Stop();
+ }
+
+ {
+ CoinStatsIndex index{1 << 20};
+ // Make sure the index can be loaded.
+ BOOST_REQUIRE(index.Start(chainstate));
+ index.Stop();
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index 5875f0218f..d2554483d4 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -46,8 +46,8 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream)
{
fs::path tmpfolder = m_args.GetDataDirBase();
// tmpfile1 should be the same as tmpfile2
- fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃";
- fs::path tmpfile2 = tmpfolder / "fs_tests_₿_🏃";
+ fs::path tmpfile1 = tmpfolder / fs::u8path("fs_tests_₿_🏃");
+ fs::path tmpfile2 = tmpfolder / fs::u8path("fs_tests_₿_🏃");
{
std::ofstream file{tmpfile1};
file << "bitcoin";
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream)
}
{
// Join an absolute path and a relative path.
- fs::path p = fsbridge::AbsPathJoin(tmpfolder, "fs_tests_₿_🏃");
+ fs::path p = fsbridge::AbsPathJoin(tmpfolder, fs::u8path("fs_tests_₿_🏃"));
BOOST_CHECK(p.is_absolute());
BOOST_CHECK_EQUAL(tmpfile1, p);
}
diff --git a/src/test/fuzz/script_format.cpp b/src/test/fuzz/script_format.cpp
index 2fa893f812..241bdfe666 100644
--- a/src/test/fuzz/script_format.cpp
+++ b/src/test/fuzz/script_format.cpp
@@ -3,7 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
+#include <consensus/consensus.h>
#include <core_io.h>
+#include <policy/policy.h>
#include <script/script.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
@@ -19,6 +21,9 @@ FUZZ_TARGET_INIT(script_format, initialize_script_format)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const CScript script{ConsumeScript(fuzzed_data_provider)};
+ if (script.size() > MAX_STANDARD_TX_WEIGHT / WITNESS_SCALE_FACTOR) {
+ return;
+ }
(void)FormatScript(script);
(void)ScriptToAsmStr(script, /*fAttemptSighashDecode=*/fuzzed_data_provider.ConsumeBool());
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index ef9d72dcde..c877105fe7 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -3,6 +3,8 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/util/setup_common.h>
+#include <univalue.h>
+#include <util/settings.h>
#include <util/strencodings.h>
#include <util/system.h>
@@ -41,6 +43,116 @@ void SetupArgs(ArgsManager& local_args, const std::vector<std::pair<std::string,
}
}
+// Test behavior of GetArg functions when string, integer, and boolean types
+// are specified in the settings.json file. GetArg functions are convenience
+// functions. The GetSetting method can always be used instead of GetArg
+// methods to retrieve original values, and there's not always an objective
+// answer to what GetArg behavior is best in every case. This test makes sure
+// there's test coverage for whatever the current behavior is, so it's not
+// broken or changed unintentionally.
+BOOST_AUTO_TEST_CASE(setting_args)
+{
+ ArgsManager args;
+ SetupArgs(args, {{"-foo", ArgsManager::ALLOW_ANY}});
+
+ auto set_foo = [&](const util::SettingsValue& value) {
+ args.LockSettings([&](util::Settings& settings) {
+ settings.rw_settings["foo"] = value;
+ });
+ };
+
+ set_foo("str");
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "\"str\"");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "str");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 0);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), false);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), false);
+
+ set_foo("99");
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "\"99\"");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "99");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 99);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), true);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), true);
+
+ set_foo("3.25");
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "\"3.25\"");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "3.25");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 3);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), true);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), true);
+
+ set_foo("0");
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "\"0\"");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "0");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 0);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), false);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), false);
+
+ set_foo("");
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "\"\"");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 0);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), true);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), true);
+
+ set_foo(99);
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "99");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "99");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 99);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", true), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", false), std::runtime_error);
+
+ set_foo(3.25);
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "3.25");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "3.25");
+ BOOST_CHECK_THROW(args.GetIntArg("foo", 100), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", true), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", false), std::runtime_error);
+
+ set_foo(0);
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "0");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "0");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 0);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", true), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", false), std::runtime_error);
+
+ set_foo(true);
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "true");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "1");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 1);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), true);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), true);
+
+ set_foo(false);
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "false");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "0");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 0);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), false);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), false);
+
+ set_foo(UniValue::VOBJ);
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "{}");
+ BOOST_CHECK_THROW(args.GetArg("foo", "default"), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetIntArg("foo", 100), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", true), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", false), std::runtime_error);
+
+ set_foo(UniValue::VARR);
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "[]");
+ BOOST_CHECK_THROW(args.GetArg("foo", "default"), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetIntArg("foo", 100), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", true), std::runtime_error);
+ BOOST_CHECK_THROW(args.GetBoolArg("foo", false), std::runtime_error);
+
+ set_foo(UniValue::VNULL);
+ BOOST_CHECK_EQUAL(args.GetSetting("foo").write(), "null");
+ BOOST_CHECK_EQUAL(args.GetArg("foo", "default"), "default");
+ BOOST_CHECK_EQUAL(args.GetIntArg("foo", 100), 100);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", true), true);
+ BOOST_CHECK_EQUAL(args.GetBoolArg("foo", false), false);
+}
+
BOOST_AUTO_TEST_CASE(boolarg)
{
ArgsManager local_args;
@@ -245,6 +357,24 @@ BOOST_AUTO_TEST_CASE(patharg)
ResetArgs(local_args, "-dir=user/.bitcoin/.//");
BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir"), relative_path);
+
+ // Check negated and default argument handling. Specifying an empty argument
+ // is the same as not specifying the argument. This is convenient for
+ // scripting so later command line arguments can override earlier command
+ // line arguments or bitcoin.conf values. Currently the -dir= case cannot be
+ // distinguished from -dir case with no assignment, but #16545 would add the
+ // ability to distinguish these in the future (and treat the no-assign case
+ // like an imperative command or an error).
+ ResetArgs(local_args, "");
+ BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{"default"});
+ ResetArgs(local_args, "-dir=override");
+ BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{"override"});
+ ResetArgs(local_args, "-dir=");
+ BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{"default"});
+ ResetArgs(local_args, "-dir");
+ BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{"default"});
+ ResetArgs(local_args, "-nodir");
+ BOOST_CHECK_EQUAL(local_args.GetPathArg("-dir", "default"), fs::path{""});
}
BOOST_AUTO_TEST_CASE(doubledash)
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index c968e4d124..211153f06c 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -180,10 +180,9 @@ ChainTestingSetup::~ChainTestingSetup()
m_node.banman.reset();
m_node.addrman.reset();
m_node.args = nullptr;
- UnloadBlockIndex(m_node.mempool.get(), *m_node.chainman);
+ WITH_LOCK(::cs_main, UnloadBlockIndex(m_node.mempool.get(), *m_node.chainman));
m_node.mempool.reset();
m_node.scheduler.reset();
- m_node.chainman->Reset();
m_node.chainman.reset();
}
diff --git a/src/test/util/validation.cpp b/src/test/util/validation.cpp
index 1aed492c3c..49535855f9 100644
--- a/src/test/util/validation.cpp
+++ b/src/test/util/validation.cpp
@@ -7,6 +7,7 @@
#include <util/check.h>
#include <util/time.h>
#include <validation.h>
+#include <validationinterface.h>
void TestChainState::ResetIbd()
{
@@ -20,3 +21,8 @@ void TestChainState::JumpOutOfIbd()
m_cached_finished_ibd = true;
Assert(!IsInitialBlockDownload());
}
+
+void ValidationInterfaceTest::BlockConnected(CValidationInterface& obj, const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex)
+{
+ obj.BlockConnected(block, pindex);
+}
diff --git a/src/test/util/validation.h b/src/test/util/validation.h
index b13aa0be60..b0bc717b6c 100644
--- a/src/test/util/validation.h
+++ b/src/test/util/validation.h
@@ -7,6 +7,8 @@
#include <validation.h>
+class CValidationInterface;
+
struct TestChainState : public CChainState {
/** Reset the ibd cache to its initial state */
void ResetIbd();
@@ -14,4 +16,10 @@ struct TestChainState : public CChainState {
void JumpOutOfIbd();
};
+class ValidationInterfaceTest
+{
+public:
+ static void BlockConnected(CValidationInterface& obj, const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex);
+};
+
#endif // BITCOIN_TEST_UTIL_VALIDATION_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 9f78215de2..1881573e7a 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -17,6 +17,7 @@
#include <util/message.h> // For MessageSign(), MessageVerify(), MESSAGE_MAGIC
#include <util/moneystr.h>
#include <util/overflow.h>
+#include <util/readwritefile.h>
#include <util/spanparsing.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -24,9 +25,10 @@
#include <util/vector.h>
#include <array>
-#include <optional>
+#include <fstream>
#include <limits>
#include <map>
+#include <optional>
#include <stdint.h>
#include <string.h>
#include <thread>
@@ -2592,4 +2594,49 @@ BOOST_AUTO_TEST_CASE(util_ParseByteUnits)
BOOST_CHECK(!ParseByteUnits("1x", noop));
}
+BOOST_AUTO_TEST_CASE(util_ReadBinaryFile)
+{
+ fs::path tmpfolder = m_args.GetDataDirBase();
+ fs::path tmpfile = tmpfolder / "read_binary.dat";
+ std::string expected_text;
+ for (int i = 0; i < 30; i++) {
+ expected_text += "0123456789";
+ }
+ {
+ std::ofstream file{tmpfile};
+ file << expected_text;
+ }
+ {
+ // read all contents in file
+ auto [valid, text] = ReadBinaryFile(tmpfile);
+ BOOST_CHECK(valid);
+ BOOST_CHECK_EQUAL(text, expected_text);
+ }
+ {
+ // read half contents in file
+ auto [valid, text] = ReadBinaryFile(tmpfile, expected_text.size() / 2);
+ BOOST_CHECK(valid);
+ BOOST_CHECK_EQUAL(text, expected_text.substr(0, expected_text.size() / 2));
+ }
+ {
+ // read from non-existent file
+ fs::path invalid_file = tmpfolder / "invalid_binary.dat";
+ auto [valid, text] = ReadBinaryFile(invalid_file);
+ BOOST_CHECK(!valid);
+ BOOST_CHECK(text.empty());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(util_WriteBinaryFile)
+{
+ fs::path tmpfolder = m_args.GetDataDirBase();
+ fs::path tmpfile = tmpfolder / "write_binary.dat";
+ std::string expected_text = "bitcoin";
+ auto valid = WriteBinaryFile(tmpfile, expected_text);
+ std::string actual_text;
+ std::ifstream file{tmpfile};
+ file >> actual_text;
+ BOOST_CHECK(valid);
+ BOOST_CHECK_EQUAL(actual_text, expected_text);
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 1beef5cf04..b0d7389d39 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -72,9 +72,6 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
// The view cache should be empty since we had to destruct to downsize.
BOOST_CHECK(!c1.CoinsTip().HaveCoinInCache(outpoint));
}
-
- // Avoid triggering the address sanitizer.
- WITH_LOCK(::cs_main, manager.Unload());
}
//! Test UpdateTip behavior for both active and background chainstates.
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 26392e690d..5d0ec593e3 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -99,8 +99,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
// Let scheduler events finish running to avoid accessing memory that is going to be unloaded
SyncWithValidationInterfaceQueue();
-
- WITH_LOCK(::cs_main, manager.Unload());
}
//! Test rebalancing the caches associated with each chainstate.
diff --git a/src/util/readwritefile.cpp b/src/util/readwritefile.cpp
index a45c41d367..628e6a3980 100644
--- a/src/util/readwritefile.cpp
+++ b/src/util/readwritefile.cpp
@@ -18,7 +18,7 @@ std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxs
std::string retval;
char buffer[128];
do {
- const size_t n = fread(buffer, 1, sizeof(buffer), f);
+ const size_t n = fread(buffer, 1, std::min(sizeof(buffer), maxsize - retval.size()), f);
// Check for reading errors so we don't return any data if we couldn't
// read the entire file (or up to maxsize)
if (ferror(f)) {
@@ -26,7 +26,7 @@ std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxs
return std::make_pair(false,"");
}
retval.append(buffer, buffer+n);
- } while (!feof(f) && retval.size() <= maxsize);
+ } while (!feof(f) && retval.size() < maxsize);
fclose(f);
return std::make_pair(true,retval);
}
diff --git a/src/util/system.cpp b/src/util/system.cpp
index aa9122106b..8e45453d31 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -387,9 +387,12 @@ std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) co
return std::nullopt;
}
-fs::path ArgsManager::GetPathArg(std::string pathlike_arg) const
+fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const
{
- auto result = fs::PathFromString(GetArg(pathlike_arg, "")).lexically_normal();
+ if (IsArgNegated(arg)) return fs::path{};
+ std::string path_str = GetArg(arg, "");
+ if (path_str.empty()) return default_value;
+ fs::path result = fs::PathFromString(path_str).lexically_normal();
// Remove trailing slash, if present.
return result.has_filename() ? result : result.parent_path();
}
@@ -516,12 +519,12 @@ bool ArgsManager::InitSettings(std::string& error)
bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp) const
{
- if (IsArgNegated("-settings")) {
+ fs::path settings = GetPathArg("-settings", fs::path{BITCOIN_SETTINGS_FILENAME});
+ if (settings.empty()) {
return false;
}
if (filepath) {
- std::string settings = GetArg("-settings", BITCOIN_SETTINGS_FILENAME);
- *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), fs::PathFromString(temp ? settings + ".tmp" : settings));
+ *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
}
return true;
}
@@ -588,7 +591,7 @@ bool ArgsManager::IsArgNegated(const std::string& strArg) const
std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
{
const util::SettingsValue value = GetSetting(strArg);
- return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str();
+ return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.isNum() ? value.getValStr() : value.get_str();
}
int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
diff --git a/src/util/system.h b/src/util/system.h
index f193c8ac0b..a66b597d41 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -271,16 +271,6 @@ protected:
std::optional<const Command> GetCommand() const;
/**
- * Get a normalized path from a specified pathlike argument
- *
- * It is guaranteed that the returned path has no trailing slashes.
- *
- * @param pathlike_arg Pathlike argument to get a path from (e.g., "-datadir", "-blocksdir" or "-walletdir")
- * @return Normalized path which is get from a specified pathlike argument
- */
- fs::path GetPathArg(std::string pathlike_arg) const;
-
- /**
* Get blocks directory path
*
* @return Blocks path which is network specific
@@ -343,6 +333,18 @@ protected:
std::string GetArg(const std::string& strArg, const std::string& strDefault) const;
/**
+ * Return path argument or default value
+ *
+ * @param arg Argument to get a path from (e.g., "-datadir", "-blocksdir" or "-walletdir")
+ * @param default_value Optional default value to return instead of the empty path.
+ * @return normalized path if argument is set, with redundant "." and ".."
+ * path components and trailing separators removed (see patharg unit test
+ * for examples or implementation for details). If argument is empty or not
+ * set, default_value is returned unchanged.
+ */
+ fs::path GetPathArg(std::string arg, const fs::path& default_value = {}) const;
+
+ /**
* Return integer argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
diff --git a/src/validation.cpp b/src/validation.cpp
index 214112e2bd..a7360e4b64 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -528,9 +528,26 @@ public:
/* m_package_submission */ true,
};
}
- // No default ctor to avoid exposing details to clients and allowing the possibility of
+
+ private:
+ // Private ctor to avoid exposing details to clients and allowing the possibility of
// mixing up the order of the arguments. Use static functions above instead.
- ATMPArgs() = delete;
+ ATMPArgs(const CChainParams& chainparams,
+ int64_t accept_time,
+ bool bypass_limits,
+ std::vector<COutPoint>& coins_to_uncache,
+ bool test_accept,
+ bool allow_bip125_replacement,
+ bool package_submission)
+ : m_chainparams{chainparams},
+ m_accept_time{accept_time},
+ m_bypass_limits{bypass_limits},
+ m_coins_to_uncache{coins_to_uncache},
+ m_test_accept{test_accept},
+ m_allow_bip125_replacement{allow_bip125_replacement},
+ m_package_submission{package_submission}
+ {
+ }
};
// Single transaction acceptance
@@ -1978,7 +1995,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// effectively caching the result of part of the verification.
BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid);
if (it != m_blockman.m_block_index.end()) {
- if (it->second->GetAncestor(pindex->nHeight) == pindex &&
+ if (it->second.GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->nChainWork >= nMinimumChainWork) {
// This block is a member of the assumed verified chain and an ancestor of the best header.
@@ -3035,8 +3052,8 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
{
LOCK(cs_main);
- for (const auto& entry : m_blockman.m_block_index) {
- CBlockIndex *candidate = entry.second;
+ for (auto& entry : m_blockman.m_block_index) {
+ CBlockIndex* candidate = &entry.second;
// We don't need to put anything in our active chain into the
// multimap, because those candidates will be found and considered
// as we disconnect.
@@ -3133,12 +3150,10 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
// it up here, this should be an essentially unobservable error.
// Loop back over all block index entries and add any missing entries
// to setBlockIndexCandidates.
- BlockMap::iterator it = m_blockman.m_block_index.begin();
- while (it != m_blockman.m_block_index.end()) {
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, m_chain.Tip())) {
- setBlockIndexCandidates.insert(it->second);
+ for (auto& [_, block_index] : m_blockman.m_block_index) {
+ if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(&block_index, m_chain.Tip())) {
+ setBlockIndexCandidates.insert(&block_index);
}
- it++;
}
InvalidChainFound(to_mark_failed);
@@ -3157,21 +3172,19 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
int nHeight = pindex->nHeight;
// Remove the invalidity flag from this block and all its descendants.
- BlockMap::iterator it = m_blockman.m_block_index.begin();
- while (it != m_blockman.m_block_index.end()) {
- if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
- it->second->nStatus &= ~BLOCK_FAILED_MASK;
- m_blockman.m_dirty_blockindex.insert(it->second);
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), it->second)) {
- setBlockIndexCandidates.insert(it->second);
+ for (auto& [_, block_index] : m_blockman.m_block_index) {
+ if (!block_index.IsValid() && block_index.GetAncestor(nHeight) == pindex) {
+ block_index.nStatus &= ~BLOCK_FAILED_MASK;
+ m_blockman.m_dirty_blockindex.insert(&block_index);
+ if (block_index.IsValid(BLOCK_VALID_TRANSACTIONS) && block_index.HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), &block_index)) {
+ setBlockIndexCandidates.insert(&block_index);
}
- if (it->second == m_chainman.m_best_invalid) {
+ if (&block_index == m_chainman.m_best_invalid) {
// Reset invalid block marker if it was pointing to one of those.
m_chainman.m_best_invalid = nullptr;
}
- m_chainman.m_failed_blocks.erase(it->second);
+ m_chainman.m_failed_blocks.erase(&block_index);
}
- it++;
}
// Remove the invalidity flag from all ancestors too.
@@ -3500,7 +3513,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
if (hash != chainparams.GetConsensus().hashGenesisBlock) {
if (miSelf != m_blockman.m_block_index.end()) {
// Block header is already known.
- CBlockIndex* pindex = miSelf->second;
+ CBlockIndex* pindex = &(miSelf->second);
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK) {
@@ -3522,7 +3535,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
LogPrint(BCLog::VALIDATION, "%s: %s prev block not found\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found");
}
- pindexPrev = (*mi).second;
+ pindexPrev = &((*mi).second);
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) {
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
@@ -3994,13 +4007,13 @@ bool CChainState::ReplayBlocks()
if (m_blockman.m_block_index.count(hashHeads[0]) == 0) {
return error("ReplayBlocks(): reorganization to unknown block requested");
}
- pindexNew = m_blockman.m_block_index[hashHeads[0]];
+ pindexNew = &(m_blockman.m_block_index[hashHeads[0]]);
if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush.
if (m_blockman.m_block_index.count(hashHeads[1]) == 0) {
return error("ReplayBlocks(): reorganization from unknown block requested");
}
- pindexOld = m_blockman.m_block_index[hashHeads[1]];
+ pindexOld = &(m_blockman.m_block_index[hashHeads[1]]);
pindexFork = LastCommonAncestor(pindexOld, pindexNew);
assert(pindexFork != nullptr);
}
@@ -4070,7 +4083,7 @@ void CChainState::UnloadBlockIndex()
// block index state
void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
{
- LOCK(cs_main);
+ AssertLockHeld(::cs_main);
chainman.Unload();
pindexBestHeader = nullptr;
if (mempool) mempool->clear();
@@ -4267,8 +4280,8 @@ void CChainState::CheckBlockIndex()
// Build forward-pointing map of the entire block tree.
std::multimap<CBlockIndex*,CBlockIndex*> forward;
- for (const std::pair<const uint256, CBlockIndex*>& entry : m_blockman.m_block_index) {
- forward.insert(std::make_pair(entry.second->pprev, entry.second));
+ for (auto& [_, block_index] : m_blockman.m_block_index) {
+ forward.emplace(block_index.pprev, &block_index);
}
assert(forward.size() == m_blockman.m_block_index.size());
@@ -5056,15 +5069,6 @@ void ChainstateManager::Unload()
m_best_invalid = nullptr;
}
-void ChainstateManager::Reset()
-{
- LOCK(::cs_main);
- m_ibd_chainstate.reset();
- m_snapshot_chainstate.reset();
- m_active_chainstate = nullptr;
- m_snapshot_validated = false;
-}
-
void ChainstateManager::MaybeRebalanceCaches()
{
AssertLockHeld(::cs_main);
diff --git a/src/validation.h b/src/validation.h
index 7766d77a88..cc2247239f 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -138,7 +138,7 @@ extern CBlockIndex *pindexBestHeader;
extern const std::vector<std::string> CHECKLEVEL_DOC;
/** Unload database information */
-void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
+void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
/** Run instances of script checking worker threads */
void StartScriptCheckWorkerThreads(int threads_num);
/** Stop all of the script checking worker threads */
@@ -991,17 +991,13 @@ public:
//! Unload block index and chain data before shutdown.
void Unload() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
- //! Clear (deconstruct) chainstate data.
- void Reset();
-
//! Check to see if caches are out of balance and if so, call
//! ResizeCoinsCaches() as needed.
void MaybeRebalanceCaches() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
~ChainstateManager() {
LOCK(::cs_main);
- UnloadBlockIndex(/* mempool */ nullptr, *this);
- Reset();
+ UnloadBlockIndex(/*mempool=*/nullptr, *this);
}
};
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 7c3ce00fbc..ac62f8b467 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -174,6 +174,7 @@ protected:
* has been received and connected to the headers tree, though not validated yet */
virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {};
friend class CMainSignals;
+ friend class ValidationInterfaceTest;
};
struct MainSignalsInstance;
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 23faad027f..513572da45 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -163,6 +163,8 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
result.AddInput(utxo_pool.at(i));
}
}
+ result.ComputeAndSetWaste(CAmount{0});
+ assert(best_waste == result.GetWaste());
return result;
}
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index 433b5a1815..072879a42a 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -659,7 +659,7 @@ RPCHelpMan fundrawtransaction()
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
"If that happens, you will need to fund the transaction with different inputs and republish it."},
- {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
+ {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
{"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
@@ -1056,7 +1056,7 @@ RPCHelpMan send()
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
"If that happens, you will need to fund the transaction with different inputs and republish it."},
{"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns a serialized transaction which will not be added to the wallet or broadcast"},
- {"change_address", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
+ {"change_address", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
{"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
@@ -1351,7 +1351,7 @@ RPCHelpMan walletcreatefundedpsbt()
{"include_unsafe", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include inputs that are not safe to spend (unconfirmed transactions from outside keys and unconfirmed replacement transactions).\n"
"Warning: the resulting transaction may become invalid if one of the unsafe inputs disappears.\n"
"If that happens, you will need to fund the transaction with different inputs and republish it."},
- {"changeAddress", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"pool address"}, "The bitcoin address to receive the change"},
+ {"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
{"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only"},
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index eef2c13ee1..ad94ce4b32 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -34,6 +34,7 @@ static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue
}
uint256 hash = wtx.GetHash();
entry.pushKV("txid", hash.GetHex());
+ entry.pushKV("wtxid", wtx.GetWitnessHash().GetHex());
UniValue conflicts(UniValue::VARR);
for (const uint256& conflict : wallet.GetTxConflicts(wtx))
conflicts.push_back(conflict.GetHex());
@@ -431,6 +432,7 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{RPCResult::Type::NUM, "blockindex", /*optional=*/true, "The index of the transaction in the block that includes it."},
{RPCResult::Type::NUM_TIME, "blocktime", /*optional=*/true, "The block time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
+ {RPCResult::Type::STR_HEX, "wtxid", "The hash of serialized transaction, including witness data."},
{RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 83eaececc1..582464c5b8 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -379,7 +379,6 @@ std::optional<SelectionResult> AttemptSelection(const CWallet& wallet, const CAm
// Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
std::vector<OutputGroup> positive_groups = GroupOutputs(wallet, coins, coin_selection_params, eligibility_filter, true /* positive_only */);
if (auto bnb_result{SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change)}) {
- bnb_result->ComputeAndSetWaste(CAmount(0));
results.push_back(*bnb_result);
}
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 2b2181e70b..55949e6e7a 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -37,6 +37,22 @@ static void ErrorLogCallback(void* arg, int code, const char* msg)
LogPrintf("SQLite Error. Code: %d. Message: %s\n", code, msg);
}
+static bool BindBlobToStatement(sqlite3_stmt* stmt,
+ int index,
+ Span<const std::byte> blob,
+ const std::string& description)
+{
+ int res = sqlite3_bind_blob(stmt, index, blob.data(), blob.size(), SQLITE_STATIC);
+ if (res != SQLITE_OK) {
+ LogPrintf("Unable to bind %s to statement: %s\n", description, sqlite3_errstr(res));
+ sqlite3_clear_bindings(stmt);
+ sqlite3_reset(stmt);
+ return false;
+ }
+
+ return true;
+}
+
static std::optional<int> ReadPragmaInteger(sqlite3* db, const std::string& key, const std::string& description, bilingual_str& error)
{
std::string stmt_text = strprintf("PRAGMA %s", key);
@@ -377,14 +393,8 @@ bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value)
assert(m_read_stmt);
// Bind: leftmost parameter in statement is index 1
- int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
- if (res != SQLITE_OK) {
- LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res));
- sqlite3_clear_bindings(m_read_stmt);
- sqlite3_reset(m_read_stmt);
- return false;
- }
- res = sqlite3_step(m_read_stmt);
+ if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false;
+ int res = sqlite3_step(m_read_stmt);
if (res != SQLITE_ROW) {
if (res != SQLITE_DONE) {
// SQLITE_DONE means "not found", don't log an error in that case.
@@ -418,23 +428,11 @@ bool SQLiteBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrit
// Bind: leftmost parameter in statement is index 1
// Insert index 1 is key, 2 is value
- int res = sqlite3_bind_blob(stmt, 1, key.data(), key.size(), SQLITE_STATIC);
- if (res != SQLITE_OK) {
- LogPrintf("%s: Unable to bind key to statement: %s\n", __func__, sqlite3_errstr(res));
- sqlite3_clear_bindings(stmt);
- sqlite3_reset(stmt);
- return false;
- }
- res = sqlite3_bind_blob(stmt, 2, value.data(), value.size(), SQLITE_STATIC);
- if (res != SQLITE_OK) {
- LogPrintf("%s: Unable to bind value to statement: %s\n", __func__, sqlite3_errstr(res));
- sqlite3_clear_bindings(stmt);
- sqlite3_reset(stmt);
- return false;
- }
+ if (!BindBlobToStatement(stmt, 1, key, "key")) return false;
+ if (!BindBlobToStatement(stmt, 2, value, "value")) return false;
// Execute
- res = sqlite3_step(stmt);
+ int res = sqlite3_step(stmt);
sqlite3_clear_bindings(stmt);
sqlite3_reset(stmt);
if (res != SQLITE_DONE) {
@@ -449,16 +447,10 @@ bool SQLiteBatch::EraseKey(CDataStream&& key)
assert(m_delete_stmt);
// Bind: leftmost parameter in statement is index 1
- int res = sqlite3_bind_blob(m_delete_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
- if (res != SQLITE_OK) {
- LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res));
- sqlite3_clear_bindings(m_delete_stmt);
- sqlite3_reset(m_delete_stmt);
- return false;
- }
+ if (!BindBlobToStatement(m_delete_stmt, 1, key, "key")) return false;
// Execute
- res = sqlite3_step(m_delete_stmt);
+ int res = sqlite3_step(m_delete_stmt);
sqlite3_clear_bindings(m_delete_stmt);
sqlite3_reset(m_delete_stmt);
if (res != SQLITE_DONE) {
@@ -473,18 +465,11 @@ bool SQLiteBatch::HasKey(CDataStream&& key)
assert(m_read_stmt);
// Bind: leftmost parameter in statement is index 1
- bool ret = false;
- int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
- if (res == SQLITE_OK) {
- res = sqlite3_step(m_read_stmt);
- if (res == SQLITE_ROW) {
- ret = true;
- }
- }
-
+ if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false;
+ int res = sqlite3_step(m_read_stmt);
sqlite3_clear_bindings(m_read_stmt);
sqlite3_reset(m_read_stmt);
- return ret;
+ return res == SQLITE_ROW;
}
bool SQLiteBatch::StartCursor()
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 7693c9c0e8..c59f7e6f05 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -367,10 +367,10 @@ static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lock
CBlockIndex* block = nullptr;
if (blockTime > 0) {
LOCK(cs_main);
- auto inserted = chainman.BlockIndex().emplace(GetRandHash(), new CBlockIndex);
+ auto inserted = chainman.BlockIndex().emplace(std::piecewise_construct, std::make_tuple(GetRandHash()), std::make_tuple());
assert(inserted.second);
const uint256& hash = inserted.first->first;
- block = inserted.first->second;
+ block = &inserted.first->second;
block->nTime = blockTime;
block->phashBlock = &hash;
state = TxStateConfirmed{hash, block->nHeight, /*position_in_block=*/0};
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 00f9c9f154..271d698e56 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -296,6 +296,7 @@ public:
bool isUnconfirmed() const { return !isAbandoned() && !isConflicted() && !isConfirmed(); }
bool isConfirmed() const { return state<TxStateConfirmed>(); }
const uint256& GetHash() const { return tx->GetHash(); }
+ const uint256& GetWitnessHash() const { return tx->GetWitnessHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
// Disable copying of CWalletTx objects to prevent bugs where instances get