aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/chainparams.cpp5
-rw-r--r--src/net.h3
-rw-r--r--src/net_processing.cpp125
-rw-r--r--src/qt/bitcoin.cpp5
-rw-r--r--src/qt/bitcoingui.cpp9
-rw-r--r--src/qt/qrimagewidget.cpp31
-rw-r--r--src/qt/qrimagewidget.h4
-rw-r--r--src/qt/sendcoinsdialog.cpp10
-rw-r--r--src/qt/sendcoinsdialog.h2
-rw-r--r--src/randomenv.cpp5
-rw-r--r--src/script/sigcache.cpp4
-rw-r--r--src/test/fuzz/deserialize.cpp14
-rw-r--r--src/test/script_tests.cpp60
-rw-r--r--src/txrequest.cpp100
-rw-r--r--src/util/system.cpp8
-rw-r--r--src/wallet/bdb.h1
-rw-r--r--src/wallet/db.h3
-rw-r--r--src/wallet/rpcwallet.cpp2
-rw-r--r--src/wallet/scriptpubkeyman.cpp2
-rw-r--r--src/wallet/scriptpubkeyman.h2
-rw-r--r--src/wallet/sqlite.cpp4
-rw-r--r--src/wallet/sqlite.h1
-rw-r--r--src/wallet/wallet.h2
-rw-r--r--src/wallet/wallettool.cpp3
24 files changed, 244 insertions, 161 deletions
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 7998357bc7..be2f5b53a6 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -330,6 +330,11 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
+ // Activation of Taproot (BIPs 340-342)
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
+ consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
+
// message start is defined as the first 4 bytes of the sha256d of the block script
CHashWriter h(SER_DISK, 0);
h << consensus.signet_challenge;
diff --git a/src/net.h b/src/net.h
index 55de7afa05..2652d82ea0 100644
--- a/src/net.h
+++ b/src/net.h
@@ -857,7 +857,6 @@ public:
RecursiveMutex cs_sendProcessing;
- std::deque<CInv> vRecvGetData;
uint64_t nRecvBytes GUARDED_BY(cs_vRecv){0};
std::atomic<int64_t> nLastSend{0};
@@ -1051,8 +1050,6 @@ public:
// Whether a ping is requested.
std::atomic<bool> fPingQueued{false};
- std::set<uint256> orphan_work_set;
-
CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn, ConnectionType conn_type_in, bool inbound_onion = false);
~CNode();
CNode(const CNode&) = delete;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index f14db379fb..94d4052fa1 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -446,6 +446,14 @@ struct Peer {
/** Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission). */
bool m_should_discourage GUARDED_BY(m_misbehavior_mutex){false};
+ /** Set of txids to reconsider once their parent transactions have been accepted **/
+ std::set<uint256> m_orphan_work_set GUARDED_BY(g_cs_orphans);
+
+ /** Protects m_getdata_requests **/
+ Mutex m_getdata_requests_mutex;
+ /** Work queue of items requested by this peer **/
+ std::deque<CInv> m_getdata_requests GUARDED_BY(m_getdata_requests_mutex);
+
Peer(NodeId id) : m_id(id) {}
};
@@ -1654,11 +1662,11 @@ static CTransactionRef FindTxForGetData(const CTxMemPool& mempool, const CNode&
return {};
}
-void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnman& connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main)
+void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, CConnman& connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(!cs_main, peer.m_getdata_requests_mutex)
{
AssertLockNotHeld(cs_main);
- std::deque<CInv>::iterator it = pfrom.vRecvGetData.begin();
+ std::deque<CInv>::iterator it = peer.m_getdata_requests.begin();
std::vector<CInv> vNotFound;
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
@@ -1670,7 +1678,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
// Process as many TX items from the front of the getdata queue as
// possible, since they're common and it's efficient to batch process
// them.
- while (it != pfrom.vRecvGetData.end() && it->IsGenTxMsg()) {
+ while (it != peer.m_getdata_requests.end() && it->IsGenTxMsg()) {
if (interruptMsgProc) return;
// The send buffer provides backpressure. If there's no space in
// the buffer, pause processing until the next call.
@@ -1718,7 +1726,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
// Only process one BLOCK item per call, since they're uncommon and can be
// expensive to process.
- if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) {
+ if (it != peer.m_getdata_requests.end() && !pfrom.fPauseSend) {
const CInv &inv = *it++;
if (inv.IsGenBlkMsg()) {
ProcessGetBlockData(pfrom, chainparams, inv, connman);
@@ -1727,7 +1735,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
// and continue processing the queue on the next call.
}
- pfrom.vRecvGetData.erase(pfrom.vRecvGetData.begin(), it);
+ peer.m_getdata_requests.erase(peer.m_getdata_requests.begin(), it);
if (!vNotFound.empty()) {
// Let the peer know that we didn't find what it asked for, so it doesn't
@@ -2270,6 +2278,8 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
return;
}
+ PeerRef peer = GetPeerRef(pfrom.GetId());
+ if (peer == nullptr) return;
if (msg_type == NetMsgType::VERSION) {
// Each connection can only send one version message
@@ -2708,8 +2718,12 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom.GetId());
}
- pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ {
+ LOCK(peer->m_getdata_requests_mutex);
+ peer->m_getdata_requests.insert(peer->m_getdata_requests.end(), vInv.begin(), vInv.end());
+ ProcessGetData(pfrom, *peer, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ }
+
return;
}
@@ -2797,36 +2811,38 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
return;
}
- LOCK(cs_main);
+ {
+ LOCK(cs_main);
- const CBlockIndex* pindex = LookupBlockIndex(req.blockhash);
- if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
- return;
- }
+ const CBlockIndex* pindex = LookupBlockIndex(req.blockhash);
+ if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());
+ return;
+ }
- if (pindex->nHeight < ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
- // If an older block is requested (should never happen in practice,
- // but can happen in tests) send a block response instead of a
- // blocktxn response. Sending a full block response instead of a
- // small blocktxn response is preferable in the case where a peer
- // might maliciously send lots of getblocktxn requests to trigger
- // expensive disk reads, because it will require the peer to
- // actually receive all the data read from disk over the network.
- LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);
- CInv inv;
- inv.type = State(pfrom.GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
- inv.hash = req.blockhash;
- pfrom.vRecvGetData.push_back(inv);
- // The message processing loop will go around again (without pausing) and we'll respond then (without cs_main)
- return;
- }
+ if (pindex->nHeight >= ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) {
+ CBlock block;
+ bool ret = ReadBlockFromDisk(block, pindex, m_chainparams.GetConsensus());
+ assert(ret);
- CBlock block;
- bool ret = ReadBlockFromDisk(block, pindex, m_chainparams.GetConsensus());
- assert(ret);
+ SendBlockTransactions(pfrom, block, req);
+ return;
+ }
+ }
- SendBlockTransactions(pfrom, block, req);
+ // If an older block is requested (should never happen in practice,
+ // but can happen in tests) send a block response instead of a
+ // blocktxn response. Sending a full block response instead of a
+ // small blocktxn response is preferable in the case where a peer
+ // might maliciously send lots of getblocktxn requests to trigger
+ // expensive disk reads, because it will require the peer to
+ // actually receive all the data read from disk over the network.
+ LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);
+ CInv inv;
+ WITH_LOCK(cs_main, inv.type = State(pfrom.GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK);
+ inv.hash = req.blockhash;
+ WITH_LOCK(peer->m_getdata_requests_mutex, peer->m_getdata_requests.push_back(inv));
+ // The message processing loop will go around again (without pausing) and we'll respond then
return;
}
@@ -2961,7 +2977,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(txid, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
for (const auto& elem : it_by_prev->second) {
- pfrom.orphan_work_set.insert(elem->first);
+ peer->m_orphan_work_set.insert(elem->first);
}
}
}
@@ -2978,7 +2994,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
}
// Recursively process any orphan transactions that depended on this one
- ProcessOrphanTx(pfrom.orphan_work_set);
+ ProcessOrphanTx(peer->m_orphan_work_set);
}
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
{
@@ -3773,21 +3789,37 @@ bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgP
{
bool fMoreWork = false;
- if (!pfrom->vRecvGetData.empty())
- ProcessGetData(*pfrom, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ PeerRef peer = GetPeerRef(pfrom->GetId());
+ if (peer == nullptr) return false;
- if (!pfrom->orphan_work_set.empty()) {
+ {
+ LOCK(peer->m_getdata_requests_mutex);
+ if (!peer->m_getdata_requests.empty()) {
+ ProcessGetData(*pfrom, *peer, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ }
+ }
+
+ {
LOCK2(cs_main, g_cs_orphans);
- ProcessOrphanTx(pfrom->orphan_work_set);
+ if (!peer->m_orphan_work_set.empty()) {
+ ProcessOrphanTx(peer->m_orphan_work_set);
+ }
}
if (pfrom->fDisconnect)
return false;
// this maintains the order of responses
- // and prevents vRecvGetData to grow unbounded
- if (!pfrom->vRecvGetData.empty()) return true;
- if (!pfrom->orphan_work_set.empty()) return true;
+ // and prevents m_getdata_requests to grow unbounded
+ {
+ LOCK(peer->m_getdata_requests_mutex);
+ if (!peer->m_getdata_requests.empty()) return true;
+ }
+
+ {
+ LOCK(g_cs_orphans);
+ if (!peer->m_orphan_work_set.empty()) return true;
+ }
// Don't bother if send buffer is too full to respond anyway
if (pfrom->fPauseSend)
@@ -3814,10 +3846,11 @@ bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgP
try {
ProcessMessage(*pfrom, msg_type, msg.m_recv, msg.m_time, interruptMsgProc);
- if (interruptMsgProc)
- return false;
- if (!pfrom->vRecvGetData.empty())
- fMoreWork = true;
+ if (interruptMsgProc) return false;
+ {
+ LOCK(peer->m_getdata_requests_mutex);
+ if (!peer->m_getdata_requests.empty()) fMoreWork = true;
+ }
} catch (const std::exception& e) {
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(msg_type), nMessageSize, e.what(), typeid(e).name());
} catch (...) {
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 3055cd7da6..63b4107f7e 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -505,6 +505,9 @@ int GuiMain(int argc, char* argv[])
return EXIT_SUCCESS;
}
+ // Install global event filter that makes sure that long tooltips can be word-wrapped
+ app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
+
/// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory
bool did_show_intro = false;
@@ -576,8 +579,6 @@ int GuiMain(int argc, char* argv[])
#endif // ENABLE_WALLET
/// 9. Main GUI initialization
- // Install global event filter that makes sure that long tooltips can be word-wrapped
- app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
// Install global event filter that makes sure that out-of-focus labels do not contain text cursor.
app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app));
#if defined(Q_OS_WIN)
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 0c2dcc3584..23370e6ad3 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -669,14 +669,15 @@ void BitcoinGUI::addWallet(WalletModel* walletModel)
{
if (!walletFrame) return;
if (!walletFrame->addWallet(walletModel)) return;
- const QString display_name = walletModel->getDisplayName();
- setWalletActionsEnabled(true);
rpcConsole->addWallet(walletModel);
- m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
- if (m_wallet_selector->count() == 2) {
+ if (m_wallet_selector->count() == 0) {
+ setWalletActionsEnabled(true);
+ } else if (m_wallet_selector->count() == 1) {
m_wallet_selector_label_action->setVisible(true);
m_wallet_selector_action->setVisible(true);
}
+ const QString display_name = walletModel->getDisplayName();
+ m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
}
void BitcoinGUI::removeWallet(WalletModel* walletModel)
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
index c816e1f8ed..52f1e60957 100644
--- a/src/qt/qrimagewidget.cpp
+++ b/src/qt/qrimagewidget.cpp
@@ -9,6 +9,7 @@
#include <QApplication>
#include <QClipboard>
#include <QDrag>
+#include <QFontDatabase>
#include <QMenu>
#include <QMimeData>
#include <QMouseEvent>
@@ -64,26 +65,28 @@ bool QRImageWidget::setQR(const QString& data, const QString& text)
}
QRcode_free(code);
- QImage qrAddrImage = QImage(QR_IMAGE_SIZE, QR_IMAGE_SIZE + (text.isEmpty() ? 0 : 20), QImage::Format_RGB32);
+ const int qr_image_size = QR_IMAGE_SIZE + (text.isEmpty() ? 0 : 2 * QR_IMAGE_MARGIN);
+ QImage qrAddrImage(qr_image_size, qr_image_size, QImage::Format_RGB32);
qrAddrImage.fill(0xffffff);
- QPainter painter(&qrAddrImage);
- painter.drawImage(0, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
+ {
+ QPainter painter(&qrAddrImage);
+ painter.drawImage(QR_IMAGE_MARGIN, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
- if (!text.isEmpty()) {
- QFont font = GUIUtil::fixedPitchFont();
- font.setStyleStrategy(QFont::NoAntialias);
- QRect paddedRect = qrAddrImage.rect();
+ if (!text.isEmpty()) {
+ QRect paddedRect = qrAddrImage.rect();
+ paddedRect.setHeight(QR_IMAGE_SIZE + QR_IMAGE_TEXT_MARGIN);
- // calculate ideal font size
- qreal font_size = GUIUtil::calculateIdealFontSize(paddedRect.width() - 20, text, font);
- font.setPointSizeF(font_size);
+ QFont font = GUIUtil::fixedPitchFont();
+ font.setStretch(QFont::SemiCondensed);
+ font.setLetterSpacing(QFont::AbsoluteSpacing, 1);
+ const qreal font_size = GUIUtil::calculateIdealFontSize(paddedRect.width() - 2 * QR_IMAGE_TEXT_MARGIN, text, font);
+ font.setPointSizeF(font_size);
- painter.setFont(font);
- paddedRect.setHeight(QR_IMAGE_SIZE+12);
- painter.drawText(paddedRect, Qt::AlignBottom|Qt::AlignCenter, text);
+ painter.setFont(font);
+ painter.drawText(paddedRect, Qt::AlignBottom | Qt::AlignCenter, text);
+ }
}
- painter.end();
setPixmap(QPixmap::fromImage(qrAddrImage));
return true;
diff --git a/src/qt/qrimagewidget.h b/src/qt/qrimagewidget.h
index cca598c2ce..a031bd7632 100644
--- a/src/qt/qrimagewidget.h
+++ b/src/qt/qrimagewidget.h
@@ -12,7 +12,9 @@
static const int MAX_URI_LENGTH = 255;
/* Size of exported QR Code image */
-static const int QR_IMAGE_SIZE = 300;
+static constexpr int QR_IMAGE_SIZE = 300;
+static constexpr int QR_IMAGE_TEXT_MARGIN = 10;
+static constexpr int QR_IMAGE_MARGIN = 2 * QR_IMAGE_TEXT_MARGIN;
QT_BEGIN_NAMESPACE
class QMenu;
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 97fb88d71c..50a1ea6936 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -28,6 +28,8 @@
#include <wallet/fees.h>
#include <wallet/wallet.h>
+#include <validation.h>
+
#include <QFontMetrics>
#include <QScrollBar>
#include <QSettings>
@@ -134,7 +136,7 @@ void SendCoinsDialog::setClientModel(ClientModel *_clientModel)
this->clientModel = _clientModel;
if (_clientModel) {
- connect(_clientModel, &ClientModel::numBlocksChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
+ connect(_clientModel, &ClientModel::numBlocksChanged, this, &SendCoinsDialog::updateNumberOfBlocks);
}
}
@@ -744,6 +746,12 @@ void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
ctrl.fAllowWatchOnly = model->wallet().privateKeysDisabled();
}
+void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state) {
+ if (sync_state == SynchronizationState::POST_INIT) {
+ updateSmartFeeLabel();
+ }
+}
+
void SendCoinsDialog::updateSmartFeeLabel()
{
if(!model || !model->getOptionsModel())
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 6961aa7821..8519f1f65b 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -17,6 +17,7 @@ class ClientModel;
class PlatformStyle;
class SendCoinsEntry;
class SendCoinsRecipient;
+enum class SynchronizationState;
namespace Ui {
class SendCoinsDialog;
@@ -98,6 +99,7 @@ private Q_SLOTS:
void coinControlClipboardLowOutput();
void coinControlClipboardChange();
void updateFeeSectionControls();
+ void updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state);
void updateSmartFeeLabel();
Q_SIGNALS:
diff --git a/src/randomenv.cpp b/src/randomenv.cpp
index 073d82b491..07122b7f6d 100644
--- a/src/randomenv.cpp
+++ b/src/randomenv.cpp
@@ -67,7 +67,8 @@ void RandAddSeedPerfmon(CSHA512& hasher)
#ifdef WIN32
// Seed with the entire set of perfmon data
- // This can take up to 2 seconds, so only do it every 10 minutes
+ // This can take up to 2 seconds, so only do it every 10 minutes.
+ // Initialize last_perfmon to 0 seconds, we don't skip the first call.
static std::atomic<std::chrono::seconds> last_perfmon{std::chrono::seconds{0}};
auto last_time = last_perfmon.load();
auto current_time = GetTime<std::chrono::seconds>();
@@ -83,7 +84,7 @@ void RandAddSeedPerfmon(CSHA512& hasher)
ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize);
if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
break;
- vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
+ vData.resize(std::min((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
}
RegCloseKey(HKEY_PERFORMANCE_DATA);
if (ret == ERROR_SUCCESS) {
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 4a6e04f2eb..c1786140de 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -46,14 +46,14 @@ public:
}
void
- ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
+ ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const
{
CSHA256 hasher = m_salted_hasher_ecdsa;
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
}
void
- ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey)
+ ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const
{
CSHA256 hasher = m_salted_hasher_schnorr;
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index b799d3b43b..26c56fbadf 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -13,6 +13,7 @@
#include <key.h>
#include <merkleblock.h>
#include <net.h>
+#include <netbase.h>
#include <node/utxo_snapshot.h>
#include <primitives/block.h>
#include <protocol.h>
@@ -44,9 +45,9 @@ struct invalid_fuzzing_input_exception : public std::exception {
};
template <typename T>
-CDataStream Serialize(const T& obj)
+CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION)
{
- CDataStream ds(SER_NETWORK, INIT_PROTO_VERSION);
+ CDataStream ds(SER_NETWORK, version);
ds << obj;
return ds;
}
@@ -79,9 +80,9 @@ void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj)
}
template <typename T>
-void AssertEqualAfterSerializeDeserialize(const T& obj)
+void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION)
{
- assert(Deserialize<T>(Serialize(obj)) == obj);
+ assert(Deserialize<T>(Serialize(obj, version)) == obj);
}
} // namespace
@@ -183,7 +184,10 @@ void test_one_input(const std::vector<uint8_t>& buffer)
#elif NETADDR_DESERIALIZE
CNetAddr na;
DeserializeFromFuzzingInput(buffer, na);
- AssertEqualAfterSerializeDeserialize(na);
+ if (na.IsAddrV1Compatible()) {
+ AssertEqualAfterSerializeDeserialize(na);
+ }
+ AssertEqualAfterSerializeDeserialize(na, INIT_PROTO_VERSION | ADDRV2_FORMAT);
#elif SERVICE_DESERIALIZE
CService s;
DeserializeFromFuzzingInput(buffer, s);
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index a2efd8ac07..7c53bd0002 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -1347,36 +1347,6 @@ static CScript ScriptFromHex(const std::string& str)
return CScript(data.begin(), data.end());
}
-static CMutableTransaction TxFromHex(const std::string& str)
-{
- CMutableTransaction tx;
- VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0) >> tx;
- return tx;
-}
-
-static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
-{
- assert(univalue.isArray());
- std::vector<CTxOut> prevouts;
- for (size_t i = 0; i < univalue.size(); ++i) {
- CTxOut txout;
- VectorReader(SER_DISK, 0, ParseHex(univalue[i].get_str()), 0) >> txout;
- prevouts.push_back(std::move(txout));
- }
- return prevouts;
-}
-
-static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
-{
- assert(univalue.isArray());
- CScriptWitness scriptwitness;
- for (size_t i = 0; i < univalue.size(); ++i) {
- auto bytes = ParseHex(univalue[i].get_str());
- scriptwitness.stack.push_back(std::move(bytes));
- }
- return scriptwitness;
-}
-
BOOST_AUTO_TEST_CASE(script_FindAndDelete)
{
// Exercise the FindAndDelete functionality
@@ -1502,6 +1472,36 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps)
#if defined(HAVE_CONSENSUS_LIB)
+static CMutableTransaction TxFromHex(const std::string& str)
+{
+ CMutableTransaction tx;
+ VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0) >> tx;
+ return tx;
+}
+
+static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
+{
+ assert(univalue.isArray());
+ std::vector<CTxOut> prevouts;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ CTxOut txout;
+ VectorReader(SER_DISK, 0, ParseHex(univalue[i].get_str()), 0) >> txout;
+ prevouts.push_back(std::move(txout));
+ }
+ return prevouts;
+}
+
+static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
+{
+ assert(univalue.isArray());
+ CScriptWitness scriptwitness;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ auto bytes = ParseHex(univalue[i].get_str());
+ scriptwitness.stack.push_back(std::move(bytes));
+ }
+ return scriptwitness;
+}
+
/* Test simple (successful) usage of bitcoinconsensus_verify_script */
BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_returns_true)
{
diff --git a/src/txrequest.cpp b/src/txrequest.cpp
index 494786c201..09eb78e927 100644
--- a/src/txrequest.cpp
+++ b/src/txrequest.cpp
@@ -69,32 +69,40 @@ struct Announcement {
/** Whether this is a wtxid request. */
const bool m_is_wtxid : 1;
- /** What state this announcement is in. */
- State m_state : 3;
+ /** What state this announcement is in.
+ * This is a uint8_t instead of a State to silence a GCC warning in versions prior to 8.4 and 9.3.
+ * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414 */
+ uint8_t m_state : 3;
+
+ /** Convert m_state to a State enum. */
+ State GetState() const { return static_cast<State>(m_state); }
+
+ /** Convert a State enum to a uint8_t and store it in m_state. */
+ void SetState(State state) { m_state = static_cast<uint8_t>(state); }
/** Whether this announcement is selected. There can be at most 1 selected peer per txhash. */
bool IsSelected() const
{
- return m_state == State::CANDIDATE_BEST || m_state == State::REQUESTED;
+ return GetState() == State::CANDIDATE_BEST || GetState() == State::REQUESTED;
}
/** Whether this announcement is waiting for a certain time to pass. */
bool IsWaiting() const
{
- return m_state == State::REQUESTED || m_state == State::CANDIDATE_DELAYED;
+ return GetState() == State::REQUESTED || GetState() == State::CANDIDATE_DELAYED;
}
/** Whether this announcement can feasibly be selected if the current IsSelected() one disappears. */
bool IsSelectable() const
{
- return m_state == State::CANDIDATE_READY || m_state == State::CANDIDATE_BEST;
+ return GetState() == State::CANDIDATE_READY || GetState() == State::CANDIDATE_BEST;
}
/** Construct a new announcement from scratch, initially in CANDIDATE_DELAYED state. */
Announcement(const GenTxid& gtxid, NodeId peer, bool preferred, std::chrono::microseconds reqtime,
SequenceNumber sequence) :
m_txhash(gtxid.GetHash()), m_time(reqtime), m_peer(peer), m_sequence(sequence), m_preferred(preferred),
- m_is_wtxid(gtxid.IsWtxid()), m_state(State::CANDIDATE_DELAYED) {}
+ m_is_wtxid(gtxid.IsWtxid()), m_state(static_cast<uint8_t>(State::CANDIDATE_DELAYED)) {}
};
//! Type alias for priorities.
@@ -143,7 +151,7 @@ struct ByPeerViewExtractor
using result_type = ByPeerView;
result_type operator()(const Announcement& ann) const
{
- return ByPeerView{ann.m_peer, ann.m_state == State::CANDIDATE_BEST, ann.m_txhash};
+ return ByPeerView{ann.m_peer, ann.GetState() == State::CANDIDATE_BEST, ann.m_txhash};
}
};
@@ -166,8 +174,8 @@ public:
using result_type = ByTxHashView;
result_type operator()(const Announcement& ann) const
{
- const Priority prio = (ann.m_state == State::CANDIDATE_READY) ? m_computer(ann) : 0;
- return ByTxHashView{ann.m_txhash, ann.m_state, prio};
+ const Priority prio = (ann.GetState() == State::CANDIDATE_READY) ? m_computer(ann) : 0;
+ return ByTxHashView{ann.m_txhash, ann.GetState(), prio};
}
};
@@ -261,8 +269,8 @@ std::unordered_map<NodeId, PeerInfo> RecomputePeerInfo(const Index& index)
for (const Announcement& ann : index) {
PeerInfo& info = ret[ann.m_peer];
++info.m_total;
- info.m_requested += (ann.m_state == State::REQUESTED);
- info.m_completed += (ann.m_state == State::COMPLETED);
+ info.m_requested += (ann.GetState() == State::REQUESTED);
+ info.m_completed += (ann.GetState() == State::COMPLETED);
}
return ret;
}
@@ -274,15 +282,15 @@ std::map<uint256, TxHashInfo> ComputeTxHashInfo(const Index& index, const Priori
for (const Announcement& ann : index) {
TxHashInfo& info = ret[ann.m_txhash];
// Classify how many announcements of each state we have for this txhash.
- info.m_candidate_delayed += (ann.m_state == State::CANDIDATE_DELAYED);
- info.m_candidate_ready += (ann.m_state == State::CANDIDATE_READY);
- info.m_candidate_best += (ann.m_state == State::CANDIDATE_BEST);
- info.m_requested += (ann.m_state == State::REQUESTED);
+ info.m_candidate_delayed += (ann.GetState() == State::CANDIDATE_DELAYED);
+ info.m_candidate_ready += (ann.GetState() == State::CANDIDATE_READY);
+ info.m_candidate_best += (ann.GetState() == State::CANDIDATE_BEST);
+ info.m_requested += (ann.GetState() == State::REQUESTED);
// And track the priority of the best CANDIDATE_READY/CANDIDATE_BEST announcements.
- if (ann.m_state == State::CANDIDATE_BEST) {
+ if (ann.GetState() == State::CANDIDATE_BEST) {
info.m_priority_candidate_best = computer(ann);
}
- if (ann.m_state == State::CANDIDATE_READY) {
+ if (ann.GetState() == State::CANDIDATE_READY) {
info.m_priority_best_candidate_ready = std::max(info.m_priority_best_candidate_ready, computer(ann));
}
// Also keep track of which peers this txhash has an announcement for (so we can detect duplicates).
@@ -369,8 +377,8 @@ private:
Iter<Tag> Erase(Iter<Tag> it)
{
auto peerit = m_peerinfo.find(it->m_peer);
- peerit->second.m_completed -= it->m_state == State::COMPLETED;
- peerit->second.m_requested -= it->m_state == State::REQUESTED;
+ peerit->second.m_completed -= it->GetState() == State::COMPLETED;
+ peerit->second.m_requested -= it->GetState() == State::REQUESTED;
if (--peerit->second.m_total == 0) m_peerinfo.erase(peerit);
return m_index.get<Tag>().erase(it);
}
@@ -380,11 +388,11 @@ private:
void Modify(Iter<Tag> it, Modifier modifier)
{
auto peerit = m_peerinfo.find(it->m_peer);
- peerit->second.m_completed -= it->m_state == State::COMPLETED;
- peerit->second.m_requested -= it->m_state == State::REQUESTED;
+ peerit->second.m_completed -= it->GetState() == State::COMPLETED;
+ peerit->second.m_requested -= it->GetState() == State::REQUESTED;
m_index.get<Tag>().modify(it, std::move(modifier));
- peerit->second.m_completed += it->m_state == State::COMPLETED;
- peerit->second.m_requested += it->m_state == State::REQUESTED;
+ peerit->second.m_completed += it->GetState() == State::COMPLETED;
+ peerit->second.m_requested += it->GetState() == State::REQUESTED;
}
//! Convert a CANDIDATE_DELAYED announcement into a CANDIDATE_READY. If this makes it the new best
@@ -393,26 +401,26 @@ private:
void PromoteCandidateReady(Iter<ByTxHash> it)
{
assert(it != m_index.get<ByTxHash>().end());
- assert(it->m_state == State::CANDIDATE_DELAYED);
+ assert(it->GetState() == State::CANDIDATE_DELAYED);
// Convert CANDIDATE_DELAYED to CANDIDATE_READY first.
- Modify<ByTxHash>(it, [](Announcement& ann){ ann.m_state = State::CANDIDATE_READY; });
+ Modify<ByTxHash>(it, [](Announcement& ann){ ann.SetState(State::CANDIDATE_READY); });
// The following code relies on the fact that the ByTxHash is sorted by txhash, and then by state (first
// _DELAYED, then _READY, then _BEST/REQUESTED). Within the _READY announcements, the best one (highest
// priority) comes last. Thus, if an existing _BEST exists for the same txhash that this announcement may
// be preferred over, it must immediately follow the newly created _READY.
auto it_next = std::next(it);
if (it_next == m_index.get<ByTxHash>().end() || it_next->m_txhash != it->m_txhash ||
- it_next->m_state == State::COMPLETED) {
+ it_next->GetState() == State::COMPLETED) {
// This is the new best CANDIDATE_READY, and there is no IsSelected() announcement for this txhash
// already.
- Modify<ByTxHash>(it, [](Announcement& ann){ ann.m_state = State::CANDIDATE_BEST; });
- } else if (it_next->m_state == State::CANDIDATE_BEST) {
+ Modify<ByTxHash>(it, [](Announcement& ann){ ann.SetState(State::CANDIDATE_BEST); });
+ } else if (it_next->GetState() == State::CANDIDATE_BEST) {
Priority priority_old = m_computer(*it_next);
Priority priority_new = m_computer(*it);
if (priority_new > priority_old) {
// There is a CANDIDATE_BEST announcement already, but this one is better.
- Modify<ByTxHash>(it_next, [](Announcement& ann){ ann.m_state = State::CANDIDATE_READY; });
- Modify<ByTxHash>(it, [](Announcement& ann){ ann.m_state = State::CANDIDATE_BEST; });
+ Modify<ByTxHash>(it_next, [](Announcement& ann){ ann.SetState(State::CANDIDATE_READY); });
+ Modify<ByTxHash>(it, [](Announcement& ann){ ann.SetState(State::CANDIDATE_BEST); });
}
}
}
@@ -427,19 +435,19 @@ private:
auto it_prev = std::prev(it);
// The next best CANDIDATE_READY, if any, immediately precedes the REQUESTED or CANDIDATE_BEST
// announcement in the ByTxHash index.
- if (it_prev->m_txhash == it->m_txhash && it_prev->m_state == State::CANDIDATE_READY) {
+ if (it_prev->m_txhash == it->m_txhash && it_prev->GetState() == State::CANDIDATE_READY) {
// If one such CANDIDATE_READY exists (for this txhash), convert it to CANDIDATE_BEST.
- Modify<ByTxHash>(it_prev, [](Announcement& ann){ ann.m_state = State::CANDIDATE_BEST; });
+ Modify<ByTxHash>(it_prev, [](Announcement& ann){ ann.SetState(State::CANDIDATE_BEST); });
}
}
- Modify<ByTxHash>(it, [new_state](Announcement& ann){ ann.m_state = new_state; });
+ Modify<ByTxHash>(it, [new_state](Announcement& ann){ ann.SetState(new_state); });
}
//! Check if 'it' is the only announcement for a given txhash that isn't COMPLETED.
bool IsOnlyNonCompleted(Iter<ByTxHash> it)
{
assert(it != m_index.get<ByTxHash>().end());
- assert(it->m_state != State::COMPLETED); // Not allowed to call this on COMPLETED announcements.
+ assert(it->GetState() != State::COMPLETED); // Not allowed to call this on COMPLETED announcements.
// This announcement has a predecessor that belongs to the same txhash. Due to ordering, and the
// fact that 'it' is not COMPLETED, its predecessor cannot be COMPLETED here.
@@ -447,7 +455,7 @@ private:
// This announcement has a successor that belongs to the same txhash, and is not COMPLETED.
if (std::next(it) != m_index.get<ByTxHash>().end() && std::next(it)->m_txhash == it->m_txhash &&
- std::next(it)->m_state != State::COMPLETED) return false;
+ std::next(it)->GetState() != State::COMPLETED) return false;
return true;
}
@@ -460,7 +468,7 @@ private:
assert(it != m_index.get<ByTxHash>().end());
// Nothing to be done if it's already COMPLETED.
- if (it->m_state == State::COMPLETED) return true;
+ if (it->GetState() == State::COMPLETED) return true;
if (IsOnlyNonCompleted(it)) {
// This is the last non-COMPLETED announcement for this txhash. Delete all.
@@ -490,9 +498,9 @@ private:
// and convert them to CANDIDATE_READY and COMPLETED respectively.
while (!m_index.empty()) {
auto it = m_index.get<ByTime>().begin();
- if (it->m_state == State::CANDIDATE_DELAYED && it->m_time <= now) {
+ if (it->GetState() == State::CANDIDATE_DELAYED && it->m_time <= now) {
PromoteCandidateReady(m_index.project<ByTxHash>(it));
- } else if (it->m_state == State::REQUESTED && it->m_time <= now) {
+ } else if (it->GetState() == State::REQUESTED && it->m_time <= now) {
if (expired) expired->emplace_back(it->m_peer, ToGenTxid(*it));
MakeCompleted(m_index.project<ByTxHash>(it));
} else {
@@ -596,7 +604,7 @@ public:
std::vector<const Announcement*> selected;
auto it_peer = m_index.get<ByPeer>().lower_bound(ByPeerView{peer, true, uint256::ZERO});
while (it_peer != m_index.get<ByPeer>().end() && it_peer->m_peer == peer &&
- it_peer->m_state == State::CANDIDATE_BEST) {
+ it_peer->GetState() == State::CANDIDATE_BEST) {
selected.emplace_back(&*it_peer);
++it_peer;
}
@@ -625,8 +633,8 @@ public:
// returned by GetRequestable always correspond to CANDIDATE_BEST announcements).
it = m_index.get<ByPeer>().find(ByPeerView{peer, false, txhash});
- if (it == m_index.get<ByPeer>().end() || (it->m_state != State::CANDIDATE_DELAYED &&
- it->m_state != State::CANDIDATE_READY)) {
+ if (it == m_index.get<ByPeer>().end() || (it->GetState() != State::CANDIDATE_DELAYED &&
+ it->GetState() != State::CANDIDATE_READY)) {
// There is no CANDIDATE announcement tracked for this peer, so we have nothing to do. Either this
// txhash wasn't tracked at all (and the caller should have called ReceivedInv), or it was already
// requested and/or completed for other reasons and this is just a superfluous RequestedTx call.
@@ -638,24 +646,24 @@ public:
// other CANDIDATE_BEST or REQUESTED can exist.
auto it_old = m_index.get<ByTxHash>().lower_bound(ByTxHashView{txhash, State::CANDIDATE_BEST, 0});
if (it_old != m_index.get<ByTxHash>().end() && it_old->m_txhash == txhash) {
- if (it_old->m_state == State::CANDIDATE_BEST) {
+ if (it_old->GetState() == State::CANDIDATE_BEST) {
// The data structure's invariants require that there can be at most one CANDIDATE_BEST or one
// REQUESTED announcement per txhash (but not both simultaneously), so we have to convert any
// existing CANDIDATE_BEST to another CANDIDATE_* when constructing another REQUESTED.
// It doesn't matter whether we pick CANDIDATE_READY or _DELAYED here, as SetTimePoint()
// will correct it at GetRequestable() time. If time only goes forward, it will always be
// _READY, so pick that to avoid extra work in SetTimePoint().
- Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.m_state = State::CANDIDATE_READY; });
- } else if (it_old->m_state == State::REQUESTED) {
+ Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.SetState(State::CANDIDATE_READY); });
+ } else if (it_old->GetState() == State::REQUESTED) {
// As we're no longer waiting for a response to the previous REQUESTED announcement, convert it
// to COMPLETED. This also helps guaranteeing progress.
- Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.m_state = State::COMPLETED; });
+ Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.SetState(State::COMPLETED); });
}
}
}
Modify<ByPeer>(it, [expiry](Announcement& ann) {
- ann.m_state = State::REQUESTED;
+ ann.SetState(State::REQUESTED);
ann.m_time = expiry;
});
}
diff --git a/src/util/system.cpp b/src/util/system.cpp
index a411b73a16..9f8035948b 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -427,6 +427,14 @@ bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
SaveErrors(read_errors, errors);
return false;
}
+ for (const auto& setting : m_settings.rw_settings) {
+ std::string section;
+ std::string key = setting.first;
+ (void)InterpretOption(section, key, /* value */ {}); // Split setting key into section and argname
+ if (!GetArgFlags('-' + key)) {
+ LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
+ }
+ }
return true;
}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index 5403e95ee4..9073c1b6b3 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -146,6 +146,7 @@ public:
/** Return path to main database filename */
std::string Filename() override { return (env->Directory() / strFile).string(); }
+ std::string Format() override { return "bdb"; }
/**
* Pointer to shared database environment.
*
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 3ecccd4e00..940d1cd242 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -144,6 +144,8 @@ public:
/** Return path to main database file for logs and error messages. */
virtual std::string Filename() = 0;
+ virtual std::string Format() = 0;
+
std::atomic<unsigned int> nUpdateCounter;
unsigned int nLastSeen;
unsigned int nLastFlushed;
@@ -190,6 +192,7 @@ public:
void IncrementUpdateCounter() override { ++nUpdateCounter; }
void ReloadDbEnv() override {}
std::string Filename() override { return "dummy"; }
+ std::string Format() override { return "dummy"; }
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); }
};
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 2295fb0ef1..29b55ec130 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2422,6 +2422,7 @@ static RPCHelpMan getwalletinfo()
{
{RPCResult::Type::STR, "walletname", "the wallet name"},
{RPCResult::Type::NUM, "walletversion", "the wallet version"},
+ {RPCResult::Type::STR, "format", "the database format (bdb or sqlite)"},
{RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
{RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
{RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
@@ -2465,6 +2466,7 @@ static RPCHelpMan getwalletinfo()
int64_t kp_oldest = pwallet->GetOldestKeyPoolTime();
obj.pushKV("walletname", pwallet->GetName());
obj.pushKV("walletversion", pwallet->GetVersion());
+ obj.pushKV("format", pwallet->GetDatabase().Format());
obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index b7c70dac3a..188289b010 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -453,7 +453,7 @@ bool LegacyScriptPubKeyMan::Upgrade(int prev_version, bilingual_str& error)
hd_upgrade = true;
}
// Upgrade to HD chain split if necessary
- if (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) && CHDChain::VERSION_HD_CHAIN_SPLIT) {
+ if (m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) {
WalletLogPrintf("Upgrading wallet to use HD chain split\n");
m_storage.SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);
split_upgrade = FEATURE_HD_SPLIT > prev_version;
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 14fb1fa89f..63c10b7a0d 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -33,7 +33,7 @@ class WalletStorage
public:
virtual ~WalletStorage() = default;
virtual const std::string GetDisplayName() const = 0;
- virtual WalletDatabase& GetDatabase() = 0;
+ virtual WalletDatabase& GetDatabase() const = 0;
virtual bool IsWalletFlagSet(uint64_t) const = 0;
virtual void UnsetBlankWalletFlag(WalletBatch&) = 0;
virtual bool CanSupportFeature(enum WalletFeature) const = 0;
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 02a161ecbd..6d2fdbe58b 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -619,8 +619,8 @@ bool IsSQLiteFile(const fs::path& path)
file.close();
// Check the magic, see https://sqlite.org/fileformat2.html
- std::string magic_str(magic);
- if (magic_str != std::string("SQLite format 3")) {
+ std::string magic_str(magic, 16);
+ if (magic_str != std::string("SQLite format 3", 16)) {
return false;
}
diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h
index 5e5e93903b..693a2ef55a 100644
--- a/src/wallet/sqlite.h
+++ b/src/wallet/sqlite.h
@@ -105,6 +105,7 @@ public:
void IncrementUpdateCounter() override { ++nUpdateCounter; }
std::string Filename() override { return m_file_path; }
+ std::string Format() override { return "sqlite"; }
/** Make a SQLiteBatch connected to this database */
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 245144a1c9..74de55dcb5 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -739,7 +739,7 @@ public:
{
return *database;
}
- WalletDatabase& GetDatabase() override { return *database; }
+ WalletDatabase& GetDatabase() const override { return *database; }
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 4452840eb1..0e18d6a740 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -95,6 +95,9 @@ static void WalletShowInfo(CWallet* wallet_instance)
LOCK(wallet_instance->cs_wallet);
tfm::format(std::cout, "Wallet info\n===========\n");
+ tfm::format(std::cout, "Name: %s\n", wallet_instance->GetName());
+ tfm::format(std::cout, "Format: %s\n", wallet_instance->GetDatabase().Format());
+ tfm::format(std::cout, "Descriptors: %s\n", wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) ? "yes" : "no");
tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->IsHDEnabled() ? "yes" : "no");
tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());