mirror of https://github.com/oxen-io/oxen-core.git
216 lines
6.7 KiB
C++
216 lines
6.7 KiB
C++
#include "wallet.hpp"
|
|
|
|
#include <common/hex.h>
|
|
#include <cryptonote_basic/cryptonote_basic.h>
|
|
#include <oxenmq/oxenmq.h>
|
|
#include <spdlog/sinks/rotating_file_sink.h>
|
|
|
|
#include <chrono>
|
|
#include <future>
|
|
#include <iostream>
|
|
#include <oxen/log.hpp>
|
|
#include <sqlitedb/database.hpp>
|
|
#include <thread>
|
|
|
|
#include "block.hpp"
|
|
#include "block_tx.hpp"
|
|
#include "db/walletdb.hpp"
|
|
#include "default_daemon_comms.hpp"
|
|
#include "wallet2½.hpp"
|
|
|
|
namespace wallet {
|
|
static auto logcat = oxen::log::Cat("wallet");
|
|
|
|
fs::path file_path_from_default_datadir(const Config& c, const fs::path& filename) {
|
|
if (filename.string() == ":memory:")
|
|
return filename;
|
|
|
|
auto file_location = fs::absolute(fs::u8path(c.general.datadir));
|
|
if (c.general.nettype != "mainnet" && c.general.append_network_type_to_datadir)
|
|
file_location /= c.general.nettype;
|
|
file_location /= filename;
|
|
|
|
return file_location;
|
|
}
|
|
|
|
Wallet::Wallet(
|
|
std::shared_ptr<oxenmq::OxenMQ> omq,
|
|
std::shared_ptr<Keyring> keyring,
|
|
std::shared_ptr<TransactionConstructor> tx_constructor,
|
|
std::shared_ptr<DaemonComms> daemon_comms,
|
|
std::string_view dbFilename,
|
|
std::string_view dbPassword,
|
|
wallet::Config config_in) :
|
|
omq(omq),
|
|
db{std::make_shared<WalletDB>(
|
|
file_path_from_default_datadir(config_in, dbFilename), dbPassword)},
|
|
keys{std::move(keyring)},
|
|
tx_scanner{keys, db},
|
|
tx_constructor{tx_constructor},
|
|
daemon_comms{daemon_comms},
|
|
omq_server{request_handler},
|
|
config(config_in) {
|
|
if (not omq)
|
|
this->omq = std::make_shared<oxenmq::OxenMQ>();
|
|
if (not daemon_comms)
|
|
this->daemon_comms = std::make_shared<DefaultDaemonComms>(omq, config.daemon);
|
|
if (not tx_constructor)
|
|
this->tx_constructor = std::make_shared<TransactionConstructor>(db, daemon_comms);
|
|
|
|
config.omq_rpc.sockname =
|
|
file_path_from_default_datadir(config, config.omq_rpc.sockname).string();
|
|
omq_server.set_omq(this->omq, config.omq_rpc);
|
|
|
|
db->create_schema();
|
|
if (!keys) {
|
|
const auto db_keys = db->load_keys();
|
|
keys = std::make_shared<wallet::Keyring>(
|
|
db_keys->spend_privkey(),
|
|
db_keys->spend_pubkey(),
|
|
db_keys->view_privkey(),
|
|
db_keys->view_pubkey(),
|
|
nettype);
|
|
tx_scanner.set_keys(keys);
|
|
}
|
|
db->save_keys(keys);
|
|
db->add_address(0, 0, keys->get_main_address());
|
|
last_scan_height = db->last_scan_height();
|
|
scan_target_height = db->scan_target_height();
|
|
}
|
|
|
|
void Wallet::init() {
|
|
keys->expand_subaddresses(
|
|
{config.general.subaddress_lookahead_major, config.general.subaddress_lookahead_minor});
|
|
oxen::log::reset_level(*oxen::logging::parse_level(config.logging.level));
|
|
fs::path log_location = "";
|
|
if (config.logging.save_logs_in_subdirectory)
|
|
log_location /= config.logging.logdir;
|
|
log_location /= config.logging.log_filename;
|
|
|
|
log_location = file_path_from_default_datadir(config, log_location);
|
|
|
|
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
|
|
log_location.string(),
|
|
config.logging.log_file_size_limit,
|
|
config.logging.extra_files,
|
|
config.logging.rotate_on_open);
|
|
|
|
oxen::log::add_sink(std::move(file_sink));
|
|
oxen::log::info(logcat, "Writing logs to {}", log_location.string());
|
|
|
|
oxen::log::info(logcat, "Remote Daemon set to {}", config.daemon.address);
|
|
request_handler.set_wallet(weak_from_this());
|
|
omq->start();
|
|
oxen::log::info(logcat, "OMQ started");
|
|
daemon_comms->set_remote(config.daemon.address);
|
|
daemon_comms->register_wallet(
|
|
*this,
|
|
last_scan_height + 1 /*next needed block*/,
|
|
true /* update sync height */,
|
|
true /* new wallet */);
|
|
oxen::log::info(logcat, "Finished wallet init");
|
|
}
|
|
|
|
Wallet::~Wallet() {}
|
|
|
|
void Wallet::propogate_config() {
|
|
daemon_comms->propogate_config();
|
|
}
|
|
|
|
uint64_t Wallet::get_balance() {
|
|
return db->overall_balance();
|
|
}
|
|
|
|
uint64_t Wallet::get_unlocked_balance() {
|
|
return db->unlocked_balance();
|
|
}
|
|
|
|
cryptonote::account_keys Wallet::export_keys() {
|
|
return keys->export_keys();
|
|
};
|
|
|
|
void Wallet::add_block(const Block& block) {
|
|
oxen::log::trace(logcat, "add block called with block height {}", block.height);
|
|
auto db_tx = db->db_transaction();
|
|
|
|
db->store_block(block);
|
|
|
|
for (const auto& tx : block.transactions) {
|
|
if (auto outputs = tx_scanner.scan_received(tx, block.height, block.timestamp);
|
|
not outputs.empty()) {
|
|
oxen::log::info(
|
|
logcat,
|
|
"outputs: tx.hash {}, block.height {}, outputs {}",
|
|
tx.hash,
|
|
block.height,
|
|
outputs.size());
|
|
db->store_transaction(tx.hash, block.height, outputs);
|
|
}
|
|
|
|
if (auto spends = tx_scanner.scan_spent(tx.tx); not spends.empty()) {
|
|
oxen::log::info(
|
|
logcat,
|
|
"spends: tx.hash {}, block.height {}, spends {}",
|
|
tx.hash,
|
|
block.height,
|
|
spends.size());
|
|
db->store_spends(tx.hash, block.height, spends);
|
|
}
|
|
}
|
|
|
|
db_tx.commit();
|
|
|
|
last_scan_height++;
|
|
}
|
|
|
|
void Wallet::add_blocks(const std::vector<Block>& blocks) {
|
|
if (not running)
|
|
return;
|
|
|
|
if (blocks.size() == 0)
|
|
throw std::runtime_error("no blocks sent to add blocks");
|
|
|
|
if (blocks.front().height > last_scan_height + 1) {
|
|
oxen::log::warning(
|
|
logcat,
|
|
"blocks.front height is greater than last scan height, calling register wallet "
|
|
"with last scan height of {}",
|
|
last_scan_height + 1);
|
|
daemon_comms->register_wallet(*this, last_scan_height + 1 /*next needed block*/, true);
|
|
return;
|
|
}
|
|
|
|
for (const auto& block : blocks) {
|
|
if (block.height == last_scan_height + 1)
|
|
add_block(block);
|
|
}
|
|
daemon_comms->register_wallet(*this, last_scan_height + 1 /*next needed block*/, false);
|
|
}
|
|
|
|
void Wallet::update_top_block_info(int64_t height, const crypto::hash& hash) {
|
|
if (not running)
|
|
return;
|
|
|
|
db->update_top_block_info(height, hash);
|
|
|
|
scan_target_height = height;
|
|
}
|
|
|
|
void Wallet::deregister() {
|
|
running = false;
|
|
auto self = weak_from_this();
|
|
std::promise<void> p;
|
|
auto f = p.get_future();
|
|
daemon_comms->deregister_wallet(*this, p);
|
|
f.wait();
|
|
|
|
/*
|
|
// At this point, only the true "owner" should have a reference
|
|
using namespace std::chrono_literals;
|
|
while (self.use_count() > 1)
|
|
std::this_thread::sleep_for(50ms);
|
|
*/
|
|
}
|
|
|
|
} // namespace wallet
|