You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
neroshop/src/core/wallet/wallet.cpp

1323 lines
70 KiB

#include "wallet.hpp"
#include "../tools/logger.hpp"
#include "../tools/string.hpp"
#include "../tools/process.hpp" // for monerod daemon process
#include "../settings.hpp" // language
#include <nlohmann/json.hpp>
#include <cmath>
#include <filesystem>
neroshop::Wallet::Wallet(WalletType wallet_type) : wallet_type(wallet_type), monero_wallet_obj(nullptr),
process(nullptr), percentage(0.0)
{}
//-------------------------------------------------------
neroshop::WalletNetworkType neroshop::Wallet::network_type(WalletNetworkType::Stagenet);
//-------------------------------------------------------
neroshop::Wallet::~Wallet()
{
if(monero_wallet_obj.get()) {
// remove listener
monero_wallet_obj->remove_listener (*this);//int listener_count = monero_wallet_obj->get_listeners().size();if(listener_count > 0) { std::cout << NEROSHOP_TAG "still need to delete listeners (" << listener_count << ")" << std::endl; }
// close monero wallet
monero_wallet_obj->close(false);
// reset (delete) monero wallet
monero_wallet_obj.reset(); // will call monero_wallet_full destructor
if(!monero_wallet_obj.get()) std::cout << "monero wallet deleted\n"; // just to confirm that the monero wallet has been set to nullptr after deletion
}
// destroy process
if(process.get()) {
process.reset(); // this will call the process destructor which should auto kill the process
if(!process.get()) std::cout << "wallet process deleted\n";
}
// clear password hash
password_hash.clear();
#ifdef NEROSHOP_DEBUG
std::cout << "wallet deleted\n";
#endif
}
//-------------------------------------------------------
//-------------------------------------------------------
// Thought: account creation date could be used as the wallet's restore height
//-------------------------------------------------------
// Reminder: path is the path and name of the wallet without the .keys extension
int neroshop::Wallet::create_random(const std::string& password, const std::string& confirm_pwd, const std::string& path) {
if(confirm_pwd != password) {
neroshop::print("Wallet passwords do not match", 1);
return static_cast<int>(WalletError::PasswordsDoNotMatch);
}
if(monero::monero_wallet_full::wallet_exists(path + ".keys")) {//if(std::filesystem::is_regular_file(path + ".keys")) {
neroshop::print("Wallet file with the same name already exists", 1);
return static_cast<int>(WalletError::AlreadyExists);
}
monero::monero_wallet_config wallet_config_obj;
wallet_config_obj.m_path = path;
wallet_config_obj.m_password = password;
wallet_config_obj.m_network_type = static_cast<monero::monero_network_type>(Wallet::network_type);
nlohmann::json settings = nlohmann::json::parse(neroshop::load_json(), nullptr, false);
if(settings.is_discarded()) {
wallet_config_obj.m_language = "English";
} else {
wallet_config_obj.m_language = settings["language"];
}
monero_wallet_obj = std::unique_ptr<monero_wallet_full>(monero_wallet_full::create_wallet (wallet_config_obj, nullptr));
if(monero_wallet_obj.get()) std::cout << "\033[1;35;49m" << "created wallet \"" << path << ".keys\"" << "\033[0m" << std::endl;
// Sign password to generate a temporary password hash (signature) that will be used to verify password whenever user inactivity is detected or when user attempts to withdraw funds
password_hash = sign_message(password, monero_message_signature_type::SIGN_WITH_SPEND_KEY);
std::cout << "password hash generated: " << password_hash << "\n";
return static_cast<int>(WalletError::Ok);
}
//-------------------------------------------------------
int neroshop::Wallet::create_from_seed(const std::string& seed, const std::string& password, const std::string& confirm_pwd, const std::string& path) {
if(confirm_pwd != password) {
neroshop::print("Wallet passwords do not match", 1);
return static_cast<int>(WalletError::PasswordsDoNotMatch);
}
if(monero::monero_wallet_full::wallet_exists(path + ".keys")) {
neroshop::print("Wallet file with the same name already exists", 1);
return static_cast<int>(WalletError::AlreadyExists);
}
monero::monero_wallet_config wallet_config_obj;
wallet_config_obj.m_path = path;
wallet_config_obj.m_password = password;
wallet_config_obj.m_network_type = static_cast<monero::monero_network_type>(Wallet::network_type);
wallet_config_obj.m_seed = seed;
monero_wallet_obj = std::unique_ptr<monero_wallet_full>(monero_wallet_full::create_wallet (wallet_config_obj, nullptr));
if(!monero_wallet_obj.get()) return static_cast<int>(WalletError::IsNotOpened);
std::cout << "\033[1;35;49m" << "created wallet \"" << path << ".keys\" (from seed)" << "\033[0m" << std::endl;
// Sign password to generate a temporary password hash (signature) that will be used to verify password whenever user inactivity is detected or when user attempts to withdraw funds
password_hash = sign_message(password, monero_message_signature_type::SIGN_WITH_SPEND_KEY);
std::cout << "password hash generated: " << password_hash << "\n";
return static_cast<int>(WalletError::Ok);
}
//-------------------------------------------------------
// To restore a view-only wallet, leave the spend key blank
int neroshop::Wallet::create_from_keys(const std::string& primary_address, const std::string& view_key, const std::string& spend_key, const std::string& password, const std::string &confirm_pwd, const std::string& path) {
if(confirm_pwd != password) {
neroshop::print("Wallet passwords do not match", 1);
return static_cast<int>(WalletError::PasswordsDoNotMatch);
}
if(monero::monero_wallet_full::wallet_exists(path + ".keys")) {
neroshop::print("Wallet file with the same name already exists", 1);
return static_cast<int>(WalletError::AlreadyExists);
}
monero::monero_wallet_config wallet_config_obj;
wallet_config_obj.m_path = path;
wallet_config_obj.m_password = password;
wallet_config_obj.m_network_type = static_cast<monero::monero_network_type>(Wallet::network_type);
wallet_config_obj.m_primary_address = primary_address;
wallet_config_obj.m_private_view_key = view_key;
wallet_config_obj.m_private_spend_key = spend_key;
monero_wallet_obj = std::unique_ptr<monero_wallet_full>(monero_wallet_full::create_wallet (wallet_config_obj, nullptr));
if(!monero_wallet_obj.get()) return static_cast<int>(WalletError::IsNotOpened);
std::cout << "\033[1;35;49m" << "created wallet \"" << path << ".keys\" (from keys)" << "\033[0m" << std::endl;
// Sign password to generate a temporary password hash (signature) that will be used to verify password whenever user inactivity is detected or when user attempts to withdraw funds
password_hash = sign_message(password, monero_message_signature_type::SIGN_WITH_SPEND_KEY);
std::cout << "password hash generated: " << password_hash << "\n";
return static_cast<int>(WalletError::Ok);
}
//-------------------------------------------------------
int neroshop::Wallet::restore_from_seed(const std::string& seed, uint64_t restore_height)
{
monero::monero_wallet_config wallet_config_obj;
wallet_config_obj.m_path = ""; // set path to "" for an in-memory wallet
wallet_config_obj.m_password = "";
wallet_config_obj.m_network_type = static_cast<monero::monero_network_type>(Wallet::network_type);
wallet_config_obj.m_seed = seed;
wallet_config_obj.m_restore_height = (restore_height == 0) ? 1580000 : restore_height;//1580000 (stagenet);3120000 (mainnet)
try {
monero_wallet_obj = std::unique_ptr<monero_wallet_full>(monero_wallet_full::create_wallet (wallet_config_obj, nullptr));
} catch (const std::exception& e) {
std::string error_msg = e.what();
std::cerr << "\033[1;91m" << error_msg << "\033[0m\n";
if(neroshop::string::contains(error_msg, "Invalid mnemonic")) {
return static_cast<int>(WalletError::InvalidMnemonic);
}
}
if(!monero_wallet_obj.get()) return static_cast<int>(WalletError::IsNotOpened);
std::cout << "\033[1;35;49m" << "restored in-memory wallet (from seed)" << "\033[0m" << std::endl;
password_hash = sign_message(wallet_config_obj.m_password.get(), monero_message_signature_type::SIGN_WITH_SPEND_KEY);
return static_cast<int>(WalletError::Ok);
}
//-------------------------------------------------------
int neroshop::Wallet::restore_from_keys(const std::string& primary_address, const std::string& view_key, const std::string& spend_key)
{
// Check validity of primary address
if(!monero_utils::is_valid_address(primary_address, static_cast<monero::monero_network_type>(Wallet::network_type))) {
std::cerr << "\033[1;91mInvalid Monero address\033[0m\n";
return static_cast<int>(WalletError::InvalidAddress);
}
monero::monero_wallet_config wallet_config_obj;
wallet_config_obj.m_path = ""; // set path to "" for an in-memory wallet
wallet_config_obj.m_password = "";
wallet_config_obj.m_network_type = static_cast<monero::monero_network_type>(Wallet::network_type);
wallet_config_obj.m_primary_address = primary_address;
wallet_config_obj.m_private_view_key = view_key;
wallet_config_obj.m_private_spend_key = spend_key;
monero_wallet_obj = std::unique_ptr<monero_wallet_full>(monero_wallet_full::create_wallet (wallet_config_obj, nullptr));
if(!monero_wallet_obj.get()) return static_cast<int>(WalletError::IsNotOpened);
std::cout << "\033[1;35;49m" << "restored in-memory wallet (from keys)" << "\033[0m" << std::endl;
password_hash = sign_message(wallet_config_obj.m_password.get(), monero_message_signature_type::SIGN_WITH_SPEND_KEY);
return static_cast<int>(WalletError::Ok);
}
//-------------------------------------------------------
int neroshop::Wallet::open(const std::string& path, const std::string& password) {
if(!monero::monero_wallet_full::wallet_exists(path)) {
return static_cast<int>(WalletError::DoesNotExist);
}
try {
monero_wallet_obj = std::unique_ptr<monero_wallet_full>(monero::monero_wallet_full::open_wallet(path, password, static_cast<monero::monero_network_type>(Wallet::network_type)));
} catch (const std::exception& e) {
std::string error_msg = e.what();
std::cerr << "\033[1;91m" << error_msg << "\033[0m\n";//tools::error::invalid_password
if(neroshop::string::contains(error_msg, "wallet cannot be opened as")) {
return static_cast<int>(WalletError::BadNetworkType);
} else if(neroshop::string::contains(error_msg, "invalid password")) {
return static_cast<int>(WalletError::WrongPassword);
} else if(neroshop::string::contains(error_msg, "Invalid decimal point specification")) {
return static_cast<int>(WalletError::BadWalletType);
} else {
return static_cast<int>(WalletError::IsOpenedByAnotherProgram);
}
}
if(!monero_wallet_obj.get()) {
return static_cast<int>(WalletError::IsNotOpened);
}
std::cout << "\033[1;35;49m" << "opened wallet \"" << path << "\"\033[0m" << std::endl;
// Sign password to generate a temporary password hash (signature) that will be used to verify password whenever user inactivity is detected or when user attempts to withdraw funds
password_hash = sign_message(password, monero_message_signature_type::SIGN_WITH_SPEND_KEY);
std::cout << "password hash generated: " << password_hash << "\n";
return static_cast<int>(WalletError::Ok);
}
//-------------------------------------------------------
void neroshop::Wallet::close(bool save)
{
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
monero_wallet_obj->close(save);
break;
case WalletType::Wownero:
break;
default:
break;
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::upload(bool open, std::string password) { // opens the wallet file
// open file dialog to retrieve walletfile path
// reminder: use this function for an upload button instead
// then call neroshop::Wallet::open when user presses a "submit" button.
char file[1024];
#ifdef __gnu_linux__
FILE *f = popen("zenity --file-selection --title \"Select Wallet\"", "r");//--filename "/home/${USER}/"
fgets(file, 1024, f);
// consider https://github.com/AndrewBelt/osdialog/blob/master/osdialog_zenity.c
#endif
std::string filename(file); // "wallet.keys" file
filename = filename.substr(0, filename.find(".")); // remove ".keys" extension
if(!monero::monero_wallet_full::wallet_exists(filename + ".keys")) { neroshop::print("wallet not found", 1); return ""; } // check if wallet file is valid (or exists)
if(open == true) neroshop::Wallet::open(filename, password);// will apply ".keys" ext to the wallet file
return std::string(filename + ".keys");
}
//-------------------------------------------------------
bool neroshop::Wallet::verify_password(const std::string& password) {
assert(!this->password_hash.empty());
return verify_message(password, this->password_hash);
}
//-------------------------------------------------------
// refer to https://woodser.github.io/monero-cpp/doxygen/structmonero_1_1monero__tx__config.html
//-------------------------------------------------------
void neroshop::Wallet::transfer(const std::string& address, double amount) {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
if(!monero_wallet_obj.get()->is_synced()) throw std::runtime_error("wallet is not synced with a daemon");
std::packaged_task<void(void)> transfer_task([this, address, amount]() -> void {
// Check if address is valid
if(!monero_utils::is_valid_address(address, monero_wallet_obj->get_network_type())) {
neroshop::print("Monero address is invalid", 1); return;
}
// Convert monero to piconero
uint64_t monero_to_piconero = amount / PICONERO; //std::cout << neroshop::string::precision(amount, 12) << " xmr to piconero: " << monero_to_piconero << "\n";
// TODO: for the 2-of-3 escrow system, take 0.5% of order total in piconeros
// Check if amount is zero or too low
if((amount < PICONERO) || (monero_to_piconero == 0)) {
neroshop::print("Nothing to send (amount is zero)", 1); return;
}
// Check if balance is sufficient
std::cout << "Wallet balance (spendable): " << monero_wallet_obj->get_unlocked_balance() << " (picos)\n";
std::cout << "Amount to send: " << monero_to_piconero << " (picos)\n";
if(monero_wallet_obj->get_unlocked_balance() < monero_to_piconero) {
neroshop::print("Wallet balance is insufficient", 1); return;
}
// Send funds from this wallet to the specified address
monero_tx_config config; // Configures a transaction to send, sweep, or create a payment URI.
config.m_account_index = 0; // withdraw funds from account at index 0
config.m_address = address; // address that will be receiving the funds
config.m_amount = monero_to_piconero;
config.m_relay = true;
// Sweep unlocked balance?
if(monero_wallet_obj->get_unlocked_balance() == monero_to_piconero) {
neroshop::print("Sweeping unlocked balance ...");
config.m_amount = boost::none;
monero_wallet_obj->sweep_unlocked(config);return;
}
// Create the transaction
std::shared_ptr<monero_tx_wallet> sent_tx = monero_wallet_obj->create_tx(config);
bool in_pool = sent_tx->m_in_tx_pool.get(); // true
// Get tx fee and hash
uint64_t fee = sent_tx->m_fee.get(); // "Are you sure you want to send ...?"
std::cout << "Estimated fee: " << (fee * PICONERO) << "\n";
//uint64_t deducted_amount = (monero_to_piconero + fee);
std::string tx_hash = monero_wallet_obj->relay_tx(*sent_tx); // recipient receives notification within 5 seconds
std::cout << "Tx hash: " << tx_hash << "\n";
monero_utils::free(sent_tx);
});
std::future<void> future_result = transfer_task.get_future();
// move the task (function) to a separate thread to prevent blocking of the main thread
std::thread worker(std::move(transfer_task));
worker.detach(); // join may block but detach won't//void transfer_result = future_result.get();
}
//-------------------------------------------------------
void neroshop::Wallet::transfer(const std::vector<std::pair<std::string, double>>& payment_addresses) { // untested
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
if(!monero_wallet_obj.get()->is_synced()) throw std::runtime_error("wallet is not synced with a daemon");
monero_tx_config tx_config = monero_tx_config();
tx_config.m_account_index = 0; // withdraw funds from this account
tx_config.m_relay = false; // create transaction and relay to the network if true
// Calculate the total amount owed
double total_amount = 0.000000000000;
for (const auto& address : payment_addresses) {
total_amount += address.second;
}
std::cout << "Total amount to pay: " << total_amount << " (xmr)\n";
// Check if balance is sufficient
uint64_t total_to_piconero = total_amount / PICONERO;
std::cout << "Wallet balance (spendable): " << monero_wallet_obj->get_unlocked_balance() << " (picos)\n";
std::cout << "Amount to send: " << total_to_piconero << " (picos)\n";
if(monero_wallet_obj->get_unlocked_balance() < total_to_piconero) {
neroshop::print("Wallet balance is insufficient", 1); return;
}
// Add each destination
std::vector<std::shared_ptr<monero_destination>> destinations; // specify the recipients and their amounts
for(const auto& address : payment_addresses) {
// Check if address is valid
if(!monero_utils::is_valid_address(address.first, monero_wallet_obj->get_network_type())) {
neroshop::print(address.first + " is not a valid Monero address", 1);
continue; // skip to the next address
}
// Convert monero to piconero
uint64_t monero_to_piconero = address.second / PICONERO; //std::cout << neroshop::string::precision(address.second, 12) << " xmr to piconero: " << monero_to_piconero << "\n";
destinations.push_back(std::make_shared<monero_destination>(address.first, monero_to_piconero));
// Print address and amount
std::cout << "Address: " << address.first << ", Amount: " << address.second << std::endl;
}
tx_config.m_destinations = destinations;
// Create the transaction, confirm with the user, and relay to the network
std::shared_ptr<monero_tx_wallet> created_tx = monero_wallet_obj->create_tx(tx_config);
uint64_t fee = created_tx->m_fee.get(); // "Are you sure you want to send ...?"
monero_wallet_obj->relay_tx(*created_tx); // recipient receives notification within 5 seconds
monero_utils::free(created_tx);
}
//-------------------------------------------------------
std::string neroshop::Wallet::sign_message(const std::string& message, monero_message_signature_type signature_type) const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->sign_message(message, signature_type, 0, 0);
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
bool neroshop::Wallet::verify_message(const std::string& message, const std::string& signature) const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->verify_message(message, monero_wallet_obj.get()->get_primary_address(), signature).m_is_good;
case WalletType::Wownero:
return false;
default:
return false;
}
}
//-------------------------------------------------------
//-------------------------------------------------------
monero::monero_subaddress neroshop::Wallet::create_subaddress(unsigned int account_idx, const std::string & label) const {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
monero_subaddress subaddress = monero_wallet_obj->create_subaddress(account_idx, label);
// we have to make sure that each newly generated subaddress is unique (meaning it has never been used)
if(subaddress.m_is_used.get()) {
throw std::runtime_error("subaddress " + subaddress.m_address.get() + " was previously used. Is your wallet node properly synced?");////neroshop::print("subaddress " + subaddress.m_address.get() + " was previously used");
}
return subaddress;
}
//-------------------------------------------------------
//-------------------------------------------------------
void neroshop::Wallet::set_wallet_type(WalletType wallet_type) {
this->wallet_type = wallet_type;
}
//-------------------------------------------------------
// NOTE: It is IMPOSSIBLE to change the network type of a pre-existing monero wallet, but it can be set before its creation
void neroshop::Wallet::set_network_type(WalletNetworkType network_type) {
auto current_network_type = get_wallet_network_type();
if(current_network_type == network_type) {
return;
}
auto network_type_str = get_wallet_network_type_as_string();
switch(wallet_type) {
case WalletType::Monero:
if(monero_wallet_obj.get()) throw std::runtime_error("cannot change " + network_type_str + " wallet network type");
break;
case WalletType::Wownero:
break;
default:
break;
}
Wallet::network_type = network_type;
}
//-------------------------------------------------------
void neroshop::Wallet::set_network_type_by_string(const std::string& network_type) {
if(network_type == "mainnet") set_network_type(WalletNetworkType::Mainnet);
if(network_type == "testnet") set_network_type(WalletNetworkType::Testnet);
if(network_type == "stagenet") set_network_type(WalletNetworkType::Stagenet);
std::cout << "Monero network type has been set to: " << network_type << std::endl;
}
//-------------------------------------------------------
//-------------------------------------------------------
std::string neroshop::Wallet::address_new() const {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
// Create a subaddress within an account (account 0 to be specific).
monero_subaddress subaddress = monero_wallet_obj->create_subaddress(0);
#ifdef NEROSHOP_DEBUG
std::cout << "address_new: " << subaddress.m_address.get() << " (account_idx: " << subaddress.m_account_index.get() << ", subaddress_idx: " << subaddress.m_index.get() << ")" << std::endl;
#endif
//if subaddress has already been used
if(subaddress.m_is_used.get()) { std::cout << "subaddress " << subaddress.m_address.get() << " already in use!" << std::endl; return ""; }
return subaddress.m_address.get();// return the newly created subaddress
//subaddress_list.push_back(subaddress.m_address.get());
}
//-------------------------------------------------------
unsigned int neroshop::Wallet::address_book_add(const std::string& address, std::string description) {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->add_address_book_entry(address, description);
}
//-------------------------------------------------------
void neroshop::Wallet::address_book_delete(unsigned int index) {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
monero_wallet_obj->delete_address_book_entry(index);
}
//-------------------------------------------------------
std::vector<monero::monero_subaddress> neroshop::Wallet::get_addresses_all(unsigned int account_idx) {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
if(!monero_wallet_obj.get()->is_synced()) throw std::runtime_error("wallet is not synced with a daemon");
std::vector<monero::monero_subaddress> addresses = {};
std::vector<unsigned int> subaddress_indices = {};
for(int i = 0; i < monero_wallet_obj->get_account(account_idx, true).m_subaddresses.size(); i++) {
addresses = monero_wallet_obj->get_subaddresses(account_idx, subaddress_indices);
std::cout << addresses[i].m_index.get() << " " << addresses[i].m_address.get() << (addresses[i].m_is_used.get() ? " (used)" : "") << std::endl;
}
return addresses;
}
std::vector<monero::monero_subaddress> neroshop::Wallet::get_addresses_used(unsigned int account_idx) {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
if(!monero_wallet_obj.get()->is_synced()) throw std::runtime_error("wallet is not synced with a daemon");
std::vector<monero::monero_subaddress> addresses = {};
std::vector<unsigned int> subaddress_indices = {};
for(int i = 0; i < monero_wallet_obj->get_account(account_idx, true).m_subaddresses.size(); i++) {
addresses = monero_wallet_obj->get_subaddresses(account_idx, subaddress_indices);
if(addresses[i].m_is_used.get()) {
std::cout << addresses[i].m_index.get() << " " << addresses[i].m_address.get() << std::endl;
}
}
return addresses;
}
std::vector<monero::monero_subaddress> neroshop::Wallet::get_addresses_unused(unsigned int account_idx) {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
if(!monero_wallet_obj.get()->is_synced()) throw std::runtime_error("wallet is not synced with a daemon");
std::vector<monero::monero_subaddress> addresses = {};
std::vector<unsigned int> subaddress_indices = {};
for(int i = 0; i < monero_wallet_obj->get_account(account_idx, true).m_subaddresses.size(); i++) {
std::vector<monero::monero_subaddress> unused_addresses = monero_wallet_obj->get_subaddresses(account_idx, subaddress_indices);
if(!unused_addresses[i].m_is_used.get()) {
std::cout << ((std::find(recent_address_list.begin(), recent_address_list.end(), unused_addresses[i].m_address.get()) != recent_address_list.end()) ? "\033[91m" : "\033[0m") << unused_addresses[i].m_index.get() << " " << unused_addresses[i].m_address.get() << "\033[0m" << std::endl;// if subaddress is found in recent_address_list, mark it red
// skip any recently used subaddress (to ensure the uniqueness of a subaddress)
if(std::find(recent_address_list.begin(), recent_address_list.end(), unused_addresses[i].m_address.get()) != recent_address_list.end())
continue;
addresses.push_back(unused_addresses[i]); // store unused addresses in vector
}
}
return addresses;
}
//-------------------------------------------------------
std::vector<std::shared_ptr<monero_transfer>> neroshop::Wallet::get_transfers() {
std::promise<std::vector<std::shared_ptr<monero_transfer>>> my_promise;
auto async_future = std::async(std::launch::async, [&my_promise, this]() -> void/*std::vector<std::shared_ptr<monero_transfer>>*/ {
monero_transfer_query transfer_query; // optional
auto transfers = monero_wallet_obj->get_transfers(transfer_query);
my_promise.set_value(transfers);
////return transfers;
});
return my_promise.get_future().get(); // return value inside promise
////return async_future.get();//.wait() for function that returns void
}
//-------------------------------------------------------
//-------------------------------------------------------
std::string neroshop::Wallet::generate_uri(const std::string& payment_address, double amount, const std::string& description, const std::string& recipient) {
bool has_amount = false;
bool has_recipient = false;
std::string monero_uri = "monero:" + payment_address;
if(amount > std::stod(neroshop::string::precision("0.000000000000", 12))) {
//std::cout << "amount (" << neroshop::string::precision(amount, 12) << ") is greater than 0.000000000000\n";
has_amount = true;
monero_uri = monero_uri + "?tx_amount=" + neroshop::string::precision(amount, 12);
}
if(!recipient.empty()) {
has_recipient = true;
std::string recipient_name = neroshop::string::swap_all(recipient, " ", "%20");
monero_uri = monero_uri + ((has_amount) ? "&recipient_name=" : "?recipient_name=") + recipient_name;
}
if(!description.empty()) {
std::string tx_description = neroshop::string::swap_all(description, " ", "%20");
monero_uri = monero_uri + ((has_amount || has_recipient) ? "&tx_description=" : "?tx_description=") + tx_description;
}
return monero_uri;
}
//-------------------------------------------------------
//-------------------------------------------------------
//-------------------------------------------------------
//-------------------------------------------------------
// override
//-------------------------------------------------------
void neroshop::Wallet::on_sync_progress(uint64_t height, uint64_t start_height, uint64_t end_height, double percent_done, const std::string& message) {
// all of this takes place in a separate thread
// if monero_wallet is already synced, skip this function (this function keeps spamming throughout the entire app session -.-)
if(monero_wallet_obj.get()->is_synced()) return;
std::cout << "Node Sync Thread ID: " << std::this_thread::get_id() << std::endl;
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::lock_guard<std::mutex> lock(wallet_data_mutex);
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now); // current time
std::stringstream ss;
ss << std::put_time(std::localtime(&in_time_t), "[%Y-%m-%d %l:%M:%S %p]");
std::string date = ss.str();
std::cout << "\033[0;35;49m" << date << " \033[1;33;49m" << "**********************************************************************" << "\033[0m" << std::endl;
std::cout << "\033[0;35;49m" << date << " \033[1;33;49m" << "Synced " << height << "/" << end_height;//<< "\033[0m" << std::endl;
unsigned int blocks_to_sync = end_height - height;
std::cout << "\033[1;33;49m" << " [Your node is " << blocks_to_sync << " block(s) behind]" << "\033[0m" << std::endl;//" blocks (" << blocks_to_sync / 60? << " minutes) behind]" << "\033[0m" << std::endl; // 1 block = 1 minute
std::cout << "\033[0;35;49m" << date << " \033[1;33;49m" << message << ": " << (percent_done * 100) << "% (actual: " << percent_done << ")\033[0m" << std::endl;
if((percent_done * 100) == 100) std::cout << "\033[0;35;49m" << date << " \033[1;32;49m" << "SYNCHRONIZATION DONE" << std::endl;
std::cout << "\033[0;35;49m" << date << std::endl;
std::cout << "\033[0;35;49m" << date << " \033[1;33;49m" << "**********************************************************************" << "\033[0m" << std::endl;
this->percentage = percent_done;
this->height = height;
this->start_height = start_height;
this->end_height = end_height;
this->message = message;
if(percent_done == 1) this->height = end_height;
}
//-------------------------------------------------------
void neroshop::Wallet::on_output_received(const monero_output_wallet& output) {
// if wallet is not fully synced with a daemon, skip this function
if(!monero_wallet_obj.get()) return;
if(!monero_wallet_obj.get()->is_synced()) return;
uint64_t amount = output.m_amount.get();
std::string tx_hash = output.m_tx->m_hash.get();
bool is_confirmed = output.m_tx->m_is_confirmed.get(); // unlocked_balance (can be spent)
bool is_locked = std::dynamic_pointer_cast<monero_tx_wallet>(output.m_tx)->m_is_locked.get(); // balance (locked - still processing)
int account_index = output.m_account_index.get(); // should always be 0 (default)
int subaddress_index = output.m_subaddress_index.get();
// get balance (actual)
double balance = (amount * PICONERO);
// get the subaddress
std::string subaddress = get_address(subaddress_index);
// you've received some xmr but it may be unspendable for the meantime
if(is_locked) {
std::cout << "\033[1;92;49m" << "TX incoming (amount: " << std::fixed << std::setprecision(12) << balance << std::fixed << std::setprecision(2) << " xmr, txid: " << tx_hash << ", account_idx: " << account_index << ", subaddress_idx: " << subaddress_index << ")" << "\033[0m" << std::endl;
// store subaddress in recent_address_list (if not yet added)
if(std::find(recent_address_list.begin(), recent_address_list.end(), subaddress) == recent_address_list.end())
recent_address_list.push_back(subaddress);//std::cout << "recently used addresses (count): " << recent_address_list.size() << std::endl;
}
// your xmr can now be spent freely :)
// at this point, any recently used subaddress will be removed from vector returned by Wallet::addresses_unused() (this is the final confirmation) - sometimes this message shows twice
if(is_confirmed) {
std::cout << "\033[1;32;49m" << "You have received " << std::fixed << std::setprecision(12) << balance << std::fixed << std::setprecision(2) << " xmr " << "(txid: " << tx_hash << ", account_idx: " << account_index << ", subaddress_idx: " << subaddress_index << ")" << "\033[0m" << std::endl;
}
}
//-------------------------------------------------------
void neroshop::Wallet::on_balances_changed(uint64_t new_balance, uint64_t new_unlocked_balance) {
// if wallet is not fully synced with a daemon, skip this function
if(!monero_wallet_obj.get()) return;
if(!monero_wallet_obj.get()->is_synced()) return;
double balance = (new_balance * PICONERO);
double unlocked_balance = (new_unlocked_balance * PICONERO);
// if total balance is still locked, display it
// but if total balance is fully unlocked, then you already have the balance so no need to keep displaying it
if(unlocked_balance != balance) {
// balance updated (unlocked)
std::cout << std::fixed << std::setprecision(12) << "\033[1;33;49m" << "balance: " << "\033[0m" << balance << std::fixed << std::setprecision(2);// << std::endl;
std::cout << std::fixed << std::setprecision(12) << " (unlocked_balance: " << unlocked_balance << std::fixed << std::setprecision(2) << ")" << std::endl;
}
if(unlocked_balance == balance) {
std::cout << "\033[1;35;49m" << "Balance is now fully unlocked" << "\033[0m" << std::endl;// Your full balance can be spent now
//std::cout << std::fixed << std::setprecision(12) << "balance: " << balance << std::fixed << std::setprecision(2);// << std::endl;
//std::cout << std::fixed << std::setprecision(12) << " (unlocked_balance: " << unlocked_balance << std::fixed << std::setprecision(2) << ")" << std::endl;//Japanese violet: 91, 50, 86
}
}
//-------------------------------------------------------
//-------------------------------------------------------
//-------------------------------------------------------
// daemon
//-------------------------------------------------------
// open the daemon before opening the wallet
void neroshop::Wallet::daemon_open(const std::string& daemon_dir, bool confirm_external_bind, bool restricted_rpc, std::string data_dir, unsigned int restore_height)
{
// Todo: use QProcess to launch monerod
// Check if there is another monerod process running in the background first
int monerod = Process::get_process_by_name("monerod");//(argv[1]);//cout << "pid: " << monerod << endl;
if(monerod != -1) { std::cout << "\033[1;90;49m" << "monerod is running (ID:" << monerod << ")\033[0m" << std::endl; return; } // daemon that was previously running in the background // exit function
// search for monero daemon (that is packaged with neroshop executable)
////std::string daemon_dir = static_cast<std::string>(std::filesystem::current_path()) + "/external/monero-cpp/external/monero-project/build/release/bin/monerod";
// If neroshop's built-in Monero daemon does not exist, exit function
if(!std::filesystem::is_regular_file(daemon_dir)) { std::cout << "monerod not found. Please set the path to monerod or use a remote node instead" << "\n"; return; }
// if neroshop's daemon exists and remote is set to false //if(File::exists(daemon_dir) && remote == false) {
// if no other daemon is running, then use daemon that comes packaged with neroshop executable
std::string ip = (confirm_external_bind == true) ? "0.0.0.0" : "127.0.0.1";
std::cout << "\033[1;90;49m" << "daemon found: \"" << daemon_dir << "\"" << "\033[0m" << std::endl;
std::string program = daemon_dir;
std::string args = (" --data-dir=" + data_dir) + (" --rpc-bind-ip=" + ip) + (" --rpc-bind-port=38081");
if(confirm_external_bind == true) { args = args + " --confirm-external-bind"; }
if(confirm_external_bind == true && restricted_rpc == true) { args = args + " --restricted-rpc"; }
auto network_type_str = get_wallet_network_type_as_string();
if(neroshop::string::lower(network_type_str) != "mainnet") args = args + (" --" + neroshop::string::lower(network_type_str));
args = args + (" --detach"); // https://monero.stackexchange.com/questions/12005/what-is-the-difference-between-monerod-detach-and-monerod-non-interactive
std::cout << "\033[1;95;49m" << "$ " << daemon_dir + args << "\033[0m" << std::endl;
// start the daemon (monerod) as a new process on launch
process = std::unique_ptr<Process>(new Process(daemon_dir, args));
monerod = process->get_handle();
std::cout << "\033[1;90;49m" << "started monerod (ID:" << monerod << ")\033[0m" << std::endl;
}
//-------------------------------------------------------
bool neroshop::Wallet::daemon_connect_local(const std::string& username, const std::string& password) { // connect to a running daemon (node)
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
// connect to the daemon
monero_wallet_obj->set_daemon_connection(monero_rpc_connection(std::string("http://127.0.0.1:38081"), username, password));
std::cout << "\033[1;90;49m" << "waiting for daemon" << "\033[0m" << std::endl;
bool connected = false; bool synced = false;
while(!connected) {
if(monero_wallet_obj.get()->is_connected_to_daemon()) {
connected = true;
// once connected to daemon, listen to sync progress (use this only on a detached daemon)
std::cout << "\033[1;90;49m" << "sync in progress .." << "\033[0m" << std::endl;
// it is not safe to connect to a daemon that has not fully synced, so listen to the sync progress before attempting to do anything else
// wallet height is 938308 (2021-10-07) - will store all txs/balances but takes longer to sync //std::cout << "syncing from wallet height: " << monero_wallet_obj->get_height() << std::endl;
// daemon height is 1070393 (most recent) - will only store the most recent txs/balances and syncs almost instantly //std::cout << "syncing from daemon height: " << monero_wallet_obj->get_daemon_height() << std::endl;
// if height is wallet's height then it will sync from the time of the wallet's creation date to the current date which takes longer to sync
// if height is daemon height, it will sync instantly but balances will not be updated and you will not be able to generate unique addresses
// if height is zero, then it will sync from 80% but will still take long to sync
std::packaged_task<void(void)> sync_job([this]() {
// monero_wallet_obj->get_daemon_height() is the height that the wallet's daemon is currently synced to
monero_wallet_obj->sync(monero_wallet_obj->get_restore_height()/*monero_wallet_obj->get_daemon_height()*/, *this);////monero_wallet_obj->sync(0, *this);// 0 = start_height is the start height to sync from (ignored if less than last processed block) //(sync_listener);//monero_sync_result sync_result = monero_wallet_obj->sync(sync_listener); // synchronize the wallet with the daemon as a one-time synchronous process//if(sync_result.m_received_money) {neroshop::print(std::string("blocks fetched: ") + std::to_string(sync_result.m_num_blocks_fetched));neroshop::print("you have received money");}
// continue syncing in order to receive tx notifications
std::cout << "\033[1;90;49m" << "starting background sync" << "\033[0m" << std::endl;
monero_wallet_obj->start_syncing(5000); // begin syncing the wallet constantly in the background (every 5 seconds)
// check if wallet's daemon is synced with the network
if(monero_wallet_obj.get()->is_daemon_synced()) {
//synced = true;
std::cout << "\033[1;90;49m" << "daemon is now fully synced with the network" << "\033[0m" << std::endl;
// add new listener when done syncing
monero_wallet_obj->add_listener(*this); // add wallet_listener
}
});
std::future<void> job_value = sync_job.get_future();
// move the task (function) to a separate thread
std::thread worker(std::move(sync_job));
worker.detach();//worker.join();
// todo: store wallet restore height in database on registration
}
}
return synced;
}
//-------------------------------------------------------
void neroshop::Wallet::daemon_connect_remote(const std::string& ip, const std::string& port, const std::string& username, const std::string& password, const monero_wallet_listener* listener) {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
monero_wallet_obj->set_daemon_connection(monero_rpc_connection(std::string("http://" + ip + ":" + port)));//, username, password));
if(monero_wallet_obj.get()->is_connected_to_daemon()) {
std::cout << "\033[1;90;49m" << "connected to daemon" << "\033[0m" << std::endl;
std::packaged_task<void(void)> sync_job([this, listener]() {
std::cout << "\033[1;90;49m" << "sync in progress ..." << "\033[0m" << std::endl;
monero_wallet_obj->sync(monero_wallet_obj->get_restore_height(), (listener != nullptr) ? *const_cast<monero_wallet_listener*>(listener) : *this);//*this); // a start_height of 0 is ignored // get_restore_height() is the height of the first block that the wallet scans// get_daemon_height() is the height that the wallet's daemon is currently synced to (will sync instantly but will not guarantee unique subaddress generation)
// begin syncing the wallet constantly in the background (every 5 seconds) in order to receive tx notifications
monero_wallet_obj->start_syncing(5000);
// check if wallet's daemon is synced with the network
if(monero_wallet_obj.get()->is_daemon_synced()) {////if(monero_wallet_obj->get_daemon_height() == monero_wallet_obj->get_daemon_max_peer_height()) {
std::cout << "\033[1;90;49m" << "daemon is now fully synced with the network" << "\033[0m" << std::endl;
// add new wallet_listener when done syncing
monero_wallet_obj->add_listener((listener != nullptr) ? *const_cast<monero_wallet_listener*>(listener) : *this);//(*this);
}
});
std::future<void> job_value = sync_job.get_future();
// move the task (function) to a separate thread to prevent blocking of the main thread
std::thread node_worker(std::move(sync_job));
node_worker.detach();
// todo: store wallet restore height in database on registration
}
}
//-------------------------------------------------------
void neroshop::Wallet::daemon_close() {
if(!process) throw std::runtime_error("process is not initialized");
#ifdef __gnu_linux__
int monerod = process->get_handle();
if(monerod != -1) kill(static_cast<pid_t>(monerod), SIGTERM);
#endif
//delete process;
}
//-------------------------------------------------------
//-------------------------------------------------------
void neroshop::Wallet::wallet_info() {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
// wallet must be synced with a daemon in order to get the *most recent* wallet info
if(!monero_wallet_obj.get()->is_synced()) throw std::runtime_error("wallet is not synced with a daemon");
// get primary_address, balance, unlocked_balance, etc.
std::string restored_primary = monero_wallet_obj->get_primary_address(); // same as: monero_wallet_obj->get_account(0, true).m_primary_address.get()
std::cout << "primary_address: " << restored_primary << std::endl;
uint64_t balance_raw = monero_wallet_obj->get_balance(); // can specify account and subaddress indices
double balance = (double)balance_raw * PICONERO;
std::cout << std::fixed << std::setprecision(12) << "balance: " << balance << std::fixed << std::setprecision(2) << std::endl;
// account ---------------------------------------------------------------
// account_0 is the default/main account where the primary address derives from
monero_account account = monero_wallet_obj->get_account(0, true); // get account with subaddresses
uint64_t unlocked_balance_raw = account.m_unlocked_balance.get();
double unlocked_balance = (double)unlocked_balance_raw * PICONERO;
std::cout << std::fixed << std::setprecision(12) << "unlocked_balance: " << unlocked_balance << std::fixed << std::setprecision(2) << std::endl; // uint64_t
// view and spend keys ----------------------------------------------------
std::cout << "view_key: " << get_view_keys().first << std::endl;
std::cout << "spend_key: " << "(secret)" << std::endl; // since this is intended to be a view-only wallet
// subaddress -------------------------------------------------------------
// generate new subaddresses on the default account 0
std::cout << std::endl;
/*for(int i = 0; i < 10; i++) wallet->address_new();
// get number of subaddresses
std::cout << std::endl;
std::cout << "address count: " << wallet->get_address_count() << std::endl;
std::string last_subaddress = wallet->get_last_subaddress();
std::cout << "last subaddress created: " << last_subaddress << std::endl;*/
}
//-------------------------------------------------------
//-------------------------------------------------------
// setters
//-------------------------------------------------------
void neroshop::Wallet::set_tx_note(const std::string& txid, const std::string& tx_note) {}
//-------------------------------------------------------
//-------------------------------------------------------
// getters
//-------------------------------------------------------
neroshop::WalletType neroshop::Wallet::get_wallet_type() const {
return wallet_type;
}
//-------------------------------------------------------
neroshop::WalletNetworkType neroshop::Wallet::get_wallet_network_type() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) return network_type;
return static_cast<WalletNetworkType>(monero_wallet_obj->get_network_type());
case WalletType::Wownero:
return network_type;
default:
return network_type;
}
}
//-------------------------------------------------------
neroshop::WalletNetworkType neroshop::Wallet::get_network_type() {
return network_type;
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_wallet_network_type_as_string() const {
auto wallet_network_type = get_wallet_network_type();
switch(wallet_network_type) {
case WalletNetworkType::Mainnet: return "mainnet"; // 0
case WalletNetworkType::Testnet: return "testnet"; // 1
case WalletNetworkType::Stagenet: return "stagenet"; // 2
default: return "mainnet";
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_network_type_as_string() {
switch(network_type) {
case WalletNetworkType::Mainnet: return "mainnet"; // 0
case WalletNetworkType::Testnet: return "testnet"; // 1
case WalletNetworkType::Stagenet: return "stagenet"; // 2
default: return "mainnet";
}
}
//-------------------------------------------------------
//-------------------------------------------------------
std::string neroshop::Wallet::get_network_port() const {
auto wallet_network_type = get_wallet_network_type();
switch(wallet_type) {
case WalletType::Monero:
return WalletNetworkPortMap[wallet_network_type][0];
case WalletType::Wownero:
return WalletNetworkPortMap[wallet_network_type][2];
default:
return "";
}
}
//-------------------------------------------------------
//-------------------------------------------------------
double neroshop::Wallet::get_sync_percentage() const {
std::lock_guard<std::mutex> lock(wallet_data_mutex);
return percentage;
}
//-------------------------------------------------------
unsigned long long neroshop::Wallet::get_sync_height() const {
std::lock_guard<std::mutex> lock(wallet_data_mutex);
return height;
}
//-------------------------------------------------------
unsigned long long neroshop::Wallet::get_sync_start_height() const {
std::lock_guard<std::mutex> lock(wallet_data_mutex);
return start_height;
}
//-------------------------------------------------------
unsigned long long neroshop::Wallet::get_sync_end_height() const {
std::lock_guard<std::mutex> lock(wallet_data_mutex);
return end_height;
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_sync_message() const {
std::lock_guard<std::mutex> lock(wallet_data_mutex);
return message;
}
//-------------------------------------------------------
//-------------------------------------------------------
std::string neroshop::Wallet::get_primary_address() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_primary_address(); // same as: monero_wallet_obj->get_account(0, true).m_primary_address.get();
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_address(unsigned int index) const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_account(0, true).m_subaddresses[index].m_address.get(); // account_idx is 0
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
unsigned int neroshop::Wallet::get_address_count() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_account(0, true).m_subaddresses.size(); // subaddresses will be created on account_idx 0
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//-------------------------------------------------------
//-------------------------------------------------------
uint64_t neroshop::Wallet::get_balance_raw() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_balance(); // get wallet balance
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//-------------------------------------------------------
uint64_t neroshop::Wallet::get_balance_raw(unsigned int account_index) const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_balance(account_index); // get balance from account
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//------------------------------------------------------- for some reason, "account.m_subaddresses[i].m_address.get()" fails, but "monero_wallet_obj->get_account(0, true).m_subaddresses.size()" usually succeeds ...-> https://stackoverflow.com/questions/68733975/difficult-to-understand-runtime-error-this-is-initialized-failure-in-boost#comment121472560_68733975
uint64_t neroshop::Wallet::get_balance_raw(unsigned int account_index, unsigned int subaddress_index) const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_balance(account_index, subaddress_index); // get balance from subaddress
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//-------------------------------------------------------
uint64_t neroshop::Wallet::get_unlocked_balance_raw() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_unlocked_balance(); // get wallet unlocked balance
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//-------------------------------------------------------
uint64_t neroshop::Wallet::get_unlocked_balance_raw(unsigned int account_index) const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_unlocked_balance(account_index); // get unlocked balance from account
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//-------------------------------------------------------
uint64_t neroshop::Wallet::get_unlocked_balance_raw(unsigned int account_index, unsigned int subaddress_index) const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_unlocked_balance(account_index, subaddress_index); // get unlocked balance from subaddress
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//-------------------------------------------------------
//-------------------------------------------------------
double neroshop::Wallet::get_balance() const {
switch(wallet_type) {
case WalletType::Monero:
return get_balance_raw() * PICONERO;
case WalletType::Wownero:
return get_balance_raw() * WOWOSHI;
default:
return 0;
}
}
//-------------------------------------------------------
double neroshop::Wallet::get_balance(unsigned int account_index) const {
switch(wallet_type) {
case WalletType::Monero:
return get_balance_raw(account_index) * PICONERO;
case WalletType::Wownero:
return get_balance_raw(account_index) * WOWOSHI;
default:
return 0;
}
}
//-------------------------------------------------------
double neroshop::Wallet::get_balance(unsigned int account_index, unsigned int subaddress_index) const {
switch(wallet_type) {
case WalletType::Monero:
return get_balance_raw(account_index, subaddress_index) * PICONERO;
case WalletType::Wownero:
return get_balance_raw(account_index, subaddress_index) * WOWOSHI;
default:
return 0;
}
}
//-------------------------------------------------------
double neroshop::Wallet::get_unlocked_balance() const {
switch(wallet_type) {
case WalletType::Monero:
return get_unlocked_balance_raw() * PICONERO;
case WalletType::Wownero:
return get_unlocked_balance_raw() * WOWOSHI;
default:
return 0;
}
}
//-------------------------------------------------------
double neroshop::Wallet::get_unlocked_balance(unsigned int account_index) const {
switch(wallet_type) {
case WalletType::Monero:
return get_unlocked_balance_raw(account_index) * PICONERO;
case WalletType::Wownero:
return get_unlocked_balance_raw(account_index) * WOWOSHI;
default:
return 0;
}
}
//-------------------------------------------------------
double neroshop::Wallet::get_unlocked_balance(unsigned int account_index, unsigned int subaddress_index) const {
switch(wallet_type) {
case WalletType::Monero:
return get_unlocked_balance_raw(account_index, subaddress_index) * PICONERO;
case WalletType::Wownero:
return get_unlocked_balance_raw(account_index, subaddress_index) * WOWOSHI;
default:
return 0;
}
}
//-------------------------------------------------------
std::vector<std::string> neroshop::Wallet::get_transactions() const {
std::vector<std::string> txs_list;
/* ****
// query incoming transfers to account 0
/*monero_transfer_query transfer_query;
transfer_query.m_is_incoming = true;
transfer_query.m_account_index = 0;
vector<shared_ptr<monero_transfer>> transfers = monero_wallet_obj->get_transfers(transfer_query);
// query unspent outputs
monero_output_query output_query;
output_query.m_is_spent = false;
vector<shared_ptr<monero_output_wallet>> outputs = wallet_restored->get_outputs(output_query);
// query a transaction by hash
monero_tx_query tx_query;
tx_query.m_hash = "314a0f1375db31cea4dac4e0a51514a6282b43792269b3660166d4d2b46437ca";
shared_ptr<monero_tx_wallet> tx = wallet_restored->get_txs(tx_query)[0];
for (const shared_ptr<monero_transfer> transfer : tx->get_transfers()) {
bool is_incoming = transfer->is_incoming().get();
uint64_t in_amount = transfer->m_amount.get();
int account_index = transfer->m_account_index.get();
}
*/
return txs_list;
}
//-------------------------------------------------------
unsigned int neroshop::Wallet::get_transactions_count() const {return 0;}
//-------------------------------------------------------
//-------------------------------------------------------
//-------------------------------------------------------
//-------------------------------------------------------
//-------------------------------------------------------
//-------------------------------------------------------
std::string neroshop::Wallet::get_last_subaddress() const
{
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
// unless wallet is synced to a daemon, you will not be able to get the *most recent* last subaddress created
if(!monero_wallet_obj.get()->is_synced()) throw std::runtime_error("wallet is not synced with a daemon");
unsigned int last = monero_wallet_obj->get_account(0, true).m_subaddresses.size() - 1;
return monero_wallet_obj->get_account(0, true).m_subaddresses[last].m_address.get();
}
//-------------------------------------------------------
//-------------------------------------------------------
std::string neroshop::Wallet::get_private_view_key() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_private_view_key();
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_public_view_key() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_public_view_key();
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
std::pair<std::string, std::string> neroshop::Wallet::get_view_keys() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return std::make_pair(monero_wallet_obj->get_private_view_key(), monero_wallet_obj->get_public_view_key());
case WalletType::Wownero:
return std::make_pair("", "");
default:
return std::make_pair("", "");
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_private_spend_key() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_private_spend_key();
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_public_spend_key() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_public_spend_key();
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
std::pair<std::string, std::string> neroshop::Wallet::get_spend_keys() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return std::make_pair(monero_wallet_obj->get_private_spend_key(), monero_wallet_obj->get_public_spend_key());
case WalletType::Wownero:
return std::make_pair("", "");
default:
return std::make_pair("", "");
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_seed() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_seed();
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_seed_language() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_seed_language();
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
std::vector<std::string> neroshop::Wallet::get_seed_languages() const {
switch(wallet_type) {
case WalletType::Monero:
return monero::monero_wallet_full::get_seed_languages();
case WalletType::Wownero:
return {};
default:
return {};
}
}
//-------------------------------------------------------
//-------------------------------------------------------
std::string neroshop::Wallet::get_path() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->monero_wallet_full::get_path(); // returns the path of this wallet's file on disk (without the .keys ext)
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_type() const {
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return "";
}
//-------------------------------------------------------
unsigned int neroshop::Wallet::get_daemon_height() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_daemon_height();
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//-------------------------------------------------------
unsigned int neroshop::Wallet::get_height() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_height();
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//-------------------------------------------------------
unsigned int neroshop::Wallet::get_height_by_date(int year, int month, int day) const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->get_height_by_date(year, month, day);
case WalletType::Wownero:
return 0;
default:
return 0;
}
}
//-------------------------------------------------------
std::string neroshop::Wallet::get_version() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return "";//monero_version monero::monero_wallet_full::get_version();
break;
case WalletType::Wownero:
return "";
default:
return "";
}
}
//-------------------------------------------------------
//-------------------------------------------------------
void * neroshop::Wallet::get_handle() const {
switch(wallet_type) {
case WalletType::Monero:
return monero_wallet_obj.get();
case WalletType::Wownero:
return nullptr;//wownero_wallet_obj.get();
default:
return nullptr;
}
}
//-------------------------------------------------------
monero_wallet_full * neroshop::Wallet::get_monero_wallet() const
{
return monero_wallet_obj.get();
}
//-------------------------------------------------------
//-------------------------------------------------------
//-------------------------------------------------------
bool neroshop::Wallet::is_opened() const {
switch(wallet_type) {
case WalletType::Monero:
return (monero_wallet_obj != nullptr);
case WalletType::Wownero:
return false;
default:
return false;
}
}
//-------------------------------------------------------
bool neroshop::Wallet::is_connected_to_daemon() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->is_connected_to_daemon();
case WalletType::Wownero:
return false;
default:
return false;
}
}
//-------------------------------------------------------
bool neroshop::Wallet::is_synced() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
return monero_wallet_obj->is_synced();
case WalletType::Wownero:
return false;
default:
return false;
}
}
//-------------------------------------------------------
bool neroshop::Wallet::is_daemon_synced() const {
switch(wallet_type) {
case WalletType::Monero:
if(!monero_wallet_obj.get()) throw std::runtime_error("monero_wallet_full is not opened");
if(!monero_wallet_obj->is_connected_to_daemon()) {
return false;
}
return monero_wallet_obj->is_daemon_synced(); // will cause crash if wallet is not connected to daemon
case WalletType::Wownero:
return false;
default:
return false;
}
}
//-------------------------------------------------------
//-------------------------------------------------------
bool neroshop::Wallet::file_exists(const std::string& filename) const {
return monero::monero_wallet_full::wallet_exists(filename + ".keys");
}
//-------------------------------------------------------
bool neroshop::Wallet::is_valid_address(const std::string& address) const {
auto wallet_network_type = get_wallet_network_type();
switch(wallet_type) {
case WalletType::Monero:
return monero_utils::is_valid_address(address, static_cast<monero::monero_network_type>(wallet_network_type));
case WalletType::Wownero:
return false;
default:
return false;
}
}
//-------------------------------------------------------
bool neroshop::Wallet::is_valid_monero_address(const std::string& address) {
return monero_utils::is_valid_address(address, static_cast<monero::monero_network_type>(Wallet::network_type));
}
//-------------------------------------------------------
/*bool neroshop::Wallet::is_valid_wownero_address(const std::string& address) {
return ;
}*/
//-------------------------------------------------------
bool neroshop::Wallet::is_valid_openalias_address(const std::string& address) {
return (string_tools::is_valid_domain(address) || string_tools::is_email(address));
}
//-------------------------------------------------------
bool neroshop::Wallet::is_cryptonote_based() const {
return (wallet_type == WalletType::Monero || wallet_type == WalletType::Wownero);
}
//-------------------------------------------------------