diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/chainparams.cpp | 5 | ||||
-rw-r--r-- | src/net.h | 3 | ||||
-rw-r--r-- | src/net_processing.cpp | 125 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 5 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 9 | ||||
-rw-r--r-- | src/qt/qrimagewidget.cpp | 31 | ||||
-rw-r--r-- | src/qt/qrimagewidget.h | 4 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 10 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.h | 2 | ||||
-rw-r--r-- | src/randomenv.cpp | 5 | ||||
-rw-r--r-- | src/script/sigcache.cpp | 4 | ||||
-rw-r--r-- | src/test/fuzz/deserialize.cpp | 14 | ||||
-rw-r--r-- | src/test/script_tests.cpp | 60 | ||||
-rw-r--r-- | src/txrequest.cpp | 100 | ||||
-rw-r--r-- | src/util/system.cpp | 8 | ||||
-rw-r--r-- | src/wallet/bdb.h | 1 | ||||
-rw-r--r-- | src/wallet/db.h | 3 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 2 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.cpp | 2 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.h | 2 | ||||
-rw-r--r-- | src/wallet/sqlite.cpp | 4 | ||||
-rw-r--r-- | src/wallet/sqlite.h | 1 | ||||
-rw-r--r-- | src/wallet/wallet.h | 2 | ||||
-rw-r--r-- | src/wallet/wallettool.cpp | 3 |
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; @@ -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()); |