mirror of
https://github.com/wownero/lwsf-wow.git
synced 2026-10-17 15:31:32 +02:00
283 lines
9.5 KiB
C++
283 lines
9.5 KiB
C++
// Copyright (c) 2025, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include "lws_frontend.h"
|
|
|
|
#include <boost/program_options/options_description.hpp>
|
|
#include <boost/program_options/variables_map.hpp>
|
|
#include <ctime>
|
|
#include <iostream>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
#include "common/command_line.h" // monero/src
|
|
#include "common/expect.h" // monero/src
|
|
#include "common/password.h" // monero/src
|
|
#include "common/util.h" // monero/src
|
|
#include "cryptonote_basic/cryptonote_format_utils.h"
|
|
#include "error.h"
|
|
|
|
namespace
|
|
{
|
|
struct options
|
|
{
|
|
const command_line::arg_descriptor<std::string> wallet_file;
|
|
const command_line::arg_descriptor<std::string> lws_url;
|
|
const command_line::arg_descriptor<std::string> network;
|
|
const command_line::arg_descriptor<std::string> config_file;
|
|
|
|
options()
|
|
: wallet_file{"wallet-file", "Path to read-and-store wallet. Omit to skip wallet cache storage."}
|
|
, lws_url{"lws-url", "<protocol>://address:port of LWS server REST API. Omit to read cached data."}
|
|
, network{"network", "<\"main\"|\"stage\"|\"test\"> - Blockchain net type", "main"}
|
|
, config_file{"config-file", "Specify any option in a config file; <name>=<value> on separate lines"}
|
|
{}
|
|
|
|
void prepare(boost::program_options::options_description& description) const
|
|
{
|
|
command_line::add_arg(description, wallet_file);
|
|
command_line::add_arg(description, lws_url);
|
|
command_line::add_arg(description, config_file);
|
|
command_line::add_arg(description, network);
|
|
command_line::add_arg(description, command_line::arg_help);
|
|
}
|
|
|
|
Monero::NetworkType get_network(boost::program_options::variables_map const& args) const
|
|
{
|
|
const std::string net = command_line::get_arg(args, network);
|
|
if (net == "main")
|
|
return Monero::MAINNET;
|
|
else if (net == "stage")
|
|
return Monero::STAGENET;
|
|
else if (net == "test")
|
|
return Monero::TESTNET;
|
|
else
|
|
throw std::runtime_error{"Bad --network value"};
|
|
}
|
|
};
|
|
|
|
struct program
|
|
{
|
|
std::string wallet_file;
|
|
std::string lws_url;
|
|
Monero::NetworkType network;
|
|
};
|
|
|
|
void print_help(std::ostream& out)
|
|
{
|
|
boost::program_options::options_description description{"Options"};
|
|
options{}.prepare(description);
|
|
|
|
out << "Usage: [options]" << std::endl;
|
|
out << description;
|
|
}
|
|
|
|
std::optional<program> get_program(int argc, char** argv)
|
|
{
|
|
namespace po = boost::program_options;
|
|
|
|
const options opts{};
|
|
po::variables_map args{};
|
|
{
|
|
po::options_description description{"Options"};
|
|
opts.prepare(description);
|
|
|
|
po::store(
|
|
po::command_line_parser(argc, argv).options(description).run(), args
|
|
);
|
|
po::notify(args);
|
|
|
|
if (!command_line::is_arg_defaulted(args, opts.config_file))
|
|
{
|
|
std::filesystem::path config_path{command_line::get_arg(args, opts.config_file)};
|
|
if (!std::filesystem::exists(config_path))
|
|
MONERO_THROW(lwsf::error::configuration, "Config file does not exist");
|
|
|
|
po::store(
|
|
po::parse_config_file<char>(config_path.string().c_str(), description), args
|
|
);
|
|
po::notify(args);
|
|
}
|
|
}
|
|
|
|
if (command_line::get_arg(args, command_line::arg_help))
|
|
{
|
|
print_help(std::cout);
|
|
return std::nullopt;
|
|
}
|
|
|
|
program prog{
|
|
command_line::get_arg(args, opts.wallet_file),
|
|
command_line::get_arg(args, opts.lws_url),
|
|
opts.get_network(args)
|
|
};
|
|
|
|
return prog;
|
|
}
|
|
|
|
void run(program prog)
|
|
{
|
|
const std::unique_ptr<Monero::WalletManager> wm{
|
|
lwsf::WalletManagerFactory::getWalletManager()
|
|
};
|
|
if (!wm)
|
|
throw std::runtime_error{"Unexpected WalletManager nullptr"};
|
|
|
|
std::unique_ptr<Monero::Wallet> wallet;
|
|
{
|
|
if (prog.wallet_file.empty() || !wm->walletExists(prog.wallet_file))
|
|
{
|
|
std::cout << "Input seed words: ";
|
|
std::string seed;
|
|
std::getline(std::cin, seed, '\n');
|
|
|
|
std::string pass;
|
|
if (!prog.wallet_file.empty())
|
|
{
|
|
const auto wiped = tools::password_container::prompt(true, "Wallet File Password");
|
|
if (!wiped)
|
|
throw std::runtime_error{"Failed to get password"};
|
|
pass.assign(wiped->password().data(), wiped->password().size());
|
|
}
|
|
|
|
wallet.reset(wm->recoveryWallet(prog.wallet_file, pass, seed, prog.network));
|
|
}
|
|
else
|
|
{
|
|
const auto wiped = tools::password_container::prompt(false, "Wallet File Password");
|
|
if (!wiped)
|
|
throw std::runtime_error{"Failed to get password"};
|
|
|
|
const std::string pass{wiped->password().data(), wiped->password().size()};
|
|
wallet.reset(wm->openWallet(prog.wallet_file, pass, prog.network));
|
|
|
|
int status = 0;
|
|
std::string error;
|
|
wallet->statusWithErrorString(status, error);
|
|
if (status != Monero::Wallet::Status_Ok)
|
|
throw std::runtime_error{"Failed to open wallet: " + error};
|
|
}
|
|
|
|
if (!wallet)
|
|
throw std::runtime_error{"Unexpected Wallet nullptr"};
|
|
}
|
|
|
|
if (!prog.lws_url.empty())
|
|
{
|
|
wallet->setAutoRefreshInterval(0); // do not start background thread
|
|
if (!wallet->init(prog.lws_url, 0, "", "", (prog.lws_url.substr(0, 8) == "https://"), true) || !wallet->refresh())
|
|
throw std::runtime_error{"Failed to refresh wallet: " + wallet->errorString()};
|
|
}
|
|
|
|
if (!prog.wallet_file.empty())
|
|
{
|
|
if (!wallet->store({}))
|
|
throw std::runtime_error{"Failed to store wallet file: " + wallet->errorString()};
|
|
}
|
|
|
|
std::cout << "; Primary Address: " << wallet->address() << std::endl;
|
|
std::cout << "; Blockchain Height: " << wallet->daemonBlockChainHeight() << std::endl;
|
|
std::cout << "; Start Height: " << wallet->getRefreshFromBlockHeight() << std::endl;
|
|
std::cout << "; Scan Height: " << wallet->blockChainHeight() << std::endl;
|
|
std::cout << "; Accounts: " << wallet->numSubaddressAccounts() << std::endl;
|
|
|
|
Monero::TransactionHistory* history = wallet->history();
|
|
if (!history)
|
|
throw std::runtime_error{"Unexpected history nullptr"};
|
|
|
|
history->refresh();
|
|
const std::vector<Monero::TransactionInfo*>& info_list = history->getAll();
|
|
|
|
std::cout << "; Transactions: " << info_list.size() << std::endl << std::endl;
|
|
|
|
for (const Monero::TransactionInfo* info : info_list)
|
|
{
|
|
if (!info)
|
|
throw std::runtime_error{"Unexpected info nullptr"};
|
|
|
|
std::tm expanded{};
|
|
const std::time_t timestamp = info->timestamp();
|
|
if (!gmtime_r(std::addressof(timestamp), std::addressof(expanded)))
|
|
throw std::runtime_error{"gmtime failure"};
|
|
|
|
char buf[11] = {0};
|
|
if (sizeof(buf) - 1 != std::strftime(buf, sizeof(buf), "%Y/%m/%d", std::addressof(expanded)))
|
|
throw std::runtime_error{"strftime failed"};
|
|
|
|
std::cout << buf << " (" << info->hash() << ") " << info->label() << std::endl;
|
|
|
|
if (info->direction() == Monero::TransactionInfo::Direction_In)
|
|
{
|
|
std::cout << " Assets:Monero:" << info->subaddrAccount() << " " << cryptonote::print_money(info->amount()) << std::endl;
|
|
std::cout << " Income" << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cout << " Expenses " << cryptonote::print_money(info->amount()) << std::endl;
|
|
std::cout << " Expenses:Monero:Fee " << cryptonote::print_money(info->fee()) << std::endl;
|
|
std::cout << " Assets:Monero:" << info->subaddrAccount() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
try
|
|
{
|
|
std::optional<program> prog;
|
|
|
|
try
|
|
{
|
|
prog = get_program(argc, argv);
|
|
}
|
|
catch (std::exception const& e)
|
|
{
|
|
std::cerr << e.what() << std::endl << std::endl;
|
|
print_help(std::cerr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (prog)
|
|
run(std::move(*prog));
|
|
}
|
|
catch (std::exception const& e)
|
|
{
|
|
std::cerr << e.what() << std::endl;;
|
|
return EXIT_FAILURE;
|
|
}
|
|
catch (...)
|
|
{
|
|
std::cerr << "Unknown exception" << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|