mirror of
https://github.com/wownero/lwsf-wow.git
synced 2026-10-17 07:21:28 +02:00
Several big changes:
- Stubbed out incomplete components to get fully compilation - Added a (mostly) working ledger output as example usage - Fixed several minor bugs discovered during first usage
This commit is contained in:
@@ -40,22 +40,28 @@ set(MONERO_LIBRARIES
|
||||
serialization
|
||||
lmdb_lib
|
||||
net
|
||||
checkpoints
|
||||
hardforks
|
||||
cryptonote_core
|
||||
cryptonote_basic
|
||||
cryptonote_format_utils_basic
|
||||
ringct
|
||||
ringct_basic
|
||||
mnemonics
|
||||
multisig
|
||||
blockchain_db
|
||||
common
|
||||
lmdb
|
||||
device
|
||||
device_trezor
|
||||
cncrypto
|
||||
randomx
|
||||
epee
|
||||
easylogging
|
||||
version
|
||||
unbound
|
||||
wallet
|
||||
wallet_api
|
||||
)
|
||||
|
||||
set(MONERO_OPTIONAL unbound)
|
||||
@@ -64,12 +70,19 @@ set(MONERO_SEARCH_PATHS
|
||||
"/contrib/epee/src"
|
||||
"/external/db_drivers/liblmdb"
|
||||
"/external/easylogging++"
|
||||
"/lib"
|
||||
"/src"
|
||||
"src/checkpoints"
|
||||
"/src/crypto"
|
||||
"/src/cryptonote_basic"
|
||||
"/src/cryptonote_core"
|
||||
"/src/device"
|
||||
"/src/device_trezor"
|
||||
"/src/hardforks"
|
||||
"/src/lmdb"
|
||||
"/src/ringct"
|
||||
"/src/rpc"
|
||||
"/src/wallet/api"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, The Monero Project
|
||||
|
||||
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.
|
||||
+10
-4
@@ -27,21 +27,23 @@
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
include_directories(.)
|
||||
add_subdirectory(wire)
|
||||
|
||||
# For both the server and admin utility.
|
||||
set(monero-lwsf_sources
|
||||
set(lwsf_sources
|
||||
address_book.cpp
|
||||
backend.cpp
|
||||
error.cpp
|
||||
pending_transaction.cpp
|
||||
rpc.cpp
|
||||
subaddress_account.cpp
|
||||
subaddress_minor.cpp
|
||||
transaction_history.cpp
|
||||
transaction_info.cpp
|
||||
wallet.cpp
|
||||
wallet_manager.cpp
|
||||
)
|
||||
set(monero-lwsf_headers
|
||||
set(lwsf_headers
|
||||
backend.h
|
||||
error.h
|
||||
lwsf_config.h
|
||||
@@ -52,9 +54,13 @@ set(monero-lwsf_headers
|
||||
wallet.h
|
||||
)
|
||||
|
||||
add_executable(monero-lwsf dummy_main.cpp ${monero-lwsf_sources} ${monero-lwsf_headers})
|
||||
target_link_libraries(monero-lwsf
|
||||
add_executable(lwsf-ledger ledger_main.cpp ${lwsf_sources} ${lwsf_headers})
|
||||
target_link_libraries(lwsf-ledger
|
||||
PRIVATE
|
||||
lwsf-wire
|
||||
lwsf-wire-json
|
||||
lwsf-wire-msgpack
|
||||
lwsf-wire-wrapper
|
||||
monero::libraries
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
|
||||
+64
-16
@@ -29,6 +29,8 @@
|
||||
#include "backend.h"
|
||||
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
#include "crypto/crypto.h" // monero/src
|
||||
#include "crypto/crypto-ops.h" // monero/src
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h" // monero/src
|
||||
#include "error.h"
|
||||
#include "lwsf_config.h"
|
||||
@@ -126,8 +128,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
void map_sub_account(F& format, T& self)
|
||||
{
|
||||
wire::object(format,
|
||||
wire::field("minor", wire::trusted_array(std::ref(self.minor))),
|
||||
WIRE_FIELD(label)
|
||||
wire::field("minor", wire::trusted_array(std::ref(self.minor)))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -207,6 +208,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
void map_account(F& format, T& self)
|
||||
{
|
||||
wire::object(format,
|
||||
WIRE_FIELD(language),
|
||||
wire::field("addressbook", wire::trusted_array(std::ref(self.addressbook))),
|
||||
wire::field("subaccounts", wire::trusted_array(std::ref(self.subaccounts))),
|
||||
wire::field("txes", wire::trusted_array(std::ref(self.txes))),
|
||||
@@ -266,20 +268,34 @@ namespace lwsf { namespace internal { namespace backend
|
||||
out.sender = source.sender.value_or(rpc::address_meta{});
|
||||
out.tx_pub = source.tx_pub_key;
|
||||
}
|
||||
|
||||
void update_tx(const account& self, transaction& out, const rpc::transaction& source)
|
||||
|
||||
crypto::secret_key get_spend_secret(const account& self, const std::optional<rpc::address_meta>& index)
|
||||
{
|
||||
if (!index || (index->maj_i == 0 && index->min_i == 0))
|
||||
return self.spend.sec;
|
||||
|
||||
// m = Hs(a || index_major || index_minor)
|
||||
const crypto::secret_key m = get_subaddress_secret_key(self.view.sec, index->maj_i, index->min_i);
|
||||
|
||||
// D = B + M
|
||||
crypto::secret_key out;
|
||||
sc_add(to_bytes(out), to_bytes(m), to_bytes(self.spend.sec));
|
||||
return out;
|
||||
}
|
||||
|
||||
bool update_tx(const account& self, transaction& out, const rpc::transaction& source)
|
||||
{
|
||||
/* Let `receives` re-populate in `merge_output`. This works because the
|
||||
server supplies all info - there is no local info to keep. */
|
||||
out.receives.clear();
|
||||
|
||||
out.id = source.tx_hash;
|
||||
out.id = source.hash;
|
||||
out.timestamp = source.timestamp;
|
||||
out.fee = std::uint64_t(source.fee.value_or(rpc::uint64_string(0)));
|
||||
out.height = source.height;
|
||||
out.unlock_time = source.unlock_time;
|
||||
out.payment_id = source.payment_id;
|
||||
out.coinbase = source.is_coinbase;
|
||||
out.coinbase = source.coinbase;
|
||||
|
||||
std::uint64_t total_spent = 0;
|
||||
for (const auto& spend : source.spent_outputs)
|
||||
@@ -291,7 +307,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
if (!crypto::derive_public_key(derivation, spend.out_index, self.spend.pub, output_pub))
|
||||
continue;
|
||||
crypto::secret_key output_secret{};
|
||||
crypto::derive_secret_key(derivation, spend.out_index, self.spend.sec, output_secret);
|
||||
crypto::derive_secret_key(derivation, spend.out_index, get_spend_secret(self, spend.sender), output_secret);
|
||||
|
||||
crypto::key_image image{};
|
||||
crypto::generate_key_image(output_pub, output_secret, image);
|
||||
@@ -304,6 +320,12 @@ namespace lwsf { namespace internal { namespace backend
|
||||
}
|
||||
}
|
||||
|
||||
if (!std::uint64_t(source.total_received) && !total_spent)
|
||||
return false; // used as decoy
|
||||
|
||||
if (out.fee <= total_spent)
|
||||
total_spent -= out.fee;
|
||||
|
||||
if (rpc::uint64_string(total_spent) < source.total_received)
|
||||
{
|
||||
out.direction = Monero::TransactionInfo::Direction_In;
|
||||
@@ -314,6 +336,8 @@ namespace lwsf { namespace internal { namespace backend
|
||||
out.direction = Monero::TransactionInfo::Direction_Out;
|
||||
out.amount = total_spent - std::uint64_t(source.total_received);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void merge_output(const std::shared_ptr<transaction>& out, const rpc::output& source, const crypto::secret_key& view_key)
|
||||
@@ -326,7 +350,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<transaction>>
|
||||
merge_response(wallet& self, const rpc::get_address_txs& source, const rpc::get_unspent_outs& unspents)
|
||||
merge_response(wallet& self, const rpc::get_address_txs& source, const rpc::get_unspent_outs_response& unspents)
|
||||
{
|
||||
std::vector<std::shared_ptr<transaction>> out;
|
||||
|
||||
@@ -369,10 +393,10 @@ namespace lwsf { namespace internal { namespace backend
|
||||
|
||||
for (const auto& tx : source.transactions)
|
||||
{
|
||||
auto inserted = updated_txes.try_emplace(tx.tx_hash, nullptr);
|
||||
auto inserted = updated_txes.try_emplace(tx.hash, nullptr);
|
||||
if (inserted.second)
|
||||
{
|
||||
const auto existing = self.primary.txes.find(tx.tx_hash);
|
||||
const auto existing = self.primary.txes.find(tx.hash);
|
||||
if (existing != self.primary.txes.end() && existing->second)
|
||||
inserted.first->second = std::make_shared<transaction>(*existing->second);
|
||||
else
|
||||
@@ -381,7 +405,8 @@ namespace lwsf { namespace internal { namespace backend
|
||||
if (existing == self.primary.txes.end())
|
||||
out.push_back(inserted.first->second);
|
||||
}
|
||||
update_tx(self.primary, *inserted.first->second, tx);
|
||||
if (!update_tx(self.primary, *inserted.first->second, tx))
|
||||
updated_txes.erase(tx.hash);
|
||||
}
|
||||
|
||||
for (const auto& output : unspents.outputs)
|
||||
@@ -412,6 +437,9 @@ namespace lwsf { namespace internal { namespace backend
|
||||
std::map<std::uint32_t, sub_account> subaccounts;
|
||||
subaccounts.swap(self.subaccounts);
|
||||
|
||||
// Never remove special {0,0} account
|
||||
self.subaccounts[0].minor[0] = std::move(subaccounts[0].minor[0]);
|
||||
|
||||
for (const auto& subaddr : subaddrs)
|
||||
{
|
||||
auto& account = self.subaccounts[subaddr.key];
|
||||
@@ -421,7 +449,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
for (std::uint32_t minor = std::get<0>(minors); minor < std::uint64_t(std::get<1>(minors)) + 1; ++minor)
|
||||
{
|
||||
auto elem = account.minor.try_emplace(minor);
|
||||
if (elem.second)
|
||||
if (elem.second && (subaddr.key || minor))
|
||||
elem.first->second = std::move(old.minor[minor]);
|
||||
}
|
||||
}
|
||||
@@ -430,6 +458,8 @@ namespace lwsf { namespace internal { namespace backend
|
||||
} // anonymous
|
||||
|
||||
WIRE_DEFINE_OBJECT(address_book_entry, map_address_book_entry);
|
||||
WIRE_DEFINE_OBJECT(subaddress, map_subaddress);
|
||||
WIRE_DEFINE_OBJECT(sub_account, map_sub_account);
|
||||
WIRE_DEFINE_OBJECT(transfer_out, map_transfer_out);
|
||||
WIRE_DEFINE_OBJECT(transfer_in, map_transfer_in);
|
||||
WIRE_DEFINE_OBJECT(transaction, map_transaction);
|
||||
@@ -566,15 +596,28 @@ namespace lwsf { namespace internal { namespace backend
|
||||
return {};
|
||||
}
|
||||
|
||||
expect<rpc::get_unspent_outs> outs_response{common_error::kInvalidArgument};
|
||||
expect<rpc::get_unspent_outs_response> outs_response{common_error::kInvalidArgument};
|
||||
|
||||
boost::unique_lock<boost::mutex> lock{sync};
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (!mandatory) // double-check
|
||||
{
|
||||
const std::chrono::steady_clock::time_point start{
|
||||
std::chrono::steady_clock::time_point::duration{last_sync.load()}
|
||||
};
|
||||
if (now - start < config::refresh_interval)
|
||||
return {};
|
||||
}
|
||||
last_sync = now.time_since_epoch().count();
|
||||
|
||||
const rpc::login login{primary.address, primary.view.sec};
|
||||
lock.unlock();
|
||||
|
||||
const auto txs_response = rpc::invoke<rpc::get_address_txs>(client, login);
|
||||
if (txs_response)
|
||||
outs_response = rpc::invoke<rpc::get_unspent_outs>(client, login);
|
||||
{
|
||||
const rpc::get_unspent_outs_request request{login, rpc::uint64_string(0), 0, true};
|
||||
outs_response = rpc::invoke<rpc::get_unspent_outs_response>(client, request);
|
||||
}
|
||||
|
||||
if (!txs_response)
|
||||
return txs_response.error();
|
||||
@@ -584,7 +627,6 @@ namespace lwsf { namespace internal { namespace backend
|
||||
lock.lock();
|
||||
const std::uint64_t orig_scan_height = primary.scan_height;
|
||||
const auto new_txes = merge_response(*this, *txs_response, *outs_response);
|
||||
last_sync = std::chrono::steady_clock::now().time_since_epoch().count();
|
||||
|
||||
const boost::lock_guard<boost::mutex> lock2{sync_listener};
|
||||
if (!listener)
|
||||
@@ -670,6 +712,12 @@ namespace lwsf { namespace internal { namespace backend
|
||||
return {error::rpc_failure};
|
||||
}
|
||||
|
||||
std::error_code wallet::restore_height(const std::uint64_t height)
|
||||
{
|
||||
/* TODO */
|
||||
throw std::logic_error{"restore_height not implemented"};
|
||||
}
|
||||
|
||||
std::error_code wallet::send_tx(epee::byte_slice tx_bytes)
|
||||
{
|
||||
const rpc::submit_raw_tx_request request{std::move(tx_bytes)};
|
||||
|
||||
+3
-2
@@ -177,6 +177,7 @@ namespace lwsf { namespace internal { namespace backend
|
||||
account() = delete;
|
||||
|
||||
std::string address; //!> not serialized, recovered on read_bytes
|
||||
std::string language;
|
||||
std::vector<address_book_entry> addressbook;
|
||||
std::map<std::uint32_t, sub_account> subaccounts;
|
||||
boost::container::flat_map<crypto::hash, std::shared_ptr<transaction>, memory> txes;
|
||||
@@ -217,11 +218,11 @@ namespace lwsf { namespace internal { namespace backend
|
||||
//! Attempt
|
||||
std::error_code login();
|
||||
|
||||
std::error_code add_subaccount(std::string label);
|
||||
|
||||
//! Refreshes txes information. Strong exception guarantee. Locks contents.
|
||||
std::error_code refresh(bool mandatory = false);
|
||||
|
||||
std::error_code add_subaccount(std::string label);
|
||||
|
||||
std::error_code restore_height(const std::uint64_t height);
|
||||
|
||||
std::error_code send_tx(epee::byte_slice tx_bytes);
|
||||
|
||||
@@ -33,6 +33,29 @@ namespace lwsf
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case error::none:
|
||||
return "No error";
|
||||
case error::configuration:
|
||||
return "Configuration issue";
|
||||
case error::connect_failure:
|
||||
return "Connection to server failed";
|
||||
case error::crypto_failure:
|
||||
return "Cryptography issue";
|
||||
case error::invalid_encoding:
|
||||
return "File had invalid encoding";
|
||||
case error::invalid_scheme:
|
||||
return "Invalid URL scheme";
|
||||
case error::read_failure:
|
||||
return "Failed to read file";
|
||||
case error::rpc_failure:
|
||||
return "RPC failed";
|
||||
case error::unexpected_userinfo:
|
||||
return "Unexpected user+pass field in URL";
|
||||
case error::unsupported_format:
|
||||
return "Could not unpack file";
|
||||
case error::write_failure:
|
||||
return "Failed to write file";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace lwsf
|
||||
enum class error : int
|
||||
{
|
||||
none = 0, //!< Must be zero for `expect<..>`
|
||||
configuration, //!< Bad Configuration
|
||||
connect_failure, //!< Connection to server failed
|
||||
crypto_failure, //!< Failure in crypto
|
||||
invalid_encoding, //!< Invalid percent encoding
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
// 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())
|
||||
{
|
||||
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 << 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();
|
||||
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;
|
||||
}
|
||||
|
||||
+40
-9
@@ -105,9 +105,9 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
}
|
||||
|
||||
void write_bytes(wire::writer& dest, const empty&)
|
||||
{ dest.boolean(true); }
|
||||
void read_bytes(wire::reader& source, const empty&)
|
||||
{ source.boolean(); }
|
||||
{ wire::object(dest); }
|
||||
void read_bytes(wire::reader& source, empty&)
|
||||
{ wire::object(source); }
|
||||
|
||||
void write_bytes(wire::json_writer& dest, const login& self)
|
||||
{
|
||||
@@ -129,6 +129,23 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
wire::object(source, WIRE_OPTIONAL_FIELD(start_height));
|
||||
}
|
||||
|
||||
void read_bytes(wire::json_reader& source, daemon_status& self)
|
||||
{
|
||||
wire::object(source,
|
||||
WIRE_FIELD(outgoing_connections_count),
|
||||
WIRE_FIELD(incoming_connections_count),
|
||||
WIRE_FIELD(height),
|
||||
WIRE_FIELD(target_height)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void write_bytes(wire::json_writer& dest, const uint64_string source)
|
||||
{
|
||||
const auto as_string = dest.to_string(std::uintmax_t(source));
|
||||
dest.string({as_string.data()});
|
||||
}
|
||||
|
||||
void read_bytes(wire::json_reader& source, uint64_string& dest)
|
||||
{
|
||||
dest = uint64_string(wire::integer::cast_unsigned<std::uint64_t>(source.safe_unsigned_integer()));
|
||||
@@ -170,8 +187,8 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
WIRE_FIELD(total_received),
|
||||
WIRE_FIELD(unlock_time),
|
||||
WIRE_OPTIONAL_FIELD(height),
|
||||
WIRE_FIELD(tx_hash),
|
||||
WIRE_FIELD(is_coinbase),
|
||||
WIRE_FIELD(hash),
|
||||
WIRE_FIELD(coinbase),
|
||||
WIRE_FIELD(mempool)
|
||||
);
|
||||
|
||||
@@ -200,10 +217,13 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
// %Y-%m-%dT%H:%M:%SZ
|
||||
namespace qi = boost::spirit::qi;
|
||||
std::tm fields{};
|
||||
if (!qi::parse(timestamp->begin(), timestamp->end(), qi::ushort_ >> '-' >> qi::ushort_ >> '-' >> qi::ushort_ >> 'T' >> qi::ushort_ >> ':' >> qi::ushort_ >> qi::ushort_ >> 'Z', fields.tm_year, fields.tm_mon, fields.tm_mday, fields.tm_hour, fields.tm_min, fields.tm_sec))
|
||||
if (!qi::parse(timestamp->begin(), timestamp->end(), qi::ushort_ >> '-' >> qi::ushort_ >> '-' >> qi::ushort_ >> 'T' >> qi::ushort_ >> ':' >> qi::ushort_ >> ':' >> qi::ushort_ >> 'Z', fields.tm_year, fields.tm_mon, fields.tm_mday, fields.tm_hour, fields.tm_min, fields.tm_sec))
|
||||
WIRE_DLOG_THROW(wire::error::schema::string, "Timestamp string invalid format");
|
||||
if ((self.timestamp = std::mktime(std::addressof(fields))) == -1)
|
||||
WIRE_DLOG_THROW(wire::error::schema::string, "Invalid timestamp value");
|
||||
|
||||
fields.tm_year -= 1900;
|
||||
--fields.tm_mon;
|
||||
fields.tm_isdst = 0;
|
||||
self.timestamp = timegm(std::addressof(fields));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +308,18 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
self.rct = std::visit(convert_rct{}, raw_rct.value);
|
||||
}
|
||||
|
||||
void read_bytes(wire::json_reader& source, get_unspent_outs& self)
|
||||
void write_bytes(wire::json_writer& dest, const get_unspent_outs_request& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
wire::field("address", std::cref(self.creds.address)),
|
||||
wire::field("view_key", std::cref(self.creds.view_key)),
|
||||
WIRE_FIELD_COPY(amount),
|
||||
WIRE_FIELD_COPY(mixin),
|
||||
WIRE_FIELD_COPY(use_dust)
|
||||
);
|
||||
}
|
||||
|
||||
void read_bytes(wire::json_reader& source, get_unspent_outs_response& self)
|
||||
{
|
||||
using min_output_size = wire::min_element_sizeof<
|
||||
ringct, crypto::hash, crypto::hash, crypto::public_key, crypto::public_key
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
return result.error();
|
||||
|
||||
F out{};
|
||||
error = wire::json::from_bytes(std::move(*result), out);
|
||||
error = wire::json::from_bytes(epee::to_span(*result), out);
|
||||
if (error)
|
||||
return error;
|
||||
return out;
|
||||
@@ -134,6 +134,7 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
|
||||
|
||||
enum class uint64_string : std::uint64_t {};
|
||||
void write_bytes(wire::json_writer&, uint64_string);
|
||||
void read_bytes(wire::json_reader&, uint64_string&);
|
||||
|
||||
struct address_meta
|
||||
@@ -178,8 +179,8 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
uint64_string total_received;
|
||||
std::uint64_t unlock_time;
|
||||
std::optional<std::uint64_t> height;
|
||||
crypto::hash tx_hash;
|
||||
bool is_coinbase;
|
||||
crypto::hash hash;
|
||||
bool coinbase;
|
||||
bool mempool;
|
||||
|
||||
transaction()
|
||||
@@ -190,8 +191,8 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
total_received(uint64_string(0)),
|
||||
unlock_time(0),
|
||||
height(),
|
||||
tx_hash{},
|
||||
is_coinbase(false),
|
||||
hash{},
|
||||
coinbase(false),
|
||||
mempool(false)
|
||||
{}
|
||||
};
|
||||
@@ -273,16 +274,26 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
};
|
||||
void read_bytes(wire::json_reader&, output&);
|
||||
|
||||
struct get_unspent_outs
|
||||
struct get_unspent_outs_request
|
||||
{
|
||||
get_unspent_outs() = delete;
|
||||
get_unspent_outs_request() = delete;
|
||||
login creds;
|
||||
uint64_string amount;
|
||||
std::uint32_t mixin;
|
||||
bool use_dust;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, const get_unspent_outs_request&);
|
||||
|
||||
struct get_unspent_outs_response
|
||||
{
|
||||
get_unspent_outs_response() = delete;
|
||||
static constexpr const char* endpoint() noexcept { return "/get_unspent_outs"; }
|
||||
|
||||
std::vector<output> outputs;
|
||||
std::uint64_t per_byte_fee;
|
||||
std::uint64_t fee_mask;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, get_unspent_outs&);
|
||||
void read_bytes(wire::json_reader&, get_unspent_outs_response&);
|
||||
|
||||
|
||||
struct import_response
|
||||
@@ -335,7 +346,7 @@ namespace lwsf { namespace internal { namespace rpc
|
||||
|
||||
}}} // lwsf // internal // rpc
|
||||
|
||||
WIRE_DECLARE_BLOB_NS(lwsf::internal::rpc::ringct);
|
||||
WIRE_DECLARE_BLOB(lwsf::internal::rpc::ringct);
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
// 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 "subaddress_account.h"
|
||||
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
#include <ctime>
|
||||
#include <utility>
|
||||
#include "backend.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h" // monero/src
|
||||
|
||||
namespace lwsf { namespace internal
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct balance
|
||||
{
|
||||
std::uint64_t unlocked;
|
||||
std::uint64_t total;
|
||||
|
||||
balance()
|
||||
: unlocked(0), total(0)
|
||||
{}
|
||||
};
|
||||
|
||||
template<typename T, typename value_type = typename T::value_type>
|
||||
std::vector<value_type> copy(const T& src)
|
||||
{
|
||||
std::vector<value_type> out;
|
||||
out.reserve(src.size());
|
||||
for (const auto& elem : src)
|
||||
out.push_back(elem);
|
||||
return out;
|
||||
}
|
||||
|
||||
void free_rows(std::vector<Monero::SubaddressAccountRow*>& rows) noexcept
|
||||
{
|
||||
for (auto row : rows)
|
||||
delete row;
|
||||
rows.clear();
|
||||
}
|
||||
}
|
||||
|
||||
subaddress_account::subaddress_account(std::shared_ptr<backend::wallet> data)
|
||||
: data_(std::move(data)), rows_()
|
||||
{
|
||||
if (!data_)
|
||||
throw std::invalid_argument{"lwsf::internal::subaddress_account cannot be given nullptr"};
|
||||
}
|
||||
|
||||
subaddress_account::~subaddress_account()
|
||||
{
|
||||
free_rows(rows_);
|
||||
}
|
||||
|
||||
void subaddress_account::addRow(const std::string &label)
|
||||
{
|
||||
data_->add_subaccount(label);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void subaddress_account::setLabel(uint32_t accountIndex, const std::string &label)
|
||||
{
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
data_->primary.subaccounts.at(accountIndex).minor.at(0).label = label;
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
void subaddress_account::refresh()
|
||||
{
|
||||
try
|
||||
{
|
||||
free_rows(rows_);
|
||||
std::unordered_map<std::uint32_t, balance> balances;
|
||||
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
const Monero::NetworkType net_type = data_->primary.type;
|
||||
const std::uint32_t chain_height = data_->blockchain_height;
|
||||
|
||||
for (const auto& tx : data_->primary.txes)
|
||||
{
|
||||
const bool unlocked = tx.second->is_unlocked(chain_height, net_type);
|
||||
for (const auto& receive : tx.second->receives)
|
||||
{
|
||||
auto& balance = balances[receive.second.recipient.maj_i];
|
||||
balance.total += receive.second.amount;
|
||||
if (unlocked)
|
||||
balance.unlocked += receive.second.amount;
|
||||
}
|
||||
|
||||
for (const auto& spend : tx.second->spends)
|
||||
{
|
||||
auto& balance = balances[spend.second.sender.maj_i];
|
||||
balance.unlocked -= spend.second.amount;
|
||||
balance.total -= spend.second.amount;
|
||||
}
|
||||
}
|
||||
|
||||
rows_.reserve(data_->primary.subaccounts.size());
|
||||
for (const auto& sub : data_->primary.subaccounts)
|
||||
{
|
||||
const auto& balance = balances[sub.first];
|
||||
rows_.push_back(
|
||||
new Monero::SubaddressAccountRow{
|
||||
sub.first,
|
||||
data_->get_spend_address({sub.first, 0}),
|
||||
sub.second.minor.empty() ? "" : sub.second.minor.begin()->second.label,
|
||||
cryptonote::print_money(balance.total),
|
||||
cryptonote::print_money(balance.unlocked)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
free_rows(rows_);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}} // lwsf // internal
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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 "subaddress_minor.h"
|
||||
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
#include <ctime>
|
||||
#include <utility>
|
||||
#include "backend.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h" // monero/src
|
||||
|
||||
namespace lwsf { namespace internal
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void free_rows(std::vector<Monero::SubaddressRow*>& rows) noexcept
|
||||
{
|
||||
for (auto row : rows)
|
||||
delete row;
|
||||
rows.clear();
|
||||
}
|
||||
}
|
||||
|
||||
subaddress_minor::subaddress_minor(std::shared_ptr<backend::wallet> data)
|
||||
: data_(std::move(data)), rows_()
|
||||
{
|
||||
if (!data_)
|
||||
throw std::invalid_argument{"lwsf::internal::subaddress_minor cannot be given nullptr"};
|
||||
}
|
||||
|
||||
subaddress_minor::~subaddress_minor()
|
||||
{
|
||||
free_rows(rows_);
|
||||
}
|
||||
|
||||
void subaddress_minor::refresh(const std::uint32_t accountIndex)
|
||||
{
|
||||
try
|
||||
{
|
||||
free_rows(rows_);
|
||||
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
const auto account = data_->primary.subaccounts.find(accountIndex);
|
||||
if (account == data_->primary.subaccounts.end())
|
||||
return;
|
||||
|
||||
rows_.reserve(account->second.minor.size());
|
||||
for (const auto& minor : account->second.minor)
|
||||
{
|
||||
rows_.push_back(
|
||||
new Monero::SubaddressRow{
|
||||
minor.first,
|
||||
data_->get_spend_address({accountIndex, minor.first}),
|
||||
minor.second.label
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
free_rows(rows_);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void subaddress_minor::addRow(const std::uint32_t accountIndex, const std::string &label)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
void subaddress_minor::setLabel(const std::uint32_t accountIndex, const std::uint32_t addressIndex, const std::string &label)
|
||||
{
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
data_->primary.subaccounts.at(accountIndex).minor.at(addressIndex).label = label;
|
||||
}
|
||||
refresh(accountIndex);
|
||||
}
|
||||
|
||||
}} // lwsf // internal
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "wallet/api/wallet2_api.h" // monero/src
|
||||
|
||||
namespace lwsf { namespace internal
|
||||
{
|
||||
namespace backend { struct wallet; }
|
||||
|
||||
class subaddress_minor final : public ::Monero::Subaddress
|
||||
{
|
||||
const std::shared_ptr<backend::wallet> data_;
|
||||
std::vector<Monero::SubaddressRow*> rows_;
|
||||
|
||||
public:
|
||||
|
||||
explicit subaddress_minor(std::shared_ptr<backend::wallet> data);
|
||||
|
||||
subaddress_minor(const subaddress_minor&) = delete;
|
||||
subaddress_minor(subaddress_minor&&) = delete;
|
||||
virtual ~subaddress_minor() override;
|
||||
subaddress_minor& operator=(const subaddress_minor&) = delete;
|
||||
subaddress_minor& operator=(subaddress_minor&&) = delete;
|
||||
|
||||
void refresh(uint32_t accountIndex) override;
|
||||
std::vector<Monero::SubaddressRow*> getAll() const override { return rows_; }
|
||||
void addRow(uint32_t accountIndex, const std::string &label) override;
|
||||
void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) override;
|
||||
};
|
||||
|
||||
}} // lwsf // internal
|
||||
@@ -37,7 +37,7 @@
|
||||
namespace lwsf { namespace internal
|
||||
{
|
||||
namespace backend { class wallet; }
|
||||
class transaction_history : public Monero::TransactionHistory
|
||||
class transaction_history final : public Monero::TransactionHistory
|
||||
{
|
||||
const std::shared_ptr<backend::wallet> data_;
|
||||
std::vector<Monero::TransactionInfo*> txes_;
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lws_frontend.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
+320
-44
@@ -42,14 +42,17 @@
|
||||
#include <string_view>
|
||||
#include "address_book.h"
|
||||
#include "backend.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h" // monero/src
|
||||
#include "error.h"
|
||||
#include "hex.h" // monero/contrib/epee/include
|
||||
#include "hex.h" // monero/contrib/epee/include
|
||||
#include "lwsf_config.h"
|
||||
#include "mnemonics/electrum-words.h" // monero/src
|
||||
#include "net/net_parse_helpers.h" // monero/contrib/epee/include
|
||||
#include "net/parse.h" // monero/src
|
||||
#include "net/socks.h" // monero/src
|
||||
#include "net/socks_connect.h" // monero/src
|
||||
#include "subaddress_account.h"
|
||||
#include "subaddress_minor.h"
|
||||
#include "transaction_history.h"
|
||||
#include "wire.h"
|
||||
#include "wire/msgpack.h"
|
||||
@@ -59,6 +62,9 @@ namespace lwsf { namespace internal
|
||||
|
||||
namespace
|
||||
{
|
||||
//! The stored wallet file always has this at beginning
|
||||
static constexpr std::string_view file_magic{"lwsf-wallet-1.0"};
|
||||
|
||||
struct null_connector
|
||||
{
|
||||
boost::unique_future<boost::asio::ip::tcp::socket>
|
||||
@@ -150,7 +156,7 @@ namespace lwsf { namespace internal
|
||||
|
||||
if (max_size() < upayload.size())
|
||||
throw std::runtime_error{std::string{"Exceeded max size for "} + cipher_name()};
|
||||
if (tag_size() < std::numeric_limits<std::size_t>::max() - upayload.size())
|
||||
if (std::numeric_limits<std::size_t>::max() - upayload.size() < tag_size())
|
||||
throw std::runtime_error{"Exceeded max size_t after authentication tag"};
|
||||
|
||||
encrypted_file out{
|
||||
@@ -201,6 +207,7 @@ namespace lwsf { namespace internal
|
||||
throw std::runtime_error{"Failed to initialize libsodium"};
|
||||
|
||||
const auto key = get_key(password);
|
||||
static_assert(key_size() == key.size());
|
||||
|
||||
epee::byte_stream buffer;
|
||||
buffer.put_n(0, epayload.size());
|
||||
@@ -208,13 +215,14 @@ namespace lwsf { namespace internal
|
||||
static_assert(
|
||||
std::numeric_limits<std::size_t>::max() <= std::numeric_limits<unsigned long long>::max()
|
||||
);
|
||||
|
||||
unsigned long long out_bytes = buffer.size();
|
||||
if (crypto_aead_chacha20poly1305_ietf_decrypt(
|
||||
buffer.data(), std::addressof(out_bytes),
|
||||
nullptr,
|
||||
epayload.data(), epayload.size(),
|
||||
nullptr, 0,
|
||||
nonce.data(), key.data()) == 0)
|
||||
nonce.data(), key.data()) != 0)
|
||||
{
|
||||
throw std::runtime_error{std::string{cipher_name()} + " decryption failed"};
|
||||
}
|
||||
@@ -241,18 +249,27 @@ namespace lwsf { namespace internal
|
||||
|
||||
epee::byte_slice try_load(const std::string& filename)
|
||||
{
|
||||
std::ifstream file{filename, std::ios::binary | std::ios::ate};
|
||||
std::ifstream file{filename, std::ios::binary};
|
||||
if (!file.is_open())
|
||||
return nullptr;
|
||||
|
||||
{
|
||||
std::string magic;
|
||||
magic.resize(file_magic.size());
|
||||
file.read(magic.data(), magic.size());
|
||||
if (!file.good() || magic != file_magic)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
const auto size = file.tellg();
|
||||
if (size < 0)
|
||||
if (size < 0 || std::size_t(size) < file_magic.size())
|
||||
return nullptr;
|
||||
|
||||
epee::byte_stream buffer;
|
||||
buffer.put_n(0, std::size_t(size));
|
||||
buffer.put_n(0, std::size_t(size) - file_magic.size());
|
||||
|
||||
file.seekg(0);
|
||||
file.seekg(file_magic.size());
|
||||
file.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
|
||||
|
||||
if (file.good())
|
||||
@@ -271,7 +288,10 @@ namespace lwsf { namespace internal
|
||||
bool rc = false;
|
||||
if (0 <= write(fd, contents.data(), contents.size()) && 0 <= fsync(fd))
|
||||
{
|
||||
const int dir = open(directory.c_str(), O_RDONLY);
|
||||
const char* cwd = directory.c_str();
|
||||
if (strcmp(cwd, "") == 0)
|
||||
cwd = ".";
|
||||
const int dir = open(cwd, O_RDONLY);
|
||||
if (0 <= dir)
|
||||
{
|
||||
if (0 <= fsync(dir))
|
||||
@@ -303,6 +323,23 @@ namespace lwsf { namespace internal
|
||||
);
|
||||
return boost::chrono::nanoseconds{source.count()};
|
||||
}
|
||||
|
||||
void load_wallet(backend::account& self, const crypto::secret_key& from, const Monero::NetworkType nettype, const bool generated_locally)
|
||||
{
|
||||
cryptonote::account_base base{};
|
||||
base.generate(from, true);
|
||||
|
||||
const auto& keys = base.get_keys();
|
||||
|
||||
self.view.sec = keys.m_view_secret_key;
|
||||
self.view.pub = keys.m_account_address.m_view_public_key;
|
||||
|
||||
self.spend.sec = keys.m_spend_secret_key;
|
||||
self.spend.pub = keys.m_account_address.m_spend_public_key;
|
||||
|
||||
self.generated_locally = generated_locally;
|
||||
self.type = nettype;
|
||||
}
|
||||
} // anonymous
|
||||
|
||||
bool wallet::set_error(const std::error_code status) const
|
||||
@@ -368,8 +405,54 @@ namespace lwsf { namespace internal
|
||||
}
|
||||
}
|
||||
|
||||
wallet::wallet(create_tag, Monero::NetworkType nettype, std::string filename, std::string password, const std::uint64_t kdf_rounds)
|
||||
bool wallet::is_wallet_file(const std::string& path)
|
||||
{
|
||||
return !try_load(path).empty();
|
||||
}
|
||||
|
||||
bool wallet::verify_password(std::string path, const std::string& password)
|
||||
{
|
||||
// We don't use a separate keys file, remove the suffix is present
|
||||
static constexpr const std::string_view keys_suffix{".keys"};
|
||||
if (keys_suffix.size() <= path.size() && std::string_view{path.data() + path.size() - keys_suffix.size()} == keys_suffix)
|
||||
path.erase(path.size() - keys_suffix.size());
|
||||
|
||||
epee::byte_slice file = try_load(path);
|
||||
if (file.empty())
|
||||
return false;
|
||||
|
||||
encrypted_file contents{};
|
||||
if (!wire::msgpack::from_bytes(std::move(file), contents))
|
||||
return false;
|
||||
return !contents.get_payload(password).empty();
|
||||
}
|
||||
|
||||
std::vector<std::string> wallet::find(const std::string& path)
|
||||
{
|
||||
std::vector<std::string> out;
|
||||
std::filesystem::path work_dir(path);
|
||||
if(!std::filesystem::is_directory(path))
|
||||
return out;
|
||||
|
||||
std::filesystem::recursive_directory_iterator end_itr;
|
||||
for (std::filesystem::recursive_directory_iterator itr(path); itr != end_itr; ++itr)
|
||||
{
|
||||
if (std::filesystem::is_regular_file(itr->status()))
|
||||
{
|
||||
std::string filename = itr->path().filename().string();
|
||||
if (!try_load(filename).empty())
|
||||
out.push_back(std::move(filename));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
wallet::wallet(create, Monero::NetworkType nettype, std::string filename, std::string password, const std::uint64_t kdf_rounds)
|
||||
: data_(std::make_shared<backend::wallet>()),
|
||||
addressbook_(),
|
||||
history_(),
|
||||
subaddresses_(),
|
||||
subaddress_minor_(),
|
||||
filename_(std::move(filename)),
|
||||
password_(std::move(password)),
|
||||
exception_error_(),
|
||||
@@ -391,26 +474,15 @@ namespace lwsf { namespace internal
|
||||
crypto::secret_key recovery;
|
||||
randombytes_buf(std::addressof(unwrap(unwrap(recovery))), sizeof(recovery));
|
||||
|
||||
cryptonote::account_base base{};
|
||||
base.generate(recovery, true);
|
||||
|
||||
const auto& keys = base.get_keys();
|
||||
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
data_->primary.view.sec = keys.m_view_secret_key;
|
||||
data_->primary.view.pub = keys.m_account_address.m_view_public_key;
|
||||
|
||||
data_->primary.spend.sec = keys.m_spend_secret_key;
|
||||
data_->primary.spend.pub = keys.m_account_address.m_spend_public_key;
|
||||
|
||||
data_->primary.generated_locally = true;
|
||||
data_->primary.type = nettype;
|
||||
|
||||
store(std::string{});
|
||||
load_wallet(data_->primary, recovery, nettype, true);
|
||||
}
|
||||
|
||||
wallet::wallet(open_tag, Monero::NetworkType nettype, std::string filename, std::string password, const std::uint64_t kdf_rounds, std::shared_ptr<backend::wallet> data)
|
||||
: data_(std::move(data)),
|
||||
wallet::wallet(open, Monero::NetworkType nettype, std::string filename, std::string password, const std::uint64_t kdf_rounds)
|
||||
: data_(std::make_shared<backend::wallet>()),
|
||||
addressbook_(),
|
||||
history_(),
|
||||
subaddresses_(),
|
||||
subaddress_minor_(),
|
||||
filename_(std::move(filename)),
|
||||
password_(std::move(password)),
|
||||
exception_error_(),
|
||||
@@ -463,8 +535,82 @@ namespace lwsf { namespace internal
|
||||
}
|
||||
}
|
||||
|
||||
wallet::wallet(from_mnemonic, const Monero::NetworkType nettype, std::string filename, std::string password, const std::uint64_t kdf_rounds, const std::string& mnemonic, const std::string& seed_offset)
|
||||
: data_(std::make_shared<backend::wallet>()),
|
||||
addressbook_(),
|
||||
history_(),
|
||||
subaddresses_(),
|
||||
subaddress_minor_(),
|
||||
filename_(std::move(filename)),
|
||||
password_(std::move(password)),
|
||||
exception_error_(),
|
||||
status_(),
|
||||
thread_(),
|
||||
iterations_(kdf_rounds),
|
||||
mixin_(config::mixin_default),
|
||||
refresh_interval_(config::refresh_interval),
|
||||
refresh_notify_(),
|
||||
error_sync_(),
|
||||
refresh_sync_(),
|
||||
thread_state_(state::stop),
|
||||
mandatory_refresh_(false)
|
||||
{
|
||||
if (sodium_init() < 0)
|
||||
throw std::runtime_error{"Failed to initialize libsodium"};
|
||||
|
||||
crypto::secret_key recovery;
|
||||
std::string language;
|
||||
if (!crypto::ElectrumWords::words_to_bytes(mnemonic, recovery, language))
|
||||
{
|
||||
exception_error_ = "Electrum-style word list failed verification";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!seed_offset.empty())
|
||||
recovery = cryptonote::decrypt_key(recovery, seed_offset);
|
||||
|
||||
load_wallet(data_->primary, recovery, nettype, false);
|
||||
}
|
||||
|
||||
wallet::~wallet() { stop_refresh_loop(); }
|
||||
|
||||
std::string wallet::seed(const std::string& seed_offset) const
|
||||
{
|
||||
std::string language;
|
||||
crypto::secret_key key;
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
language = data_->primary.language;
|
||||
key = data_->primary.spend.sec;
|
||||
}
|
||||
|
||||
if (language.empty())
|
||||
language = "English";
|
||||
|
||||
if (!seed_offset.empty())
|
||||
key = cryptonote::encrypt_key(key, seed_offset);
|
||||
|
||||
epee::wipeable_string electrum_words;
|
||||
if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, language))
|
||||
{
|
||||
set_critical(std::runtime_error{"Failed to crate seed from key for " + language});
|
||||
return {};
|
||||
}
|
||||
return std::string{electrum_words.data(), electrum_words.size()};
|
||||
}
|
||||
|
||||
std::string wallet::getSeedLanguage() const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
return data_->primary.language;
|
||||
}
|
||||
|
||||
void wallet::setSeedLanguage(const std::string &arg)
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
data_->primary.language = arg;
|
||||
}
|
||||
|
||||
int wallet::status() const
|
||||
{
|
||||
int out = 0;
|
||||
@@ -507,6 +653,11 @@ namespace lwsf { namespace internal
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string wallet::address(const std::uint32_t accountIndex, const std::uint32_t addressIndex) const
|
||||
{
|
||||
return data_->get_spend_address({accountIndex, addressIndex});
|
||||
}
|
||||
|
||||
Monero::NetworkType wallet::nettype() const { return data_->primary.type; }
|
||||
|
||||
std::string wallet::secretViewKey() const
|
||||
@@ -551,6 +702,7 @@ namespace lwsf { namespace internal
|
||||
|
||||
epee::byte_stream buffer;
|
||||
buffer.reserve(payload_size + 2048);
|
||||
buffer.write(file_magic.data(), file_magic.size());
|
||||
|
||||
if (!(status = wire::msgpack::to_bytes(buffer, contents)))
|
||||
{
|
||||
@@ -610,7 +762,7 @@ namespace lwsf { namespace internal
|
||||
stop_refresh_loop();
|
||||
data_->client.disconnect();
|
||||
|
||||
if (!setProxy(proxy_address))
|
||||
if (!proxy_address.empty() && !setProxy(proxy_address))
|
||||
return false;
|
||||
|
||||
epee::net_utils::http::url_content url{};
|
||||
@@ -622,9 +774,12 @@ namespace lwsf { namespace internal
|
||||
if (url.schema == "https")
|
||||
use_ssl = true;
|
||||
|
||||
epee::net_utils::http::login login{daemon_username, daemon_password};
|
||||
const epee::net_utils::ssl_options_t options{
|
||||
use_ssl ? epee::net_utils::ssl_support_t::e_ssl_support_disabled : epee::net_utils::ssl_support_t::e_ssl_support_enabled
|
||||
boost::optional<epee::net_utils::http::login> login;
|
||||
if (!daemon_username.empty() || !daemon_password.empty())
|
||||
login.emplace(daemon_username, daemon_password);
|
||||
|
||||
epee::net_utils::ssl_options_t options{
|
||||
use_ssl ? epee::net_utils::ssl_support_t::e_ssl_support_enabled : epee::net_utils::ssl_support_t::e_ssl_support_disabled
|
||||
};
|
||||
|
||||
data_->client.set_server(std::move(url.host), std::to_string(url.port), std::move(login), std::move(options));
|
||||
@@ -703,17 +858,75 @@ namespace lwsf { namespace internal
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t wallet::balance(const uint32_t accountIndex) const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
|
||||
std::uint64_t balance = 0;
|
||||
for (const auto& tx : data_->primary.txes)
|
||||
{
|
||||
for (const auto& receive : tx.second->receives)
|
||||
{
|
||||
if (receive.second.recipient.maj_i == accountIndex)
|
||||
balance += receive.second.amount;
|
||||
}
|
||||
|
||||
for (const auto& spend : tx.second->spends)
|
||||
{
|
||||
if (spend.second.sender.maj_i == accountIndex)
|
||||
balance -= spend.second.amount;
|
||||
}
|
||||
}
|
||||
|
||||
return balance;
|
||||
}
|
||||
|
||||
uint64_t wallet::unlockedBalance(const uint32_t accountIndex) const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
const Monero::NetworkType net_type = data_->primary.type;
|
||||
const std::uint32_t chain_height = data_->blockchain_height;
|
||||
|
||||
std::uint64_t balance = 0;
|
||||
for (const auto& tx : data_->primary.txes)
|
||||
{
|
||||
const bool unlocked = tx.second->is_unlocked(chain_height, net_type);
|
||||
for (const auto& receive : tx.second->receives)
|
||||
{
|
||||
if (unlocked && receive.second.recipient.maj_i == accountIndex)
|
||||
balance += receive.second.amount;
|
||||
}
|
||||
|
||||
for (const auto& spend : tx.second->spends)
|
||||
{
|
||||
if (spend.second.sender.maj_i == accountIndex)
|
||||
balance -= spend.second.amount;
|
||||
}
|
||||
}
|
||||
|
||||
return balance;
|
||||
}
|
||||
|
||||
uint64_t wallet::blockChainHeight() const
|
||||
{
|
||||
const boost::unique_lock<boost::mutex> lock{data_->sync};
|
||||
return data_->blockchain_height;
|
||||
return data_->primary.scan_height;
|
||||
}
|
||||
|
||||
uint64_t wallet::daemonBlockChainHeight() const
|
||||
{ return blockChainHeight(); }
|
||||
{
|
||||
const boost::unique_lock<boost::mutex> lock{data_->sync};
|
||||
return data_->blockchain_height;
|
||||
}
|
||||
|
||||
uint64_t wallet::daemonBlockChainTargetHeight() const
|
||||
{ return blockChainHeight(); }
|
||||
{ return daemonBlockChainHeight(); }
|
||||
|
||||
bool wallet::synchronized() const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
return data_->blockchain_height == data_->primary.scan_height;
|
||||
}
|
||||
|
||||
void wallet::startRefresh()
|
||||
{
|
||||
@@ -732,7 +945,7 @@ namespace lwsf { namespace internal
|
||||
|
||||
bool wallet::refresh()
|
||||
{
|
||||
return set_error(data_->refresh(true));
|
||||
return set_error(data_->refresh(false));
|
||||
}
|
||||
|
||||
void wallet::refreshAsync()
|
||||
@@ -767,6 +980,63 @@ namespace lwsf { namespace internal
|
||||
return refresh_interval_.count();
|
||||
}
|
||||
|
||||
void wallet::addSubaddressAccount(const std::string& label)
|
||||
{
|
||||
data_->add_subaccount(label);
|
||||
}
|
||||
|
||||
std::size_t wallet::numSubaddressAccounts() const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
return data_->primary.subaccounts.size();
|
||||
}
|
||||
|
||||
|
||||
std::size_t wallet::numSubaddresses(const std::uint32_t accountIndex) const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
const auto account = data_->primary.subaccounts.find(accountIndex);
|
||||
if (account != data_->primary.subaccounts.end())
|
||||
return account->second.minor.size();
|
||||
set_critical(std::runtime_error{"numSubaddresses failed, " + std::to_string(accountIndex) + " does not exist"});
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string wallet::getSubaddressLabel(const std::uint32_t accountIndex, const std::uint32_t addressIndex) const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
const auto major = data_->primary.subaccounts.find(accountIndex);
|
||||
if (major != data_->primary.subaccounts.end())
|
||||
{
|
||||
const auto minor = major->second.minor.find(addressIndex);
|
||||
if (minor != major->second.minor.end())
|
||||
return minor->second.label;
|
||||
}
|
||||
set_critical(std::runtime_error{"getSubaddressLabel failed, " + std::to_string(accountIndex) + "," + std::to_string(addressIndex) + " does not exist"});
|
||||
return {};
|
||||
}
|
||||
|
||||
void wallet::setSubaddressLabel(const std::uint32_t accountIndex, const std::uint32_t addressIndex, const std::string &label)
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
auto major = data_->primary.subaccounts.find(accountIndex);
|
||||
if (major != data_->primary.subaccounts.end())
|
||||
{
|
||||
auto minor = major->second.minor.find(addressIndex);
|
||||
if (minor != major->second.minor.end())
|
||||
{
|
||||
minor->second.label = label;
|
||||
return;
|
||||
}
|
||||
}
|
||||
set_critical(std::runtime_error{"setSubaddressLabel failed, " + std::to_string(accountIndex) + "," + std::to_string(addressIndex) + " does not exist"});
|
||||
}
|
||||
|
||||
void wallet::disposeTransaction(Monero::PendingTransaction * t)
|
||||
{
|
||||
delete t;
|
||||
}
|
||||
|
||||
uint64_t wallet::estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
|
||||
Monero::PendingTransaction::Priority priority) const
|
||||
{
|
||||
@@ -805,6 +1075,13 @@ namespace lwsf { namespace internal
|
||||
return addressbook_.get();
|
||||
}
|
||||
|
||||
Monero::Subaddress* wallet::subaddress()
|
||||
{
|
||||
if (!subaddress_minor_)
|
||||
subaddress_minor_ = std::make_unique<subaddress_minor>(data_);
|
||||
return subaddress_minor_.get();
|
||||
}
|
||||
|
||||
Monero::TransactionHistory* wallet::history()
|
||||
{
|
||||
if (!history_)
|
||||
@@ -825,7 +1102,8 @@ namespace lwsf { namespace internal
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync_listener};
|
||||
data_->listener = listener;
|
||||
}
|
||||
listener->onSetWallet(this);
|
||||
if (listener)
|
||||
listener->onSetWallet(this);
|
||||
}
|
||||
|
||||
std::uint32_t wallet::defaultMixin() const
|
||||
@@ -842,15 +1120,17 @@ namespace lwsf { namespace internal
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
data_->primary.attributes.try_emplace(key).first->second = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string wallet::getCacheAttribute(const std::string &key) const
|
||||
{
|
||||
const boost::lock_guard<boost::mutex> lock{data_->sync};
|
||||
const auto iter = data_->primary.attributes.find(key);
|
||||
if (iter == data_->primary.attributes.end())
|
||||
return {};
|
||||
return iter->second;
|
||||
const auto attribute = data_->primary.attributes.find(key);
|
||||
if (attribute != data_->primary.attributes.end())
|
||||
return attribute->second;
|
||||
set_critical(std::runtime_error{"attribute " + key + " does not exist"});
|
||||
return {};
|
||||
}
|
||||
|
||||
bool wallet::setUserNote(const std::string &txid, const std::string ¬e)
|
||||
@@ -921,8 +1201,4 @@ namespace lwsf { namespace internal
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
bool wallet::checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
|
||||
{
|
||||
}
|
||||
}} // lwsf // internal
|
||||
|
||||
+138
-127
@@ -46,9 +46,7 @@ namespace internal
|
||||
{
|
||||
namespace backend { struct wallet; }
|
||||
|
||||
|
||||
//! \TODO Mark final when completely implemented
|
||||
class wallet : public Monero::Wallet
|
||||
class wallet final : public Monero::Wallet
|
||||
{
|
||||
enum class state : std::uint8_t { stop = 0, paused, skip_once, run };
|
||||
|
||||
@@ -56,8 +54,10 @@ namespace internal
|
||||
std::unique_ptr<Monero::AddressBook> addressbook_;
|
||||
std::unique_ptr<Monero::TransactionHistory> history_;
|
||||
std::unique_ptr<Monero::SubaddressAccount> subaddresses_;
|
||||
std::unique_ptr<Monero::Subaddress> subaddress_minor_;
|
||||
const std::string filename_;
|
||||
std::string password_;
|
||||
std::string language_;
|
||||
mutable std::string exception_error_;
|
||||
mutable std::error_code status_;
|
||||
boost::thread thread_;
|
||||
@@ -77,11 +77,19 @@ namespace internal
|
||||
void refresh_loop();
|
||||
|
||||
public:
|
||||
struct create_tag{};
|
||||
struct open_tag{};
|
||||
struct create{};
|
||||
struct from_keys{};
|
||||
struct from_mnemonic{};
|
||||
struct open{};
|
||||
|
||||
wallet(create_tag, Monero::NetworkType nettype, std::string filename, std::string password, std::uint64_t kdf_rounds);
|
||||
wallet(open_tag, Monero::NetworkType nettpe, std::string filename, std::string password, std::uint64_t kdf_rounds, std::shared_ptr<backend::wallet> data);
|
||||
static bool is_wallet_file(const std::string& path);
|
||||
static bool verify_password(std::string path, const std::string& password);
|
||||
static std::vector<std::string> find(const std::string& path);
|
||||
|
||||
wallet(create, Monero::NetworkType nettype, std::string filename, std::string password, std::uint64_t kdf_rounds);
|
||||
wallet(open, Monero::NetworkType nettpe, std::string filename, std::string password, std::uint64_t kdf_rounds);
|
||||
wallet(from_mnemonic, Monero::NetworkType nettype, std::string path, std::string password, std::uint64_t kdf_rounds, const std::string& mnemonic, const std::string& seed_offset);
|
||||
wallet(from_keys, Monero::NetworkType nettype, std::string path, std::string password, std::uint64_t kdf_rounds, const std::string& mnemonic, const std::string& seed_offset);
|
||||
|
||||
wallet(const wallet&) = delete;
|
||||
wallet(wallet&&) = delete;
|
||||
@@ -89,10 +97,10 @@ namespace internal
|
||||
wallet& operator=(const wallet&) = delete;
|
||||
wallet& operator=(wallet&&) = delete;
|
||||
|
||||
virtual std::string seed(const std::string& seed_offset = "") const = 0;
|
||||
virtual std::string seed(const std::string& seed_offset = "") const override;
|
||||
|
||||
virtual std::string getSeedLanguage() const = 0;
|
||||
virtual void setSeedLanguage(const std::string &arg) = 0;
|
||||
virtual std::string getSeedLanguage() const override;
|
||||
virtual void setSeedLanguage(const std::string &arg) override;
|
||||
|
||||
virtual int status() const override;
|
||||
virtual std::string errorString() const override;
|
||||
@@ -104,12 +112,12 @@ namespace internal
|
||||
virtual bool setDevicePin(const std::string &) override { return false; };
|
||||
virtual bool setDevicePassphrase(const std::string &) override { return false; };
|
||||
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
|
||||
virtual std::string path() const = 0;
|
||||
virtual std::string path() const override { return filename_; }
|
||||
virtual Monero::NetworkType nettype() const override;
|
||||
//! returns current hard fork info
|
||||
virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
|
||||
virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const override { /* TODO */ };
|
||||
//! check if hard fork rules should be used
|
||||
virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
|
||||
virtual bool useForkRules(uint8_t version, int64_t early_blocks) const override { /* TODO */ return false; };
|
||||
/*!
|
||||
* \brief integratedAddress - returns integrated address for current wallet address and given payment_id.
|
||||
* if passed "payment_id" param is an empty string or not-valid payment id string
|
||||
@@ -119,7 +127,7 @@ namespace internal
|
||||
* generated
|
||||
* \return - 106 characters string representing integrated address
|
||||
*/
|
||||
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
|
||||
virtual std::string integratedAddress(const std::string &payment_id) const override { /* TODO */ return {}; }
|
||||
|
||||
/*!
|
||||
* \brief secretViewKey - returns secret view key
|
||||
@@ -173,7 +181,7 @@ namespace internal
|
||||
* \brief keysFilename - returns keys filename. usually this formed as "wallet_filename".keys
|
||||
* \return
|
||||
*/
|
||||
virtual std::string keysFilename() const = 0;
|
||||
virtual std::string keysFilename() const override { return filename_; }
|
||||
/*!
|
||||
* \brief init - initializes wallet with daemon connection params.
|
||||
* if daemon_address is local address, "trusted daemon" will be set to true forcibly
|
||||
@@ -194,7 +202,7 @@ namespace internal
|
||||
* \param language
|
||||
* \return - true if created successfully
|
||||
*/
|
||||
virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
|
||||
virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const override { /* TODO */ return false; }
|
||||
|
||||
/*!
|
||||
* \brief setRefreshFromBlockHeight - start refresh from block height on recover
|
||||
@@ -214,14 +222,14 @@ namespace internal
|
||||
*
|
||||
* \param recoveringFromSeed - true/false
|
||||
*/
|
||||
virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0;
|
||||
virtual void setRecoveringFromSeed(bool recoveringFromSeed) override { /* TODO */ };
|
||||
|
||||
/*!
|
||||
* \brief setRecoveringFromDevice - set state to recovering from device
|
||||
*
|
||||
* \param recoveringFromDevice - true/false
|
||||
*/
|
||||
virtual void setRecoveringFromDevice(bool recoveringFromDevice) = 0;
|
||||
virtual void setRecoveringFromDevice(bool recoveringFromDevice) override { /* TODO */ }
|
||||
|
||||
/*!
|
||||
* \brief setSubaddressLookahead - set size of subaddress lookahead
|
||||
@@ -229,7 +237,7 @@ namespace internal
|
||||
* \param major - size fot the major index
|
||||
* \param minor - size fot the minor index
|
||||
*/
|
||||
virtual void setSubaddressLookahead(uint32_t major, uint32_t minor) = 0;
|
||||
virtual void setSubaddressLookahead(uint32_t major, uint32_t minor) override { /* TODO */ }
|
||||
|
||||
/**
|
||||
* @brief connectToDaemon - connects to the daemon. TODO: check if it can be removed
|
||||
@@ -245,32 +253,20 @@ namespace internal
|
||||
virtual void setTrustedDaemon(bool) override {}
|
||||
virtual bool trustedDaemon() const override { return true; }
|
||||
virtual bool setProxy(const std::string &address) override;
|
||||
virtual uint64_t balance(uint32_t accountIndex = 0) const = 0;
|
||||
uint64_t balanceAll() const {
|
||||
uint64_t result = 0;
|
||||
for (uint32_t i = 0; i < numSubaddressAccounts(); ++i)
|
||||
result += balance(i);
|
||||
return result;
|
||||
}
|
||||
virtual uint64_t unlockedBalance(uint32_t accountIndex = 0) const = 0;
|
||||
uint64_t unlockedBalanceAll() const {
|
||||
uint64_t result = 0;
|
||||
for (uint32_t i = 0; i < numSubaddressAccounts(); ++i)
|
||||
result += unlockedBalance(i);
|
||||
return result;
|
||||
}
|
||||
virtual uint64_t balance(uint32_t accountIndex = 0) const override;
|
||||
virtual uint64_t unlockedBalance(uint32_t accountIndex = 0) const override;
|
||||
|
||||
/**
|
||||
* @brief watchOnly - checks if wallet is watch only
|
||||
* @return - true if watch only
|
||||
*/
|
||||
virtual bool watchOnly() const = 0;
|
||||
virtual bool watchOnly() const override { /* TODO */ return false; }
|
||||
|
||||
/**
|
||||
* @brief isDeterministic - checks if wallet keys are deterministic
|
||||
* @return - true if deterministic
|
||||
*/
|
||||
virtual bool isDeterministic() const = 0;
|
||||
virtual bool isDeterministic() const override { return true; }
|
||||
|
||||
/**
|
||||
* @brief blockChainHeight - returns current blockchain height
|
||||
@@ -282,14 +278,14 @@ namespace internal
|
||||
* @brief approximateBlockChainHeight - returns approximate blockchain height calculated from date/time
|
||||
* @return
|
||||
*/
|
||||
virtual uint64_t approximateBlockChainHeight() const = 0;
|
||||
virtual uint64_t approximateBlockChainHeight() const override { /* TODO */ return 0; }
|
||||
|
||||
/**
|
||||
* @brief estimateBlockChainHeight - returns estimate blockchain height. More accurate than approximateBlockChainHeight,
|
||||
* uses daemon height and falls back to calculation from date/time
|
||||
* @return
|
||||
**/
|
||||
virtual uint64_t estimateBlockChainHeight() const = 0;
|
||||
virtual uint64_t estimateBlockChainHeight() const override { /* TODO */ return 0; }
|
||||
/**
|
||||
* @brief daemonBlockChainHeight - returns daemon blockchain height
|
||||
* @return 0 - in case error communicating with the daemon.
|
||||
@@ -308,36 +304,7 @@ namespace internal
|
||||
* @brief synchronized - checks if wallet was ever synchronized
|
||||
* @return
|
||||
*/
|
||||
virtual bool synchronized() const = 0;
|
||||
|
||||
static std::string displayAmount(uint64_t amount);
|
||||
static uint64_t amountFromString(const std::string &amount);
|
||||
static uint64_t amountFromDouble(double amount);
|
||||
static std::string genPaymentId();
|
||||
static bool paymentIdValid(const std::string &paiment_id);
|
||||
static bool addressValid(const std::string &str, Monero::NetworkType nettype);
|
||||
static bool addressValid(const std::string &str, bool testnet) // deprecated
|
||||
{
|
||||
return addressValid(str, testnet ? Monero::TESTNET : Monero::MAINNET);
|
||||
}
|
||||
static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, Monero::NetworkType nettype, std::string &error);
|
||||
static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error) // deprecated
|
||||
{
|
||||
return keyValid(secret_key_string, address_string, isViewKey, testnet ? Monero::TESTNET : Monero::MAINNET, error);
|
||||
}
|
||||
static std::string paymentIdFromAddress(const std::string &str, Monero::NetworkType nettype);
|
||||
static std::string paymentIdFromAddress(const std::string &str, bool testnet) // deprecated
|
||||
{
|
||||
return paymentIdFromAddress(str, testnet ? Monero::TESTNET : Monero::MAINNET);
|
||||
}
|
||||
static uint64_t maximumAllowedAmount();
|
||||
// Easylogger wrapper
|
||||
static void init(const char *argv0, const char *default_log_base_name) { init(argv0, default_log_base_name, "", true); }
|
||||
static void init(const char *argv0, const char *default_log_base_name, const std::string &log_path, bool console);
|
||||
static void debug(const std::string &category, const std::string &str);
|
||||
static void info(const std::string &category, const std::string &str);
|
||||
static void warning(const std::string &category, const std::string &str);
|
||||
static void error(const std::string &category, const std::string &str);
|
||||
virtual bool synchronized() const override;
|
||||
|
||||
/**
|
||||
* @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds)
|
||||
@@ -363,12 +330,12 @@ namespace internal
|
||||
* @brief rescanBlockchain - rescans the wallet, updating transactions from daemon
|
||||
* @return - true if refreshed successfully;
|
||||
*/
|
||||
virtual bool rescanBlockchain() = 0;
|
||||
virtual bool rescanBlockchain() override { /* TODO */ return false; }
|
||||
|
||||
/**
|
||||
* @brief rescanBlockchainAsync - rescans wallet asynchronously, starting from genesys
|
||||
*/
|
||||
virtual void rescanBlockchainAsync() = 0;
|
||||
virtual void rescanBlockchainAsync() override { /* TODO */ }
|
||||
|
||||
/**
|
||||
* @brief setAutoRefreshInterval - setup interval for automatic refresh.
|
||||
@@ -386,84 +353,93 @@ namespace internal
|
||||
* @brief addSubaddressAccount - appends a new subaddress account at the end of the last major index of existing subaddress accounts
|
||||
* @param label - the label for the new account (which is the as the label of the primary address (accountIndex,0))
|
||||
*/
|
||||
virtual void addSubaddressAccount(const std::string& label) = 0;
|
||||
virtual void addSubaddressAccount(const std::string& label) override;
|
||||
/**
|
||||
* @brief numSubaddressAccounts - returns the number of existing subaddress accounts
|
||||
*/
|
||||
virtual size_t numSubaddressAccounts() const = 0;
|
||||
virtual size_t numSubaddressAccounts() const override;
|
||||
/**
|
||||
* @brief numSubaddresses - returns the number of existing subaddresses associated with the specified subaddress account
|
||||
* @param accountIndex - the major index specifying the subaddress account
|
||||
*/
|
||||
virtual size_t numSubaddresses(uint32_t accountIndex) const = 0;
|
||||
virtual size_t numSubaddresses(uint32_t accountIndex) const override;
|
||||
/**
|
||||
* @brief addSubaddress - appends a new subaddress at the end of the last minor index of the specified subaddress account
|
||||
* @param accountIndex - the major index specifying the subaddress account
|
||||
* @param label - the label for the new subaddress
|
||||
*/
|
||||
virtual void addSubaddress(uint32_t accountIndex, const std::string& label) = 0;
|
||||
virtual void addSubaddress(uint32_t accountIndex, const std::string& label) override { /* TODO */ }
|
||||
/**
|
||||
* @brief getSubaddressLabel - gets the label of the specified subaddress
|
||||
* @param accountIndex - the major index specifying the subaddress account
|
||||
* @param addressIndex - the minor index specifying the subaddress
|
||||
*/
|
||||
virtual std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const = 0;
|
||||
virtual std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const override;
|
||||
/**
|
||||
* @brief setSubaddressLabel - sets the label of the specified subaddress
|
||||
* @param accountIndex - the major index specifying the subaddress account
|
||||
* @param addressIndex - the minor index specifying the subaddress
|
||||
* @param label - the new label for the specified subaddress
|
||||
*/
|
||||
virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0;
|
||||
virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) override;
|
||||
|
||||
/**
|
||||
* @brief multisig - returns current state of multisig wallet creation process
|
||||
* @return MultisigState struct
|
||||
*/
|
||||
virtual Monero::MultisigState multisig() const = 0;
|
||||
virtual Monero::MultisigState multisig() const override { /* TODO */ return {}; }
|
||||
/**
|
||||
* @brief getMultisigInfo
|
||||
* @return serialized and signed multisig info string
|
||||
*/
|
||||
virtual std::string getMultisigInfo() const = 0;
|
||||
virtual std::string getMultisigInfo() const override { /* TODO */ return {}; }
|
||||
/**
|
||||
* @brief makeMultisig - switches wallet in multisig state. The one and only creation phase for N / N wallets
|
||||
* @param info - vector of multisig infos from other participants obtained with getMulitisInfo call
|
||||
* @param threshold - number of required signers to make valid transaction. Must be <= number of participants
|
||||
* @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info
|
||||
*/
|
||||
virtual std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) = 0;
|
||||
virtual std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) override { /* TODO */ return {}; }
|
||||
/**
|
||||
* @brief exchange_multisig_keys - provides additional key exchange round for arbitrary multisig schemes (like N-1/N, M/N)
|
||||
* @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call
|
||||
* @param force_update_use_with_caution - force multisig account to update even if not all signers contribute round messages
|
||||
* @return new info string if more rounds required or an empty string if wallet creation is done
|
||||
*/
|
||||
virtual std::string exchangeMultisigKeys(const std::vector<std::string> &info, const bool force_update_use_with_caution) = 0;
|
||||
virtual std::string exchangeMultisigKeys(const std::vector<std::string> &info, const bool force_update_use_with_caution) override { /* TODO */ return {}; }
|
||||
/**
|
||||
* @brief getMultisigKeyExchangeBooster - obtain partial information for the key exchange round after the in-progress round,
|
||||
* to speed up another signer's key exchange process
|
||||
* @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call
|
||||
* @param threshold - number of required signers to make valid transaction. Must be <= number of participants.
|
||||
* @param num_signers - total number of multisig participants.
|
||||
* @return new info string if more rounds required or exception if no more rounds (i.e. no rounds to boost)
|
||||
*/
|
||||
virtual std::string getMultisigKeyExchangeBooster(const std::vector<std::string> &info, const uint32_t threshold, const uint32_t num_signers) override { /* TODO */ return {}; }
|
||||
/**
|
||||
* @brief exportMultisigImages - exports transfers' key images
|
||||
* @param images - output paramter for hex encoded array of images
|
||||
* @return true if success
|
||||
*/
|
||||
virtual bool exportMultisigImages(std::string& images) = 0;
|
||||
virtual bool exportMultisigImages(std::string& images) override { /* TODO */ return false; }
|
||||
/**
|
||||
* @brief importMultisigImages - imports other participants' multisig images
|
||||
* @param images - array of hex encoded arrays of images obtained with exportMultisigImages
|
||||
* @return number of imported images
|
||||
*/
|
||||
virtual size_t importMultisigImages(const std::vector<std::string>& images) = 0;
|
||||
virtual size_t importMultisigImages(const std::vector<std::string>& images) override { /* TODO */ return false; }
|
||||
/**
|
||||
* @brief hasMultisigPartialKeyImages - checks if wallet needs to import multisig key images from other participants
|
||||
* @return true if there are partial key images
|
||||
*/
|
||||
virtual bool hasMultisigPartialKeyImages() const = 0;
|
||||
virtual bool hasMultisigPartialKeyImages() const override { /* TODO */ return false; }
|
||||
|
||||
/**
|
||||
* @brief restoreMultisigTransaction creates PendingTransaction from signData
|
||||
* @param signData encrypted unsigned transaction. Obtained with PendingTransaction::multisigSignData
|
||||
* @return PendingTransaction
|
||||
*/
|
||||
virtual Monero::PendingTransaction* restoreMultisigTransaction(const std::string& signData) = 0;
|
||||
virtual Monero::PendingTransaction* restoreMultisigTransaction(const std::string& signData) override { /* TODO */ return nullptr; }
|
||||
|
||||
/*!
|
||||
* \brief createTransactionMultDest creates transaction with multiple destinations. if dst_addr is an integrated address, payment_id is ignored
|
||||
@@ -482,7 +458,7 @@ namespace internal
|
||||
Monero::optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
|
||||
Monero::PendingTransaction::Priority = Monero::PendingTransaction::Priority_Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) = 0;
|
||||
std::set<uint32_t> subaddr_indices = {}) override { /* TODO */ return nullptr; }
|
||||
|
||||
/*!
|
||||
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
|
||||
@@ -501,7 +477,7 @@ namespace internal
|
||||
std::optional<uint64_t> amount, uint32_t mixin_count,
|
||||
Monero::PendingTransaction::Priority = Monero::PendingTransaction::Priority_Low,
|
||||
uint32_t subaddr_account = 0,
|
||||
std::set<uint32_t> subaddr_indices = {}) = 0;
|
||||
std::set<uint32_t> subaddr_indices = {}) override { /* TODO */ return nullptr; }
|
||||
|
||||
/*!
|
||||
* \brief createSweepUnmixableTransaction creates transaction with unmixable outputs.
|
||||
@@ -509,28 +485,27 @@ namespace internal
|
||||
* after object returned
|
||||
*/
|
||||
|
||||
virtual Monero::PendingTransaction* createSweepUnmixableTransaction() = 0;
|
||||
virtual Monero::PendingTransaction* createSweepUnmixableTransaction() override { /* TODO */ return nullptr; }
|
||||
|
||||
/*!
|
||||
* \brief loadUnsignedTx - creates transaction from unsigned tx file
|
||||
* \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status()
|
||||
* after object returned
|
||||
*/
|
||||
virtual Monero::UnsignedTransaction* loadUnsignedTx(const std::string &unsigned_filename) = 0;
|
||||
virtual Monero::UnsignedTransaction* loadUnsignedTx(const std::string &unsigned_filename) override { /* TODO */ return nullptr; }
|
||||
|
||||
/*!
|
||||
* \brief submitTransaction - submits transaction in signed tx file
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool submitTransaction(const std::string &fileName) = 0;
|
||||
virtual bool submitTransaction(const std::string &fileName) override { /* TODO */ return false; }
|
||||
|
||||
|
||||
/*!
|
||||
* \brief disposeTransaction - destroys transaction object
|
||||
* \param t - pointer to the "PendingTransaction" object. Pointer is not valid after function returned;
|
||||
*/
|
||||
virtual void disposeTransaction(Monero::PendingTransaction * t) override final
|
||||
{}
|
||||
virtual void disposeTransaction(Monero::PendingTransaction * t) override;
|
||||
|
||||
/*!
|
||||
* \brief Estimates transaction fee.
|
||||
@@ -576,9 +551,45 @@ namespace internal
|
||||
*/
|
||||
virtual bool scanTransactions(const std::vector<std::string> &txids) override;
|
||||
|
||||
/*!
|
||||
* \brief setupBackgroundSync - setup background sync mode with just a view key
|
||||
* \param background_sync_type - the mode the wallet background syncs in
|
||||
* \param wallet_password
|
||||
* \param background_cache_password - custom password to encrypt background cache, only needed for custom password background sync type
|
||||
* \return - true on success
|
||||
*/
|
||||
virtual bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const Monero::optional<std::string> &background_cache_password) override { return true; }
|
||||
|
||||
/*!
|
||||
* \brief getBackgroundSyncType - get mode the wallet background syncs in
|
||||
* \return - the type, or off if type is unknown
|
||||
*/
|
||||
virtual BackgroundSyncType getBackgroundSyncType() const override { return Monero::Wallet::BackgroundSync_ReusePassword; }
|
||||
|
||||
/**
|
||||
* @brief startBackgroundSync - sync the chain in the background with just view key
|
||||
*/
|
||||
virtual bool startBackgroundSync() override { return true; }
|
||||
|
||||
/**
|
||||
* @brief stopBackgroundSync - bring back spend key and process background synced txs
|
||||
* \param wallet_password
|
||||
*/
|
||||
virtual bool stopBackgroundSync(const std::string &wallet_password) override { return false; }
|
||||
|
||||
/**
|
||||
* @brief isBackgroundSyncing - returns true if the wallet is background syncing
|
||||
*/
|
||||
virtual bool isBackgroundSyncing() const override { return true; }
|
||||
|
||||
/**
|
||||
* @brief isBackgroundWallet - returns true if the wallet is a background wallet
|
||||
*/
|
||||
virtual bool isBackgroundWallet() const override { return true; }
|
||||
|
||||
virtual Monero::TransactionHistory* history() override;
|
||||
virtual Monero::AddressBook* addressBook() override;
|
||||
virtual Monero::Subaddress* subaddress() = 0;
|
||||
virtual Monero::Subaddress* subaddress() override;
|
||||
virtual Monero::SubaddressAccount* subaddressAccount() override;
|
||||
virtual void setListener(Monero::WalletListener*) override;
|
||||
/*!
|
||||
@@ -604,7 +615,7 @@ namespace internal
|
||||
* \param key - the key
|
||||
* \return the attached string, or empty string if there is none
|
||||
*/
|
||||
virtual std::string getCacheAttribute(const std::string &key) const = 0;
|
||||
virtual std::string getCacheAttribute(const std::string &key) const override;
|
||||
/*!
|
||||
* \brief setUserNote - attach an arbitrary string note to a txid
|
||||
* \param txid - the transaction id to attach the note to
|
||||
@@ -619,24 +630,24 @@ namespace internal
|
||||
*/
|
||||
virtual std::string getUserNote(const std::string &txid) const override;
|
||||
virtual std::string getTxKey(const std::string &txid) const override;
|
||||
virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
|
||||
virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const = 0;
|
||||
virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
|
||||
virtual std::string getSpendProof(const std::string &txid, const std::string &message) const = 0;
|
||||
virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const = 0;
|
||||
virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) override { /* TODO */ return false; }
|
||||
virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const override { /* TODO */ return {}; }
|
||||
virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) override { /* TODO */ return false; }
|
||||
virtual std::string getSpendProof(const std::string &txid, const std::string &message) const override { /* TODO */ return {}; }
|
||||
virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const override { /* TODO */ return false; }
|
||||
/*!
|
||||
* \brief getReserveProof - Generates a proof that proves the reserve of unspent funds
|
||||
* Parameters `account_index` and `amount` are ignored when `all` is true
|
||||
*/
|
||||
virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const = 0;
|
||||
virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const = 0;
|
||||
virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const override { /* TODO */ return {}; }
|
||||
virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const override { /* TODO */ return false; }
|
||||
|
||||
/*
|
||||
* \brief signMessage - sign a message with the spend private key
|
||||
* \param message - the message to sign (arbitrary byte data)
|
||||
* \return the signature
|
||||
*/
|
||||
virtual std::string signMessage(const std::string &message, const std::string &address = "") = 0;
|
||||
virtual std::string signMessage(const std::string &message, const std::string &address = "") override { /* TODO */ return {}; }
|
||||
/*!
|
||||
* \brief verifySignedMessage - verify a signature matches a given message
|
||||
* \param message - the message (arbitrary byte data)
|
||||
@@ -644,14 +655,14 @@ namespace internal
|
||||
* \param signature - the signature
|
||||
* \return true if the signature verified, false otherwise
|
||||
*/
|
||||
virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0;
|
||||
virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const override { /* TODO */ return false; }
|
||||
|
||||
/*!
|
||||
* \brief signMultisigParticipant signs given message with the multisig public signer key
|
||||
* \param message message to sign
|
||||
* \return signature in case of success. Sets status to Error and return empty string in case of error
|
||||
*/
|
||||
virtual std::string signMultisigParticipant(const std::string &message) const = 0;
|
||||
virtual std::string signMultisigParticipant(const std::string &message) const override { /* TODO */ return {}; }
|
||||
/*!
|
||||
* \brief verifyMessageWithPublicKey verifies that message was signed with the given public key
|
||||
* \param message message
|
||||
@@ -659,78 +670,78 @@ namespace internal
|
||||
* \param signature signature of the message
|
||||
* \return true if the signature is correct. false and sets error state in case of error
|
||||
*/
|
||||
virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0;
|
||||
virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const override { /* TODO */ return false; }
|
||||
|
||||
virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0;
|
||||
virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const = 0;
|
||||
virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) override { /* TODO */ return false; }
|
||||
virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const override { /* TODO */ return {}; }
|
||||
|
||||
virtual std::string getDefaultDataDir() const = 0;
|
||||
virtual std::string getDefaultDataDir() const override { /* TODO */ return {}; }
|
||||
|
||||
/*
|
||||
* \brief rescanSpent - Rescan spent outputs - Can only be used with trusted daemon
|
||||
* \return true on success
|
||||
*/
|
||||
virtual bool rescanSpent() = 0;
|
||||
virtual bool rescanSpent() override { return false; }
|
||||
|
||||
/*
|
||||
* \brief setOffline - toggle set offline on/off
|
||||
* \param offline - true/false
|
||||
*/
|
||||
virtual void setOffline(bool offline) = 0;
|
||||
virtual bool isOffline() const = 0;
|
||||
virtual void setOffline(bool offline) override { /* TODO */ }
|
||||
virtual bool isOffline() const override { /* TODO */ return false; }
|
||||
|
||||
//! blackballs a set of outputs
|
||||
virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) = 0;
|
||||
virtual bool blackballOutputs(const std::vector<std::string> &outputs, bool add) override { return false; }
|
||||
|
||||
//! blackballs an output
|
||||
virtual bool blackballOutput(const std::string &amount, const std::string &offset) = 0;
|
||||
virtual bool blackballOutput(const std::string &amount, const std::string &offset) override { return false; }
|
||||
|
||||
//! unblackballs an output
|
||||
virtual bool unblackballOutput(const std::string &amount, const std::string &offset) = 0;
|
||||
virtual bool unblackballOutput(const std::string &amount, const std::string &offset) override { return false; }
|
||||
|
||||
//! gets the ring used for a key image, if any
|
||||
virtual bool getRing(const std::string &key_image, std::vector<uint64_t> &ring) const = 0;
|
||||
virtual bool getRing(const std::string &key_image, std::vector<uint64_t> &ring) const override { /* TODO */ return false; }
|
||||
|
||||
//! gets the rings used for a txid, if any
|
||||
virtual bool getRings(const std::string &txid, std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings) const = 0;
|
||||
virtual bool getRings(const std::string &txid, std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings) const override { /* TODO */ return false; }
|
||||
|
||||
//! sets the ring used for a key image
|
||||
virtual bool setRing(const std::string &key_image, const std::vector<uint64_t> &ring, bool relative) = 0;
|
||||
virtual bool setRing(const std::string &key_image, const std::vector<uint64_t> &ring, bool relative) override { /* TODO */ return false; }
|
||||
|
||||
//! sets whether pre-fork outs are to be segregated
|
||||
virtual void segregatePreForkOutputs(bool segregate) = 0;
|
||||
virtual void segregatePreForkOutputs(bool segregate) override { /* TODO */ }
|
||||
|
||||
//! sets the height where segregation should occur
|
||||
virtual void segregationHeight(uint64_t height) = 0;
|
||||
virtual void segregationHeight(uint64_t height) override { /* TODO */ }
|
||||
|
||||
//! secondary key reuse mitigation
|
||||
virtual void keyReuseMitigation2(bool mitigation) = 0;
|
||||
virtual void keyReuseMitigation2(bool mitigation) override { /* TODO */ }
|
||||
|
||||
//! locks/unlocks the keys file; returns true on success
|
||||
virtual bool lockKeysFile() = 0;
|
||||
virtual bool unlockKeysFile() = 0;
|
||||
virtual bool lockKeysFile() override { return false; }
|
||||
virtual bool unlockKeysFile() override { return true; }
|
||||
//! returns true if the keys file is locked
|
||||
virtual bool isKeysFileLocked() = 0;
|
||||
virtual bool isKeysFileLocked() override { return false; }
|
||||
|
||||
/*!
|
||||
* \brief Queries backing device for wallet keys
|
||||
* \return Device they are on
|
||||
*/
|
||||
virtual Device getDeviceType() const = 0;
|
||||
virtual Device getDeviceType() const override { /* TODO */ return {}; }
|
||||
|
||||
//! cold-device protocol key image sync
|
||||
virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) = 0;
|
||||
virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) override { /* TODO */ return 0; }
|
||||
|
||||
//! shows address on device display
|
||||
virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) = 0;
|
||||
virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) override { /* TODO */ }
|
||||
|
||||
//! attempt to reconnect to hardware device
|
||||
virtual bool reconnectDevice() = 0;
|
||||
virtual bool reconnectDevice() override { /* TODO */ return false; }
|
||||
|
||||
//! get bytes received
|
||||
virtual uint64_t getBytesReceived() = 0;
|
||||
virtual uint64_t getBytesReceived() override { /* TODO */ return 0; }
|
||||
|
||||
//! get bytes sent
|
||||
virtual uint64_t getBytesSent() = 0;
|
||||
virtual uint64_t getBytesSent() override { /* TODO */ return 0; }
|
||||
};
|
||||
}} // lwsf // internal
|
||||
|
||||
+35
-20
@@ -96,9 +96,7 @@ namespace lwsf
|
||||
*/
|
||||
Monero::Wallet* createWallet(const std::string &path, const std::string &password, const std::string &language, Monero::NetworkType nettype, uint64_t kdf_rounds) override
|
||||
{
|
||||
return new wallet{
|
||||
wallet::create_tag{}, nettype, path, password, kdf_rounds
|
||||
};
|
||||
return new wallet{wallet::create{}, nettype, path, password, kdf_rounds};
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -113,15 +111,11 @@ namespace lwsf
|
||||
|
||||
Monero::Wallet* openWallet(const std::string &path, const std::string &password, Monero::NetworkType nettype, uint64_t kdf_rounds, Monero::WalletListener* listener) override
|
||||
{
|
||||
auto data = std::make_shared<backend::wallet>();
|
||||
data->listener = std::move(listener);
|
||||
auto out = std::make_unique<wallet>(
|
||||
wallet::open_tag{}, nettype, path, password, kdf_rounds, data
|
||||
wallet::open{}, nettype, path, password, kdf_rounds
|
||||
);
|
||||
|
||||
// wallet file should be fully loaded in construction of `wallet`.
|
||||
if (data->listener)
|
||||
data->listener->onSetWallet(out.get());
|
||||
if (listener)
|
||||
out->setListener(listener);
|
||||
return out.release();
|
||||
}
|
||||
|
||||
@@ -140,7 +134,11 @@ namespace lwsf
|
||||
Monero::NetworkType nettype = Monero::MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1,
|
||||
const std::string &seed_offset = {}) override final
|
||||
{
|
||||
//! \TODO Complete
|
||||
auto out = std::make_unique<wallet>(
|
||||
wallet::from_mnemonic{}, nettype, path, password, kdf_rounds, mnemonic, seed_offset
|
||||
);
|
||||
out->setRefreshFromBlockHeight(restoreHeight);
|
||||
return out.release();
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -154,6 +152,7 @@ namespace lwsf
|
||||
*/
|
||||
Monero::Wallet* recoveryWallet(const std::string &path, const std::string &mnemonic, Monero::NetworkType nettype, uint64_t restoreHeight = 0) override
|
||||
{
|
||||
return recoveryWallet(path, "", mnemonic, nettype, restoreHeight);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -236,9 +235,10 @@ namespace lwsf
|
||||
*/
|
||||
bool closeWallet(Monero::Wallet* const wallet, const bool store) override
|
||||
{
|
||||
const std::unique_ptr<Monero::Wallet> destroy{wallet};
|
||||
if (wallet && store)
|
||||
wallet->store(wallet->filename());
|
||||
if (wallet && store && !wallet->store({}))
|
||||
return false;
|
||||
delete wallet;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -252,7 +252,7 @@ namespace lwsf
|
||||
*/
|
||||
bool walletExists(const std::string &path) override
|
||||
{
|
||||
return std::filesystem::exists(std::filesystem::path{path});
|
||||
return lwsf::internal::wallet::is_wallet_file(path);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -267,9 +267,9 @@ namespace lwsf
|
||||
* This function will fail when the wallet keys file is opened because the wallet program locks the keys file.
|
||||
* In this case, Wallet::unlockKeysFile() and Wallet::lockKeysFile() need to be called before and after the call to this function, respectively.
|
||||
*/
|
||||
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds) const override
|
||||
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool, uint64_t) const override
|
||||
{
|
||||
//! \TODO Complete
|
||||
lwsf::internal::wallet::verify_password(keys_file_name, password);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -294,7 +294,7 @@ namespace lwsf
|
||||
*/
|
||||
std::vector<std::string> findWallets(const std::string &path) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
return lwsf::internal::wallet::find(path);
|
||||
}
|
||||
|
||||
std::string errorString() const override { return error_; }
|
||||
@@ -302,13 +302,28 @@ namespace lwsf
|
||||
//! set the daemon address (hostname and port)
|
||||
void setDaemonAddress(const std::string &address) override
|
||||
{
|
||||
//! \TODO Complete
|
||||
epee::net_utils::http::url_content url{};
|
||||
if (!epee::net_utils::parse_url(address, url))
|
||||
throw std::runtime_error{"Invalid LWS URL: " + address};
|
||||
if (!url.m_uri_content.m_path.empty())
|
||||
throw std::runtime_error{"LWS URL contains path (unsupported)"};
|
||||
|
||||
bool use_ssl = false;
|
||||
if (url.schema == "https")
|
||||
use_ssl = true;
|
||||
|
||||
const epee::net_utils::ssl_options_t options{
|
||||
use_ssl ? epee::net_utils::ssl_support_t::e_ssl_support_disabled : epee::net_utils::ssl_support_t::e_ssl_support_enabled
|
||||
};
|
||||
|
||||
client_.set_server(std::move(url.host), std::to_string(url.port), boost::none, std::move(options));
|
||||
}
|
||||
|
||||
//! returns whether the daemon can be reached, and its version number
|
||||
bool connected(uint32_t *version = NULL) override
|
||||
{
|
||||
version = 0;
|
||||
if (version)
|
||||
*version = 0;
|
||||
return client_.is_connected();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
# 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.
|
||||
|
||||
set(monero-lws-wire_sources basic_value.cpp error.cpp read.cpp write.cpp)
|
||||
set(monero-lws-wire_headers basic_value.h error.h field.h filters.h fwd.h json.h read.h traits.h vector.h write.h)
|
||||
set(lwsf-wire_sources basic_value.cpp error.cpp read.cpp write.cpp)
|
||||
set(lwsf-wire_headers basic_value.h error.h field.h filters.h fwd.h json.h read.h traits.h vector.h write.h)
|
||||
|
||||
add_library(monero-lws-wire ${monero-lws-wire_sources} ${monero-lws-wire_headers})
|
||||
target_include_directories(monero-lws-wire PUBLIC "${LMDB_INCLUDE}")
|
||||
target_link_libraries(monero-lws-wire PRIVATE monero::libraries)
|
||||
add_library(lwsf-wire ${lwsf-wire_sources} ${lwsf-wire_headers})
|
||||
target_include_directories(lwsf-wire PUBLIC "${LMDB_INCLUDE}")
|
||||
target_link_libraries(lwsf-wire PRIVATE monero::libraries)
|
||||
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(msgpack)
|
||||
|
||||
+10
-10
@@ -39,7 +39,7 @@ namespace crypto
|
||||
template<typename R>
|
||||
void read_bytes(R& source, crypto::secret_key& self)
|
||||
{
|
||||
source.binary(epee::as_mut_byte_span(unwrap(unwrap(self))));
|
||||
source.binary(epee::as_mut_byte_span(unwrap(unwrap(self))), /* exact */ true);
|
||||
}
|
||||
|
||||
template<typename W>
|
||||
@@ -51,13 +51,13 @@ namespace crypto
|
||||
|
||||
namespace wire
|
||||
{
|
||||
WIRE_DECLARE_BLOB(crypto::ec_scalar);
|
||||
WIRE_DECLARE_BLOB(crypto::hash);
|
||||
WIRE_DECLARE_BLOB(crypto::hash8);
|
||||
WIRE_DECLARE_BLOB(crypto::key_derivation);
|
||||
WIRE_DECLARE_BLOB(crypto::key_image);
|
||||
WIRE_DECLARE_BLOB(crypto::public_key);
|
||||
WIRE_DECLARE_BLOB(crypto::signature);
|
||||
WIRE_DECLARE_BLOB(crypto::view_tag);
|
||||
WIRE_DECLARE_BLOB(rct::key);
|
||||
WIRE_DECLARE_BLOB_NS(crypto::ec_scalar);
|
||||
WIRE_DECLARE_BLOB_NS(crypto::hash);
|
||||
WIRE_DECLARE_BLOB_NS(crypto::hash8);
|
||||
WIRE_DECLARE_BLOB_NS(crypto::key_derivation);
|
||||
WIRE_DECLARE_BLOB_NS(crypto::key_image);
|
||||
WIRE_DECLARE_BLOB_NS(crypto::public_key);
|
||||
WIRE_DECLARE_BLOB_NS(crypto::signature);
|
||||
WIRE_DECLARE_BLOB_NS(crypto::view_tag);
|
||||
WIRE_DECLARE_BLOB_NS(rct::key);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,6 @@ namespace wire
|
||||
}
|
||||
void write_bytes(writer& dest, const basic_value& source)
|
||||
{
|
||||
boost::apply_visitor([&dest] (const auto& val) { wire_write::bytes(dest, val); }, source.value);
|
||||
std::visit([&dest] (const auto& val) { wire_write::bytes(dest, val); }, source.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
# 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.
|
||||
|
||||
set(monero-lws_wire-json_sources error.cpp read.cpp write.cpp)
|
||||
set(monero-lws_wire-json_headers base.h error.h fwd.h read.h write.h)
|
||||
set(lwsf_wire-json_sources error.cpp read.cpp write.cpp)
|
||||
set(lwsf_wire-json_headers base.h error.h fwd.h read.h write.h)
|
||||
|
||||
add_library(monero-lws-wire-json ${monero-lws_wire-json_sources} ${monero-lws-wire-json_headers})
|
||||
target_link_libraries(monero-lws-wire-json monero::libraries monero-lws-wire)
|
||||
add_library(lwsf-wire-json ${lwsf_wire-json_sources} ${lwsf-wire-json_headers})
|
||||
target_link_libraries(lwsf-wire-json monero::libraries lwsf-wire)
|
||||
|
||||
@@ -43,9 +43,9 @@ namespace wire
|
||||
using output_type = json_slice_writer;
|
||||
|
||||
template<typename T>
|
||||
static std::error_code from_bytes(std::string&& source, T& dest)
|
||||
static std::error_code from_bytes(const epee::span<const char> source, T& dest)
|
||||
{
|
||||
return wire_read::from_bytes<input_type>(std::move(source), dest);
|
||||
return wire_read::from_bytes<input_type>(source, dest);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
|
||||
+19
-4
@@ -25,7 +25,7 @@
|
||||
// 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 "serialization/wire/json/read.h"
|
||||
#include "wire/json/read.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
@@ -34,9 +34,9 @@
|
||||
|
||||
#include "byte_stream.h"
|
||||
#include "hex.h"
|
||||
#include "serialization/wire/basic_value.h"
|
||||
#include "serialization/wire/error.h"
|
||||
#include "serialization/wire/json/error.h"
|
||||
#include "wire/basic_value.h"
|
||||
#include "wire/error.h"
|
||||
#include "wire/json/error.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -345,6 +345,21 @@ namespace wire
|
||||
}
|
||||
return std::string{std::move(str_buffer_)};
|
||||
}
|
||||
|
||||
std::size_t json_reader::string(epee::span<char> dest, bool exact)
|
||||
{
|
||||
{
|
||||
rapidjson_sax json_string{str_buffer_, error::schema::string};
|
||||
read_next_value(json_string);
|
||||
}
|
||||
|
||||
if (!exact && str_buffer_.size() < dest.size())
|
||||
dest = {dest.data(), str_buffer_.size()};
|
||||
if (str_buffer_.size() != dest.size())
|
||||
WIRE_DLOG_THROW(error::schema::string, "expected size " << dest.size() << " but got " << str_buffer_.size());
|
||||
return dest.size();
|
||||
|
||||
}
|
||||
|
||||
epee::byte_slice json_reader::binary()
|
||||
{
|
||||
|
||||
+32
-22
@@ -28,18 +28,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <rapidjson/reader.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "wire/field.h"
|
||||
#include "byte_slice.h" // monero/contrib/epee/include
|
||||
#include "wire/fwd.h"
|
||||
#include "wire/json/base.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/traits.h"
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
|
||||
namespace wire
|
||||
{
|
||||
@@ -48,7 +44,7 @@ namespace wire
|
||||
{
|
||||
struct rapidjson_sax;
|
||||
|
||||
std::string source_;
|
||||
std::string str_buffer_;
|
||||
rapidjson::Reader reader_;
|
||||
|
||||
void read_next_value(rapidjson_sax& handler);
|
||||
@@ -58,14 +54,21 @@ namespace wire
|
||||
//! Skips next value. \throw wire::exception if invalid JSON syntax.
|
||||
void skip_value();
|
||||
|
||||
//! \throw wire::exception if next token not `[`.
|
||||
std::size_t do_start_array(std::size_t) override final;
|
||||
|
||||
//! \throw wire::exception if next token not `{`.
|
||||
std::size_t do_start_object() override final;
|
||||
|
||||
public:
|
||||
explicit json_reader(std::string&& source);
|
||||
//! `source` must "outlive" `json_reader`.
|
||||
explicit json_reader(epee::span<const char> source);
|
||||
|
||||
//! \throw wire::exception if JSON parsing is incomplete.
|
||||
void check_complete() const override final;
|
||||
|
||||
//! \throw wire::exception if array, object, or end of stream.
|
||||
basic_value basic();
|
||||
basic_value basic() override final;
|
||||
|
||||
//! \throw wire::exception if next token not a boolean.
|
||||
bool boolean() override final;
|
||||
@@ -76,7 +79,7 @@ namespace wire
|
||||
//! \throw wire::exception if next token not an unsigned integer.
|
||||
std::uintmax_t unsigned_integer() override final;
|
||||
|
||||
//! \throw wire::exception if next token is not an integer encoded as string OR unsigned integer
|
||||
//! \throw wire::exception if next token is not an integer encoded as string
|
||||
std::uintmax_t safe_unsigned_integer();
|
||||
|
||||
//! \throw wire::exception if next token not a valid real number
|
||||
@@ -85,23 +88,30 @@ namespace wire
|
||||
//! \throw wire::exception if next token not a string
|
||||
std::string string() override final;
|
||||
|
||||
/*! Copy upcoming string directly into `dest`.
|
||||
\throw wire::exception if next value not string
|
||||
\throw wire::exception if next string exceeds `dest.size())`
|
||||
\throw wire::exception if `exact == true` and next string size is not
|
||||
`dest.size()`
|
||||
\return Number of bytes read into `dest`. */
|
||||
std::size_t string(epee::span<char> dest, bool exact) override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex
|
||||
epee::byte_slice binary() override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex into `dest`.
|
||||
void binary(epee::span<std::uint8_t> dest) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `[`.
|
||||
std::size_t start_array(std::size_t) override final;
|
||||
/* Copy upcoming binary, encoded as a hex string, into `dest`.
|
||||
\throw wire::exception if next value is not a string
|
||||
\throw wire::exception if next string is not valid hex.
|
||||
\throw wire::exception if `dest.size() * 2` is less than next string length
|
||||
\throw wire::exception if `exact == true` and next string length is
|
||||
not exactly `dest.size() * 2`
|
||||
\return Number of bytes read into `dest` (not number of bytes read
|
||||
from wire). */
|
||||
std::size_t binary(epee::span<std::uint8_t> dest, bool exact) override final;
|
||||
|
||||
//! Skips whitespace to next token. \return True if next token is eof or ']'.
|
||||
bool is_array_end(std::size_t count) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `{`.
|
||||
std::size_t start_object() override final;
|
||||
|
||||
|
||||
/*! \throw wire::exception if next token not key or `}`.
|
||||
\param[out] index of key match within `map`.
|
||||
\return True if another value to read. */
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
# 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.
|
||||
|
||||
set(monero-lws_wire-msgpack_sources error.cpp read.cpp write.cpp)
|
||||
set(monero-lws_wire-msgpack_headers base.h error.h fwd.h read.h write.h)
|
||||
set(lwsf_wire-msgpack_sources error.cpp read.cpp write.cpp)
|
||||
set(lwsf_wire-msgpack_headers base.h error.h fwd.h read.h write.h)
|
||||
|
||||
add_library(monero-lws-wire-msgpack ${monero-lws_wire-msgpack_sources} ${monero-lws-wire-msgpack_headers})
|
||||
target_link_libraries(monero-lws-wire-msgpack monero::libraries monero-lws-wire)
|
||||
add_library(lwsf-wire-msgpack ${lwsf_wire-msgpack_sources} ${lwsf-wire-msgpack_headers})
|
||||
target_link_libraries(lwsf-wire-msgpack monero::libraries lwsf-wire)
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "wire/basic_value.h"
|
||||
#include "wire/error.h"
|
||||
#include "wire/msgpack/error.h"
|
||||
|
||||
@@ -331,7 +332,7 @@ namespace wire
|
||||
return read_integer<std::intmax_t>(remaining_, next);
|
||||
}
|
||||
|
||||
std::uintmax_t msgpack_reader::do_unsigned_integer(const msgpack::tag next)
|
||||
std::uintmax_t msgpack_reader:: do_unsigned_integer(const msgpack::tag next)
|
||||
{
|
||||
return read_integer<std::uintmax_t>(remaining_, next);
|
||||
}
|
||||
@@ -366,6 +367,62 @@ namespace wire
|
||||
WIRE_DLOG_THROW_(error::msgpack::incomplete);
|
||||
}
|
||||
|
||||
basic_value msgpack_reader::basic()
|
||||
{
|
||||
update_tags_remaining();
|
||||
const auto next = get_tag();
|
||||
|
||||
if (std::uint8_t(next) <= msgpack::ftag_unsigned::max())
|
||||
return {std::uintmax_t(next)};
|
||||
if (msgpack::ftag_signed::matches(next))
|
||||
return {do_integer(next)};
|
||||
|
||||
if (msgpack::ftag_string::matches(next))
|
||||
{
|
||||
const auto bytes = read_string(remaining_, next);
|
||||
return {std::string{reinterpret_cast<const char*>(bytes.data()), bytes.size()}};
|
||||
}
|
||||
|
||||
switch (next)
|
||||
{
|
||||
case msgpack::tag::nil:
|
||||
return {nullptr};
|
||||
|
||||
case msgpack::tag::True:
|
||||
return {true};
|
||||
case msgpack::tag::False:
|
||||
return {false};
|
||||
|
||||
case msgpack::tag::int8:
|
||||
case msgpack::tag::int16:
|
||||
case msgpack::tag::int32:
|
||||
case msgpack::tag::int64:
|
||||
return {do_integer(next)};
|
||||
|
||||
case msgpack::tag::uint8:
|
||||
case msgpack::tag::uint16:
|
||||
case msgpack::tag::uint32:
|
||||
case msgpack::tag::uint64:
|
||||
return {do_unsigned_integer(next)};
|
||||
/*
|
||||
case msgpack::tag::binary8:
|
||||
case msgpack::tag::binary16:
|
||||
case msgpack::tag::binary32:
|
||||
return {epee::byte_slice{{read_binary(remaining_, next)}}}; */
|
||||
|
||||
case msgpack::tag::string8:
|
||||
case msgpack::tag::string16:
|
||||
case msgpack::tag::string32:
|
||||
{
|
||||
const auto bytes = read_string(remaining_, next);
|
||||
return {std::string{reinterpret_cast<const char*>(bytes.data()), bytes.size()}};
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW(error::schema::number, "expected a boolean, integer, float or string");
|
||||
}
|
||||
|
||||
bool msgpack_reader::boolean()
|
||||
{
|
||||
update_tags_remaining();
|
||||
@@ -413,6 +470,19 @@ namespace wire
|
||||
return std::string{reinterpret_cast<const char*>(bytes.data()), bytes.size()};
|
||||
}
|
||||
|
||||
std::size_t msgpack_reader::string(epee::span<char> dest, bool exact)
|
||||
{
|
||||
update_tags_remaining();
|
||||
const epee::span<const std::uint8_t> bytes = read_string(remaining_, get_tag());
|
||||
|
||||
if (!exact && bytes.size() < dest.size())
|
||||
dest = {dest.data(), bytes.size()};
|
||||
if (dest.size() != bytes.size())
|
||||
WIRE_DLOG_THROW(error::schema::string, "of size " << dest.size() << " but got " << bytes.size());
|
||||
std::memcpy(dest.data(), bytes.data(), dest.size());
|
||||
return dest.size();
|
||||
}
|
||||
|
||||
epee::byte_slice msgpack_reader::binary()
|
||||
{
|
||||
update_tags_remaining();
|
||||
@@ -421,16 +491,19 @@ namespace wire
|
||||
return source_.get_slice(begin, begin + bytes.size());
|
||||
}
|
||||
|
||||
void msgpack_reader::binary(epee::span<std::uint8_t> dest)
|
||||
std::size_t msgpack_reader::binary(epee::span<std::uint8_t> dest, const bool exact)
|
||||
{
|
||||
update_tags_remaining();
|
||||
const epee::span<const std::uint8_t> bytes = read_binary(remaining_, get_tag());
|
||||
if (!exact && bytes.size() < dest.size())
|
||||
dest = {dest.data(), bytes.size()};
|
||||
if (dest.size() != bytes.size())
|
||||
WIRE_DLOG_THROW(error::schema::fixed_binary, "of size " << dest.size() << " but got " << bytes.size());
|
||||
std::memcpy(dest.data(), bytes.data(), dest.size());
|
||||
return dest.size();
|
||||
}
|
||||
|
||||
std::size_t msgpack_reader::start_array(const std::size_t min_element_size)
|
||||
std::size_t msgpack_reader::do_start_array(const std::size_t min_element_size)
|
||||
{
|
||||
const std::size_t upcoming =
|
||||
read_count<msgpack::ftag_array, msgpack::array_types>(error::schema::array);
|
||||
@@ -440,7 +513,6 @@ namespace wire
|
||||
WIRE_DLOG_THROW(error::schema::array, upcoming << " array elements of at least " << min_element_size << " bytes each exceeds " << remaining_.size() << " remaining bytes");
|
||||
|
||||
tags_remaining_ += upcoming;
|
||||
increment_depth();
|
||||
return upcoming;
|
||||
}
|
||||
|
||||
@@ -452,7 +524,7 @@ namespace wire
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t msgpack_reader::start_object()
|
||||
std::size_t msgpack_reader::do_start_object()
|
||||
{
|
||||
const std::size_t upcoming =
|
||||
read_count<msgpack::ftag_object, msgpack::object_types>(error::schema::object);
|
||||
@@ -461,7 +533,6 @@ namespace wire
|
||||
if (limits<std::size_t>::max() - tags_remaining_ < upcoming * 2)
|
||||
WIRE_DLOG_THROW_(error::msgpack::max_tree_size);
|
||||
tags_remaining_ += upcoming * 2;
|
||||
increment_depth();
|
||||
return upcoming;
|
||||
}
|
||||
|
||||
|
||||
+19
-10
@@ -75,6 +75,13 @@ namespace wire
|
||||
template<typename T, typename U>
|
||||
std::size_t read_count(error::schema);
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `[`.
|
||||
std::size_t do_start_array(std::size_t min_element_size) override final;
|
||||
|
||||
//! \throw wire::exception if next token not `{`.
|
||||
std::size_t do_start_object() override final;
|
||||
|
||||
public:
|
||||
explicit msgpack_reader(epee::byte_slice&& source)
|
||||
: reader(nullptr), source_(std::move(source)), tags_remaining_(1)
|
||||
@@ -85,6 +92,9 @@ namespace wire
|
||||
//! \throw wire::exception if JSON parsing is incomplete.
|
||||
void check_complete() const override final;
|
||||
|
||||
//! \throw wire::exception if array, object, or end of stream.
|
||||
basic_value basic() override final;
|
||||
|
||||
//! \throw wire::exception if next token not a boolean.
|
||||
bool boolean() override final;
|
||||
|
||||
@@ -114,23 +124,22 @@ namespace wire
|
||||
//! \throw wire::exception if next token not a string
|
||||
std::string string() override final;
|
||||
|
||||
/*! Copy upcoming string directly into `dest`.
|
||||
\throw wire::exception if next value not string
|
||||
\throw wire::exception if next string exceeds `dest.size())`
|
||||
\throw wire::exception if `exact == true` and next string is not `dest.size()`
|
||||
\return Number of bytes read into `dest`. */
|
||||
std::size_t string(epee::span<char> dest, bool exact) override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex
|
||||
epee::byte_slice binary() override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex into `dest`.
|
||||
void binary(epee::span<std::uint8_t> dest) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `[`.
|
||||
std::size_t start_array(std::size_t min_element_size) override final;
|
||||
std::size_t binary(epee::span<std::uint8_t> dest, bool exact) override final;
|
||||
|
||||
//! \return true when `count == 0`.
|
||||
bool is_array_end(const std::size_t count) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `{`.
|
||||
std::size_t start_object() override final;
|
||||
|
||||
|
||||
/*! \throw wire::exception if next token not key or `}`.
|
||||
\param[out] index of key match within `map`.
|
||||
\return True if another value to read. */
|
||||
|
||||
+17
-5
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
// Copyright (c) 2021, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
@@ -42,8 +42,21 @@ void wire::reader::decrement_depth()
|
||||
--depth_;
|
||||
}
|
||||
|
||||
[[noreturn]] void wire::integer::throw_exception(std::intmax_t source, std::intmax_t min, std::intmax_t max)
|
||||
std::size_t wire::reader::start_array(std::size_t min_element_size)
|
||||
{
|
||||
increment_depth();
|
||||
return do_start_array(min_element_size);
|
||||
}
|
||||
|
||||
std::size_t wire::reader::start_object()
|
||||
{
|
||||
increment_depth();
|
||||
return do_start_object();
|
||||
}
|
||||
|
||||
[[noreturn]] void wire::integer::throw_exception(const std::intmax_t source, const std::intmax_t min, const std::intmax_t max)
|
||||
{
|
||||
static_assert(0 <= std::numeric_limits<std::intmax_t>::max(), "expected 0 <= intmax_t::max");
|
||||
static_assert(
|
||||
std::numeric_limits<std::intmax_t>::max() <= std::numeric_limits<std::uintmax_t>::max(),
|
||||
"expected intmax_t::max <= uintmax_t::max"
|
||||
@@ -53,9 +66,9 @@ void wire::reader::decrement_depth()
|
||||
else
|
||||
throw_exception(std::uintmax_t(source), std::uintmax_t(max));
|
||||
}
|
||||
[[noreturn]] void wire::integer::throw_exception(std::uintmax_t source, std::uintmax_t max)
|
||||
[[noreturn]] void wire::integer::throw_exception(const std::uintmax_t source, const std::uintmax_t max)
|
||||
{
|
||||
WIRE_DLOG_THROW(error::schema::smaller_integer, source << " given when " << max << "is maximum permitted");
|
||||
WIRE_DLOG_THROW(error::schema::smaller_integer, source << " given when " << max << " is maximum permitted");
|
||||
}
|
||||
|
||||
[[noreturn]] void wire_read::throw_exception(const wire::error::schema code, const char* display, epee::span<char const* const> names)
|
||||
@@ -72,4 +85,3 @@ void wire::reader::decrement_depth()
|
||||
WIRE_DLOG_THROW(code, display << (name ? name : ""));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+98
-56
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020-2023, The Monero Project
|
||||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
@@ -30,23 +30,52 @@
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "byte_slice.h" // monero/contrib/epee/include
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "byte_slice.h" // monero/contrib/epee/include
|
||||
#include "wire/error.h"
|
||||
#include "wire/field.h"
|
||||
#include "wire/fwd.h"
|
||||
#include "wire/traits.h"
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
|
||||
/*
|
||||
Custom types (e.g. `type` in namespace `ns`) can define an input function by:
|
||||
* `namespace wire { template<> struct is_blob<ns::type> : std::true_type {}; }`
|
||||
* `namespace wire { void read_bytes(writer&, ns::type&); }`
|
||||
* `namespace ns { void read_bytes(wire::writer&, type&); }`
|
||||
|
||||
See `traits.h` for `is_blob` requirements. `read_bytes` function can also
|
||||
specify derived type for faster output (i.e.
|
||||
`namespace ns { void read_bytes(wire::epee_reader&, type&); }`). Using the
|
||||
derived type allows the compiler to de-virtualize and allows for custom
|
||||
functions not defined by base interface. Using base interface allows for
|
||||
multiple formats with minimal instruction count. */
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Interface for converting "wire" (byte) formats to C/C++ objects without a DOM.
|
||||
class reader
|
||||
{
|
||||
{
|
||||
std::size_t depth_; //!< Tracks number of recursive objects and arrays
|
||||
|
||||
//! \throw wire::exception if max depth is reached
|
||||
void increment_depth();
|
||||
//! \throw std::logic_error if already `depth() == 0`.
|
||||
void decrement_depth();
|
||||
|
||||
/*! \param min_element_size of each array element in any format - if known.
|
||||
Derived types with explicit element count should verify available
|
||||
space, and throw a `wire::exception` on issues.
|
||||
\throw wire::exception if next value not array
|
||||
\throw wire::exception if not enough bytes for all array elements
|
||||
(with epee/msgpack which has specified number of elements).
|
||||
\return Number of values to read before calling `is_array_end()`. */
|
||||
virtual std::size_t do_start_array(std::size_t min_element_size) = 0;
|
||||
|
||||
//! \throw wire::exception if not object begin. \return State to be given to `key(...)` function.
|
||||
virtual std::size_t do_start_object() = 0;
|
||||
|
||||
protected:
|
||||
epee::span<const std::uint8_t> remaining_; //!< Derived class tracks unprocessed bytes here
|
||||
|
||||
@@ -54,22 +83,16 @@ namespace wire
|
||||
: depth_(0), remaining_(remaining)
|
||||
{}
|
||||
|
||||
reader(const reader&) = default;
|
||||
reader(reader&&) = default;
|
||||
reader& operator=(const reader&) = default;
|
||||
reader& operator=(reader&&) = default;
|
||||
|
||||
//! \throw wire::exception if max depth is reached
|
||||
void increment_depth();
|
||||
|
||||
//! \throw std::logic_error if already `depth() == 0`.
|
||||
void decrement_depth();
|
||||
|
||||
reader(const reader&) = delete;
|
||||
reader(reader&&) = delete;
|
||||
reader& operator=(const reader&) = delete;
|
||||
reader& operator=(reader&&) = delete;
|
||||
|
||||
public:
|
||||
struct key_map
|
||||
{
|
||||
const char* name;
|
||||
unsigned id; //<! For integer key formats;
|
||||
const char* name;
|
||||
unsigned id; //<! For integer key formats;
|
||||
};
|
||||
|
||||
//! \return Maximum read depth for both objects and arrays before erroring
|
||||
@@ -90,10 +113,13 @@ namespace wire
|
||||
//! \throw wire::exception if parsing is incomplete.
|
||||
virtual void check_complete() const = 0;
|
||||
|
||||
//! \throw wire::exception if array, object, or end of stream.
|
||||
virtual basic_value basic() = 0;
|
||||
|
||||
//! \throw wire::exception if next value not a boolean.
|
||||
virtual bool boolean() = 0;
|
||||
|
||||
//! \throw wire::exception if next value not an integer.
|
||||
//! \throw wire::expception if next value not an integer.
|
||||
virtual std::intmax_t integer() = 0;
|
||||
|
||||
//! \throw wire::exception if next value not an unsigned integer.
|
||||
@@ -105,30 +131,40 @@ namespace wire
|
||||
//! throw wire::exception if next value not string
|
||||
virtual std::string string() = 0;
|
||||
|
||||
/*! Copy upcoming string directly into `dest`.
|
||||
\throw wire::exception if next value not string
|
||||
\throw wire::exception if next string exceeds `dest.size())`
|
||||
\throw wire::exception if `exact == true` and next string is not `dest.size()`
|
||||
\return Number of bytes read into `dest`. */
|
||||
virtual std::size_t string(epee::span<char> dest, bool exact) = 0;
|
||||
|
||||
// ! \throw wire::exception if next value cannot be read as binary
|
||||
virtual epee::byte_slice binary() = 0;
|
||||
|
||||
//! \throw wire::exception if next value cannot be read as binary into `dest`.
|
||||
virtual void binary(epee::span<std::uint8_t> dest) = 0;
|
||||
/*! Copy upcoming binary directly into `dest`.
|
||||
\throw wire::exception if next value not binary
|
||||
\throw wire::exception if next binary exceeds `dest.size()`
|
||||
\throw wire::exception if `exact == true` and next binary is not `dest.size()`.
|
||||
\return Number of bytes read into `dest`. */
|
||||
virtual std::size_t binary(epee::span<std::uint8_t> dest, const bool exact) = 0;
|
||||
|
||||
/* \param min_element_size of each array element in any format - if known.
|
||||
/*! \param min_element_size of each array element in any format - if known.
|
||||
Derived types with explicit element count should verify available
|
||||
space, and throw a `wire::exception` on issues.
|
||||
\throw wire::exception if next value not array
|
||||
\throw wire::exception if not enough bytes for all array elements
|
||||
(with epee/msgpack which has specified number of elements).
|
||||
\return Number of values to read before calling `is_array_end()` */
|
||||
virtual std::size_t start_array(std::size_t min_element_size) = 0;
|
||||
\return Number of values to read before calling `is_array_end()`. */
|
||||
std::size_t start_array(std::size_t min_element_size);
|
||||
|
||||
//! \return True if there is another element to read.
|
||||
virtual bool is_array_end(std::size_t count) = 0;
|
||||
|
||||
//! \throw wire::exception if array end delimiter not present.
|
||||
void end_array() { decrement_depth(); }
|
||||
|
||||
|
||||
//! \throw wire::exception if not object begin. \return State to be given to `key(...)` function.
|
||||
virtual std::size_t start_object() = 0;
|
||||
std::size_t start_object();
|
||||
|
||||
/*! Read a key of an object field and match against a known list of keys.
|
||||
Skips or throws exceptions on unknown fields depending on implementation
|
||||
@@ -168,7 +204,7 @@ namespace wire
|
||||
|
||||
template<typename R, typename T>
|
||||
inline std::enable_if_t<is_blob<T>::value> read_bytes(R& source, T& dest)
|
||||
{ source.binary(epee::as_mut_byte_span(dest)); }
|
||||
{ source.binary(epee::as_mut_byte_span(dest), /*exact=*/ true); }
|
||||
|
||||
//! Use `read_bytes(...)` method if available for `T`.
|
||||
template<typename R, typename T>
|
||||
@@ -193,7 +229,7 @@ namespace wire
|
||||
"source must be signed integer type"
|
||||
);
|
||||
if (source < limit::min() || limit::max() < source)
|
||||
throw_exception(source, limit::min(), limit::max());
|
||||
throw_exception(source, limit::min(), limit::max());
|
||||
return static_cast<T>(source);
|
||||
}
|
||||
|
||||
@@ -213,7 +249,7 @@ namespace wire
|
||||
throw_exception(source, limit::max());
|
||||
return static_cast<T>(source);
|
||||
}
|
||||
}
|
||||
} // integer
|
||||
|
||||
//! read all current and future signed integer types
|
||||
template<typename R, typename T>
|
||||
@@ -234,11 +270,12 @@ namespace wire
|
||||
|
||||
namespace wire_read
|
||||
{
|
||||
/*! Don't add a function called `read_bytes` to this namespace, it will prevent
|
||||
ADL lookup. ADL lookup delays the function searching until the template
|
||||
is used instead of when its defined. This allows the unqualified calls to
|
||||
`read_bytes` in this namespace to "find" user functions that are declared
|
||||
after these functions (the technique behind `boost::serialization`). */
|
||||
/*! Don't add a function called `read_bytes` to this namespace, it will
|
||||
prevent 2-phase lookup. 2-phase lookup delays the function searching until
|
||||
the template is used instead of when its defined. This allows the
|
||||
unqualified calls to `read_bytes` in this namespace to "find" user
|
||||
functions that are declared after these functions (the technique behind
|
||||
`boost::serialization`). */
|
||||
|
||||
[[noreturn]] void throw_exception(wire::error::schema code, const char* display, epee::span<char const* const> name_list);
|
||||
|
||||
@@ -248,10 +285,13 @@ namespace wire_read
|
||||
read_bytes(source, dest); // ADL (searches every associated namespace)
|
||||
}
|
||||
|
||||
//! \return `T` converted from `source` or error.
|
||||
//! Use `source` to store information at `dest`
|
||||
template<typename R, typename T, typename U>
|
||||
inline std::error_code from_bytes(T&& source, U& dest)
|
||||
{
|
||||
if (wire::is_optional_root<U>::value && source.empty())
|
||||
return {};
|
||||
|
||||
try
|
||||
{
|
||||
R in{std::forward<T>(source)};
|
||||
@@ -262,6 +302,7 @@ namespace wire_read
|
||||
{
|
||||
return e.code();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -273,17 +314,18 @@ namespace wire_read
|
||||
}
|
||||
|
||||
// Insert to sorted containers
|
||||
template<typename R, typename T, typename V = std::pair<typename T::key_type, typename T::mapped_type>>
|
||||
template<typename R, typename T, typename V = typename T::value_type>
|
||||
inline auto array_insert(R& source, T& dest) -> decltype(dest.emplace_hint(dest.end(), std::declval<V>()), bool(true))
|
||||
{
|
||||
V val{};
|
||||
using value_type = typename wire::remove_const_key<V>::type;
|
||||
value_type val{};
|
||||
wire_read::bytes(source, val);
|
||||
dest.emplace_hint(dest.end(), std::move(val));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Insert into unsorted containers
|
||||
template<typename R, typename T, typename V = typename T::value_type>
|
||||
template<typename R, typename T>
|
||||
inline auto array_insert(R& source, T& dest) -> decltype(dest.emplace_back(), dest.back(), bool(true))
|
||||
{
|
||||
// more efficient to process the object in-place in many cases
|
||||
@@ -307,10 +349,6 @@ namespace wire_read
|
||||
if (max_element_count < count)
|
||||
throw_exception(wire::error::schema::array_max_element, "", nullptr);
|
||||
|
||||
// also checked by derived formats when count is known
|
||||
if (min_element_size && (source.remaining().size() / min_element_size) < count)
|
||||
throw_exception(wire::error::schema::array_min_size, "", nullptr);
|
||||
|
||||
dest.clear();
|
||||
wire::reserve(dest, count);
|
||||
|
||||
@@ -360,13 +398,13 @@ namespace wire_read
|
||||
}
|
||||
|
||||
template<typename R, typename T, unsigned I>
|
||||
inline void unpack_field(std::size_t, R& source, wire::field_<T, true, I>& dest)
|
||||
inline void unpack_field(R& source, wire::field_<T, true, I>& dest)
|
||||
{
|
||||
bytes(source, dest.get_value());
|
||||
}
|
||||
|
||||
template<typename R, typename T, unsigned I>
|
||||
inline void unpack_field(std::size_t, R& source, wire::field_<T, false, I>& dest)
|
||||
inline void unpack_field(R& source, wire::field_<T, false, I>& dest)
|
||||
{
|
||||
if (!bool(dest.get_value()))
|
||||
dest.get_value().emplace();
|
||||
@@ -395,10 +433,9 @@ namespace wire_read
|
||||
return (is_required() && !read_) ? field_.name : nullptr;
|
||||
}
|
||||
|
||||
|
||||
//! Set all entries in `map` related to this field (expand variant types!).
|
||||
template<std::size_t N>
|
||||
std::size_t set_mapping(std::size_t index, wire::reader::key_map (&map)[N])
|
||||
std::size_t set_mapping(std::size_t index, wire::reader::key_map (&map)[N]) noexcept
|
||||
{
|
||||
our_index_ = index;
|
||||
map[index].id = field_.id();
|
||||
@@ -415,7 +452,7 @@ namespace wire_read
|
||||
if (read_)
|
||||
throw_exception(wire::error::schema::invalid_key, "duplicate", {std::addressof(field_.name), 1});
|
||||
|
||||
unpack_field(index - our_index_, source, field_);
|
||||
unpack_field(source, field_);
|
||||
read_ = true;
|
||||
return 1 + is_required();
|
||||
}
|
||||
@@ -432,13 +469,11 @@ namespace wire_read
|
||||
// `expand_tracker_map` writes all `tracker` types to a table
|
||||
|
||||
template<std::size_t N>
|
||||
inline constexpr std::size_t expand_tracker_map(std::size_t index, const wire::reader::key_map (&)[N])
|
||||
{
|
||||
return index;
|
||||
}
|
||||
inline void expand_tracker_map(std::size_t index, const wire::reader::key_map (&)[N]) noexcept
|
||||
{}
|
||||
|
||||
template<std::size_t N, typename T, typename... U>
|
||||
inline void expand_tracker_map(std::size_t index, wire::reader::key_map (&map)[N], tracker<T>& head, tracker<U>&... tail)
|
||||
inline void expand_tracker_map(std::size_t index, wire::reader::key_map (&map)[N], tracker<T>& head, tracker<U>&... tail) noexcept
|
||||
{
|
||||
expand_tracker_map(head.set_mapping(index, map), map, tail...);
|
||||
}
|
||||
@@ -448,15 +483,16 @@ namespace wire_read
|
||||
{
|
||||
static constexpr const std::size_t total_subfields = wire::sum(fields.count()...);
|
||||
static_assert(total_subfields < 100, "algorithm uses too much stack space and linear searching");
|
||||
static_assert(sizeof...(T) <= total_subfields, "subfield parameter pack size mismatch");
|
||||
|
||||
std::size_t state = source.start_object();
|
||||
std::size_t required = wire::sum(std::size_t(fields.is_required())...);
|
||||
|
||||
wire::reader::key_map map[total_subfields] = {};
|
||||
wire::reader::key_map map[total_subfields + 1] = {}; // +1 for empty object
|
||||
expand_tracker_map(0, map, fields...);
|
||||
|
||||
std::size_t next = 0;
|
||||
while (source.key(map, state, next))
|
||||
while (source.key({map, total_subfields}, state, next))
|
||||
{
|
||||
switch (wire::sum(fields.try_read(source, next)...))
|
||||
{
|
||||
@@ -473,7 +509,7 @@ namespace wire_read
|
||||
|
||||
if (required)
|
||||
{
|
||||
const char* missing[] = {fields.name_if_missing()...};
|
||||
const char* missing[] = {fields.name_if_missing()..., nullptr};
|
||||
throw_exception(wire::error::schema::missing_key, "", missing);
|
||||
}
|
||||
|
||||
@@ -502,4 +538,10 @@ namespace wire
|
||||
{
|
||||
wire_read::object(source, wire_read::tracker<T>{std::move(fields)}...);
|
||||
}
|
||||
|
||||
template<typename R, typename... T>
|
||||
inline void object_fwd(const std::true_type /*is_read*/, R& source, T&&... fields)
|
||||
{
|
||||
wire::object(source, std::forward<T>(fields)...);
|
||||
}
|
||||
}
|
||||
|
||||
+40
-15
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
// Copyright (c) 2021-2024, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
@@ -27,17 +27,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#define WIRE_DECLARE_BLOB(type) \
|
||||
template<> \
|
||||
struct is_blob<type> \
|
||||
: std::true_type \
|
||||
#define WIRE_DECLARE_BLOB_NS(type) \
|
||||
template<> \
|
||||
struct is_blob<type> \
|
||||
: std::true_type \
|
||||
{}
|
||||
|
||||
#define WIRE_DECLARE_BLOB_NS(type) \
|
||||
namespace wire { WIRE_DECLARE_BLOB(type); }
|
||||
#define WIRE_DECLARE_BLOB(type) \
|
||||
namespace wire { WIRE_DECLARE_BLOB_NS(type); }
|
||||
|
||||
#define WIRE_DECLARE_OPTIONAL_ROOT(type) \
|
||||
template<> \
|
||||
struct is_optional_root<type> \
|
||||
: std::true_type \
|
||||
{}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
@@ -55,20 +62,32 @@ namespace wire
|
||||
template<typename T>
|
||||
using unwrap_reference_t = typename unwrap_reference<T>::type;
|
||||
|
||||
template<typename T>
|
||||
struct remove_const_key
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename K, typename V>
|
||||
struct remove_const_key<std::pair<const K, V>>
|
||||
{
|
||||
using type = std::pair<K, V>;
|
||||
};
|
||||
|
||||
/*! Mark `T` as an array for writing, and reading when
|
||||
`default_min_element_size<T::value_type>::value != 0`. See `array_` in
|
||||
`wrapper/array.h`. */
|
||||
`default_min_element_size<T::value_type>::value != 0`. See `array_` in
|
||||
`wrapper/array.h`. */
|
||||
template<typename T>
|
||||
struct is_array : std::false_type
|
||||
{};
|
||||
|
||||
/*! Mark `T` as fixed binary data for reading+writing. Concept requirements
|
||||
for reading:
|
||||
* `T` must be compatible with `epee::as_mut_byte_span` (`std::is_pod<T>`
|
||||
and no padding).
|
||||
Concept requirements for writing:
|
||||
* `T` must be compatible with `epee::as_byte_span` (std::is_pod<T>` and
|
||||
no padding). */
|
||||
for reading:
|
||||
* `T` must be compatible with `epee::as_mut_byte_span` (`std::is_pod<T>`
|
||||
and no padding).
|
||||
Concept requirements for writing:
|
||||
* `T` must be compatible with `epee::as_byte_span` (std::is_pod<T>` and
|
||||
no padding). */
|
||||
template<typename T>
|
||||
struct is_blob : std::false_type
|
||||
{};
|
||||
@@ -84,6 +103,12 @@ namespace wire
|
||||
: is_array<T> // all array types in old output engine were optional when empty
|
||||
{};
|
||||
|
||||
//! When `T` is being read as root object, allow an empty read buffer.
|
||||
template<typename T>
|
||||
struct is_optional_root
|
||||
: std::is_empty<T>
|
||||
{};
|
||||
|
||||
//! A constraint for `wire_read::array` where a max of `N` elements can be read.
|
||||
template<std::size_t N>
|
||||
struct max_element_count
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
# 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.
|
||||
|
||||
set(monero-lws-wire_sources variant.cpp)
|
||||
set(monero-lws-wire_headers variant.h)
|
||||
set(lwsf-wire_sources variant.cpp)
|
||||
set(lwsf-wire_headers variant.h)
|
||||
|
||||
add_library(monero-lws-wire-wrapper ${monero-lws-wire_sources} ${monero-lws-wire_headers})
|
||||
target_include_directories(monero-lws-wire-wrapper PUBLIC "${LMDB_INCLUDE}")
|
||||
target_link_libraries(monero-lws-wire-wrapper PRIVATE monero::libraries)
|
||||
add_library(lwsf-wire-wrapper ${lwsf-wire_sources} ${lwsf-wire_headers})
|
||||
target_include_directories(lwsf-wire-wrapper PUBLIC "${LMDB_INCLUDE}")
|
||||
target_link_libraries(lwsf-wire-wrapper PRIVATE monero::libraries)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user