Files
lwsf-wow/src/ledger_main.cpp
T
2025-04-01 16:18:53 -04:00

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;
}