ObsidianDragon - DragonX ImGui Wallet
Full-node GUI wallet for DragonX cryptocurrency. Built with Dear ImGui, SDL3, and OpenGL3/DX11. Features: - Send/receive shielded and transparent transactions - Autoshield with merged transaction display - Built-in CPU mining (xmrig) - Peer management and network monitoring - Wallet encryption with PIN lock - QR code generation for receive addresses - Transaction history with pagination - Console for direct RPC commands - Cross-platform (Linux, Windows)
This commit is contained in:
184
src/data/address_book.cpp
Normal file
184
src/data/address_book.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#include "address_book.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "../util/logger.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h>
|
||||
#else
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace dragonx {
|
||||
namespace data {
|
||||
|
||||
AddressBook::AddressBook() = default;
|
||||
AddressBook::~AddressBook() = default;
|
||||
|
||||
std::string AddressBook::getDefaultPath()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char path[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, path))) {
|
||||
std::string dir = std::string(path) + "\\ObsidianDragon";
|
||||
fs::create_directories(dir);
|
||||
return dir + "\\addressbook.json";
|
||||
}
|
||||
return "addressbook.json";
|
||||
#elif defined(__APPLE__)
|
||||
const char* home = getenv("HOME");
|
||||
if (!home) {
|
||||
struct passwd* pw = getpwuid(getuid());
|
||||
home = pw->pw_dir;
|
||||
}
|
||||
std::string dir = std::string(home) + "/Library/Application Support/ObsidianDragon";
|
||||
fs::create_directories(dir);
|
||||
return dir + "/addressbook.json";
|
||||
#else
|
||||
const char* home = getenv("HOME");
|
||||
if (!home) {
|
||||
struct passwd* pw = getpwuid(getuid());
|
||||
home = pw->pw_dir;
|
||||
}
|
||||
std::string dir = std::string(home) + "/.config/ObsidianDragon";
|
||||
fs::create_directories(dir);
|
||||
return dir + "/addressbook.json";
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AddressBook::load()
|
||||
{
|
||||
file_path_ = getDefaultPath();
|
||||
|
||||
std::ifstream file(file_path_);
|
||||
if (!file.is_open()) {
|
||||
// No file yet - that's OK
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
json j;
|
||||
file >> j;
|
||||
|
||||
entries_.clear();
|
||||
|
||||
if (j.contains("entries") && j["entries"].is_array()) {
|
||||
for (const auto& entry : j["entries"]) {
|
||||
AddressBookEntry e;
|
||||
e.label = entry.value("label", "");
|
||||
e.address = entry.value("address", "");
|
||||
e.notes = entry.value("notes", "");
|
||||
|
||||
if (!e.address.empty()) {
|
||||
entries_.push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_LOGF("Address book loaded: %zu entries\n", entries_.size());
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("Error loading address book: %s\n", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AddressBook::save()
|
||||
{
|
||||
if (file_path_.empty()) {
|
||||
file_path_ = getDefaultPath();
|
||||
}
|
||||
|
||||
try {
|
||||
json j;
|
||||
j["entries"] = json::array();
|
||||
|
||||
for (const auto& entry : entries_) {
|
||||
json e;
|
||||
e["label"] = entry.label;
|
||||
e["address"] = entry.address;
|
||||
e["notes"] = entry.notes;
|
||||
j["entries"].push_back(e);
|
||||
}
|
||||
|
||||
// Ensure directory exists
|
||||
fs::path p(file_path_);
|
||||
fs::create_directories(p.parent_path());
|
||||
|
||||
std::ofstream file(file_path_);
|
||||
if (!file.is_open()) {
|
||||
DEBUG_LOGF("Could not open address book for writing: %s\n", file_path_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
file << j.dump(2);
|
||||
DEBUG_LOGF("Address book saved: %zu entries\n", entries_.size());
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("Error saving address book: %s\n", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AddressBook::addEntry(const AddressBookEntry& entry)
|
||||
{
|
||||
// Check for duplicate address
|
||||
if (findByAddress(entry.address) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entries_.push_back(entry);
|
||||
return save();
|
||||
}
|
||||
|
||||
bool AddressBook::updateEntry(size_t index, const AddressBookEntry& entry)
|
||||
{
|
||||
if (index >= entries_.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicate address (excluding current entry)
|
||||
int existing = findByAddress(entry.address);
|
||||
if (existing >= 0 && static_cast<size_t>(existing) != index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entries_[index] = entry;
|
||||
return save();
|
||||
}
|
||||
|
||||
bool AddressBook::removeEntry(size_t index)
|
||||
{
|
||||
if (index >= entries_.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entries_.erase(entries_.begin() + index);
|
||||
return save();
|
||||
}
|
||||
|
||||
int AddressBook::findByAddress(const std::string& address) const
|
||||
{
|
||||
for (size_t i = 0; i < entries_.size(); i++) {
|
||||
if (entries_[i].address == address) {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace data
|
||||
} // namespace dragonx
|
||||
103
src/data/address_book.h
Normal file
103
src/data/address_book.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dragonx {
|
||||
namespace data {
|
||||
|
||||
/**
|
||||
* @brief A single address book entry
|
||||
*/
|
||||
struct AddressBookEntry {
|
||||
std::string label;
|
||||
std::string address;
|
||||
std::string notes;
|
||||
|
||||
AddressBookEntry() = default;
|
||||
AddressBookEntry(const std::string& l, const std::string& a, const std::string& n = "")
|
||||
: label(l), address(a), notes(n) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Address book manager
|
||||
*
|
||||
* Stores labeled addresses for easy access when sending.
|
||||
*/
|
||||
class AddressBook {
|
||||
public:
|
||||
AddressBook();
|
||||
~AddressBook();
|
||||
|
||||
/**
|
||||
* @brief Load address book from disk
|
||||
* @return true if loaded successfully
|
||||
*/
|
||||
bool load();
|
||||
|
||||
/**
|
||||
* @brief Save address book to disk
|
||||
* @return true if saved successfully
|
||||
*/
|
||||
bool save();
|
||||
|
||||
/**
|
||||
* @brief Get default address book file path
|
||||
*/
|
||||
static std::string getDefaultPath();
|
||||
|
||||
/**
|
||||
* @brief Add a new entry
|
||||
* @param entry The entry to add
|
||||
* @return true if added (false if duplicate address)
|
||||
*/
|
||||
bool addEntry(const AddressBookEntry& entry);
|
||||
|
||||
/**
|
||||
* @brief Update an existing entry
|
||||
* @param index Index of entry to update
|
||||
* @param entry New entry data
|
||||
* @return true if updated
|
||||
*/
|
||||
bool updateEntry(size_t index, const AddressBookEntry& entry);
|
||||
|
||||
/**
|
||||
* @brief Remove an entry
|
||||
* @param index Index of entry to remove
|
||||
* @return true if removed
|
||||
*/
|
||||
bool removeEntry(size_t index);
|
||||
|
||||
/**
|
||||
* @brief Find entry by address
|
||||
* @param address Address to search for
|
||||
* @return Index or -1 if not found
|
||||
*/
|
||||
int findByAddress(const std::string& address) const;
|
||||
|
||||
/**
|
||||
* @brief Get all entries
|
||||
*/
|
||||
const std::vector<AddressBookEntry>& entries() const { return entries_; }
|
||||
|
||||
/**
|
||||
* @brief Get entry count
|
||||
*/
|
||||
size_t size() const { return entries_.size(); }
|
||||
|
||||
/**
|
||||
* @brief Check if empty
|
||||
*/
|
||||
bool empty() const { return entries_.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<AddressBookEntry> entries_;
|
||||
std::string file_path_;
|
||||
};
|
||||
|
||||
} // namespace data
|
||||
} // namespace dragonx
|
||||
34
src/data/exchange_info.cpp
Normal file
34
src/data/exchange_info.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#include "exchange_info.h"
|
||||
|
||||
namespace dragonx {
|
||||
namespace data {
|
||||
|
||||
const std::vector<ExchangeInfo>& getExchangeRegistry()
|
||||
{
|
||||
static const std::vector<ExchangeInfo> registry = {
|
||||
{
|
||||
"TradeOgre",
|
||||
"https://tradeogre.com",
|
||||
{
|
||||
{"DRGX", "BTC", "DRGX/BTC", "https://tradeogre.com/exchange/DRGX-BTC"},
|
||||
{"DRGX", "LTC", "DRGX/LTC", "https://tradeogre.com/exchange/DRGX-LTC"},
|
||||
{"DRGX", "USDT", "DRGX/USDT", "https://tradeogre.com/exchange/DRGX-USDT"},
|
||||
}
|
||||
},
|
||||
{
|
||||
"Exbitron",
|
||||
"https://www.exbitron.com",
|
||||
{
|
||||
{"DRGX", "USDT", "DRGX/USDT", "https://www.exbitron.com/trading/drgxusdt"},
|
||||
}
|
||||
},
|
||||
};
|
||||
return registry;
|
||||
}
|
||||
|
||||
} // namespace data
|
||||
} // namespace dragonx
|
||||
38
src/data/exchange_info.h
Normal file
38
src/data/exchange_info.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dragonx {
|
||||
namespace data {
|
||||
|
||||
/**
|
||||
* @brief A single trading pair on an exchange (e.g. DRGX/BTC)
|
||||
*/
|
||||
struct ExchangePair {
|
||||
std::string base; ///< e.g. "DRGX"
|
||||
std::string quote; ///< e.g. "BTC"
|
||||
std::string displayName; ///< e.g. "DRGX/BTC"
|
||||
std::string tradeUrl; ///< Link to the exchange pair page
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Metadata for a supported exchange
|
||||
*/
|
||||
struct ExchangeInfo {
|
||||
std::string name; ///< e.g. "TradeOgre"
|
||||
std::string baseUrl; ///< e.g. "https://tradeogre.com"
|
||||
std::vector<ExchangePair> pairs;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Returns the static registry of supported exchanges + pairs
|
||||
*/
|
||||
const std::vector<ExchangeInfo>& getExchangeRegistry();
|
||||
|
||||
} // namespace data
|
||||
} // namespace dragonx
|
||||
57
src/data/wallet_state.cpp
Normal file
57
src/data/wallet_state.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#include "wallet_state.h"
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace dragonx {
|
||||
|
||||
std::string TransactionInfo::getTimeString() const
|
||||
{
|
||||
if (timestamp == 0) return "Unknown";
|
||||
|
||||
std::time_t t = static_cast<std::time_t>(timestamp);
|
||||
std::tm* tm = std::localtime(&t);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(tm, "%Y-%m-%d %H:%M");
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string TransactionInfo::getTypeDisplay() const
|
||||
{
|
||||
if (type == "send") return "Sent";
|
||||
if (type == "receive") return "Received";
|
||||
if (type == "mined" || type == "generate" || type == "immature") return "Mined";
|
||||
return type;
|
||||
}
|
||||
|
||||
std::string PeerInfo::getConnectionTime() const
|
||||
{
|
||||
if (conntime == 0) return "Unknown";
|
||||
|
||||
int64_t now = std::time(nullptr);
|
||||
int64_t diff = now - conntime;
|
||||
|
||||
if (diff < 60) return std::to_string(diff) + "s";
|
||||
if (diff < 3600) return std::to_string(diff / 60) + "m";
|
||||
if (diff < 86400) return std::to_string(diff / 3600) + "h";
|
||||
return std::to_string(diff / 86400) + "d";
|
||||
}
|
||||
|
||||
std::string BannedPeer::getBannedUntilString() const
|
||||
{
|
||||
if (banned_until == 0) return "Never";
|
||||
|
||||
std::time_t t = static_cast<std::time_t>(banned_until);
|
||||
std::tm* tm = std::localtime(&t);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(tm, "%Y-%m-%d %H:%M");
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace dragonx
|
||||
267
src/data/wallet_state.h
Normal file
267
src/data/wallet_state.h
Normal file
@@ -0,0 +1,267 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
|
||||
namespace dragonx {
|
||||
|
||||
/**
|
||||
* @brief Represents an address with its balance
|
||||
*/
|
||||
struct AddressInfo {
|
||||
std::string address;
|
||||
double balance = 0.0;
|
||||
std::string type; // "shielded" or "transparent"
|
||||
|
||||
// For display
|
||||
std::string label;
|
||||
|
||||
// Derived
|
||||
bool isZAddr() const { return !address.empty() && address[0] == 'z'; }
|
||||
bool isShielded() const { return type == "shielded"; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a wallet transaction
|
||||
*/
|
||||
struct TransactionInfo {
|
||||
std::string txid;
|
||||
std::string type; // "send", "receive", "mined"
|
||||
double amount = 0.0;
|
||||
int64_t timestamp = 0; // Unix timestamp
|
||||
int confirmations = 0;
|
||||
std::string address; // destination (send) or source (receive)
|
||||
std::string from_address; // source address for sends
|
||||
std::string memo;
|
||||
|
||||
// Computed fields
|
||||
std::string getTimeString() const;
|
||||
std::string getTypeDisplay() const;
|
||||
bool isConfirmed() const { return confirmations >= 1; }
|
||||
bool isMature() const { return confirmations >= 100; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a connected peer
|
||||
*/
|
||||
struct PeerInfo {
|
||||
int id = 0;
|
||||
std::string addr;
|
||||
std::string subver;
|
||||
std::string services;
|
||||
int version = 0;
|
||||
int64_t conntime = 0;
|
||||
int banscore = 0;
|
||||
double pingtime = 0.0;
|
||||
int64_t bytessent = 0;
|
||||
int64_t bytesrecv = 0;
|
||||
int startingheight = 0;
|
||||
int synced_headers = 0;
|
||||
int synced_blocks = 0;
|
||||
bool inbound = false;
|
||||
|
||||
// TLS info
|
||||
std::string tls_cipher;
|
||||
bool tls_verified = false;
|
||||
|
||||
std::string getConnectionTime() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a banned peer
|
||||
*/
|
||||
struct BannedPeer {
|
||||
std::string address;
|
||||
std::string subnet;
|
||||
int64_t banned_until = 0;
|
||||
|
||||
std::string getBannedUntilString() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mining statistics
|
||||
*/
|
||||
struct MiningInfo {
|
||||
bool generate = false;
|
||||
int genproclimit = 0; // -1 means max CPUs
|
||||
double localHashrate = 0.0; // Local hashrate (H/s) from getlocalsolps RPC (RandomX)
|
||||
double networkHashrate = 0.0; // Network hashrate (H/s)
|
||||
int blocks = 0;
|
||||
double difficulty = 0.0;
|
||||
std::string chain;
|
||||
|
||||
double daemon_memory_mb = 0.0; // Daemon process RSS in MB
|
||||
|
||||
// History for chart
|
||||
std::vector<double> hashrate_history; // Last N samples
|
||||
static constexpr int MAX_HISTORY = 300; // 5 minutes at 1s intervals
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Blockchain synchronization info
|
||||
*/
|
||||
struct SyncInfo {
|
||||
int blocks = 0;
|
||||
int headers = 0;
|
||||
double verification_progress = 0.0;
|
||||
bool syncing = false;
|
||||
std::string best_blockhash;
|
||||
|
||||
bool isSynced() const { return !syncing && blocks > 0 && blocks >= headers - 2; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Market/price information
|
||||
*/
|
||||
struct MarketInfo {
|
||||
double price_usd = 0.0;
|
||||
double price_btc = 0.0;
|
||||
double volume_24h = 0.0;
|
||||
double change_24h = 0.0;
|
||||
double market_cap = 0.0;
|
||||
std::string last_updated;
|
||||
|
||||
// Price history for chart
|
||||
std::vector<double> price_history;
|
||||
static constexpr int MAX_HISTORY = 24; // 24 hours
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Pool mining state (from xmrig HTTP API)
|
||||
*/
|
||||
struct PoolMiningState {
|
||||
bool pool_mode = false; // UI toggle: solo vs pool
|
||||
bool xmrig_running = false;
|
||||
std::string pool_url;
|
||||
std::string algo;
|
||||
|
||||
double hashrate_10s = 0;
|
||||
double hashrate_60s = 0;
|
||||
double hashrate_15m = 0;
|
||||
int64_t accepted = 0;
|
||||
int64_t rejected = 0;
|
||||
int64_t uptime_sec = 0;
|
||||
double pool_diff = 0;
|
||||
bool connected = false;
|
||||
|
||||
// Memory/thread usage (bytes for memory)
|
||||
int64_t memory_used = 0;
|
||||
int threads_active = 0;
|
||||
|
||||
// Hashrate history for chart (mirrors MiningInfo::hashrate_history)
|
||||
std::vector<double> hashrate_history;
|
||||
static constexpr int MAX_HISTORY = 60; // 5 minutes at ~5s intervals
|
||||
|
||||
// Recent log lines for the log panel
|
||||
std::vector<std::string> log_lines;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Complete wallet state - all data fetched from daemon
|
||||
*/
|
||||
struct WalletState {
|
||||
// Connection
|
||||
bool connected = false;
|
||||
int daemon_version = 0;
|
||||
std::string daemon_subversion;
|
||||
int protocol_version = 0;
|
||||
int p2p_port = 0;
|
||||
int longestchain = 0;
|
||||
int notarized = 0;
|
||||
|
||||
// Sync status
|
||||
SyncInfo sync;
|
||||
|
||||
// Balances (named to match UI usage)
|
||||
double privateBalance = 0.0; // shielded balance
|
||||
double transparentBalance = 0.0;
|
||||
double totalBalance = 0.0;
|
||||
double unconfirmedBalance = 0.0;
|
||||
|
||||
// Aliases for backward compatibility
|
||||
double& shielded_balance = privateBalance;
|
||||
double& transparent_balance = transparentBalance;
|
||||
double& total_balance = totalBalance;
|
||||
double& unconfirmed_balance = unconfirmedBalance;
|
||||
|
||||
// Addresses - combined list for UI convenience
|
||||
std::vector<AddressInfo> addresses;
|
||||
|
||||
// Also keep separate lists for legacy code
|
||||
std::vector<AddressInfo> z_addresses;
|
||||
std::vector<AddressInfo> t_addresses;
|
||||
|
||||
// Transactions
|
||||
std::vector<TransactionInfo> transactions;
|
||||
|
||||
// Peers
|
||||
std::vector<PeerInfo> peers;
|
||||
std::vector<BannedPeer> bannedPeers;
|
||||
|
||||
// Aliases for banned_peers
|
||||
std::vector<BannedPeer>& banned_peers = bannedPeers;
|
||||
|
||||
// Mining
|
||||
MiningInfo mining;
|
||||
|
||||
// Pool mining (xmrig)
|
||||
PoolMiningState pool_mining;
|
||||
|
||||
// Market
|
||||
MarketInfo market;
|
||||
|
||||
// Wallet encryption state (populated from getwalletinfo)
|
||||
bool encrypted = false; // true if wallet has ever been encrypted
|
||||
bool locked = false; // true if encrypted && unlocked_until <= now
|
||||
int64_t unlocked_until = 0; // 0 = locked, >0 = unix timestamp when auto-lock fires
|
||||
bool encryption_state_known = false; // true once first getwalletinfo response processed
|
||||
|
||||
bool isEncrypted() const { return encrypted; }
|
||||
bool isLocked() const { return encrypted && locked; }
|
||||
bool isUnlocked() const { return encrypted && !locked; }
|
||||
|
||||
// Timestamps for refresh logic
|
||||
int64_t last_balance_update = 0;
|
||||
int64_t last_tx_update = 0;
|
||||
int64_t last_peer_update = 0;
|
||||
int64_t last_mining_update = 0;
|
||||
|
||||
// Helper methods
|
||||
int getAddressCount() const { return addresses.size(); }
|
||||
double getBalanceUSD() const { return totalBalance * market.price_usd; }
|
||||
|
||||
void clear() {
|
||||
connected = false;
|
||||
privateBalance = transparentBalance = totalBalance = 0.0;
|
||||
encrypted = false;
|
||||
locked = false;
|
||||
unlocked_until = 0;
|
||||
encryption_state_known = false;
|
||||
addresses.clear();
|
||||
z_addresses.clear();
|
||||
t_addresses.clear();
|
||||
transactions.clear();
|
||||
peers.clear();
|
||||
bannedPeers.clear();
|
||||
}
|
||||
|
||||
// Rebuild combined addresses list from z/t lists
|
||||
void rebuildAddressList() {
|
||||
addresses.clear();
|
||||
addresses.reserve(z_addresses.size() + t_addresses.size());
|
||||
for (const auto& addr : z_addresses) {
|
||||
addresses.push_back(addr);
|
||||
}
|
||||
for (const auto& addr : t_addresses) {
|
||||
addresses.push_back(addr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dragonx
|
||||
Reference in New Issue
Block a user