Vote generator config
This commit is contained in:
parent
83f8a931fd
commit
fe275363f5
6 changed files with 63 additions and 20 deletions
|
|
@ -308,7 +308,7 @@ TEST (toml_config, daemon_config_deserialize_defaults)
|
||||||
ASSERT_EQ (conf.node.signature_checker_threads, defaults.node.signature_checker_threads);
|
ASSERT_EQ (conf.node.signature_checker_threads, defaults.node.signature_checker_threads);
|
||||||
ASSERT_EQ (conf.node.unchecked_cutoff_time, defaults.node.unchecked_cutoff_time);
|
ASSERT_EQ (conf.node.unchecked_cutoff_time, defaults.node.unchecked_cutoff_time);
|
||||||
ASSERT_EQ (conf.node.use_memory_pools, defaults.node.use_memory_pools);
|
ASSERT_EQ (conf.node.use_memory_pools, defaults.node.use_memory_pools);
|
||||||
ASSERT_EQ (conf.node.vote_generator_delay, defaults.node.vote_generator_delay);
|
ASSERT_EQ (conf.node.vote_generator.delay, defaults.node.vote_generator.delay);
|
||||||
ASSERT_EQ (conf.node.vote_minimum, defaults.node.vote_minimum);
|
ASSERT_EQ (conf.node.vote_minimum, defaults.node.vote_minimum);
|
||||||
ASSERT_EQ (conf.node.work_peers, defaults.node.work_peers);
|
ASSERT_EQ (conf.node.work_peers, defaults.node.work_peers);
|
||||||
ASSERT_EQ (conf.node.work_threads, defaults.node.work_threads);
|
ASSERT_EQ (conf.node.work_threads, defaults.node.work_threads);
|
||||||
|
|
@ -473,7 +473,6 @@ TEST (toml_config, daemon_config_deserialize_no_defaults)
|
||||||
signature_checker_threads = 999
|
signature_checker_threads = 999
|
||||||
unchecked_cutoff_time = 999
|
unchecked_cutoff_time = 999
|
||||||
use_memory_pools = false
|
use_memory_pools = false
|
||||||
vote_generator_delay = 999
|
|
||||||
vote_minimum = "999"
|
vote_minimum = "999"
|
||||||
work_peers = ["dev.org:999"]
|
work_peers = ["dev.org:999"]
|
||||||
work_threads = 999
|
work_threads = 999
|
||||||
|
|
@ -670,6 +669,9 @@ TEST (toml_config, daemon_config_deserialize_no_defaults)
|
||||||
duplicate_filter_cutoff = 999
|
duplicate_filter_cutoff = 999
|
||||||
minimum_fanout = 99
|
minimum_fanout = 99
|
||||||
|
|
||||||
|
[node.vote_generator]
|
||||||
|
delay = 999
|
||||||
|
|
||||||
[opencl]
|
[opencl]
|
||||||
device = 999
|
device = 999
|
||||||
enable = true
|
enable = true
|
||||||
|
|
@ -740,7 +742,7 @@ TEST (toml_config, daemon_config_deserialize_no_defaults)
|
||||||
ASSERT_NE (conf.node.signature_checker_threads, defaults.node.signature_checker_threads);
|
ASSERT_NE (conf.node.signature_checker_threads, defaults.node.signature_checker_threads);
|
||||||
ASSERT_NE (conf.node.unchecked_cutoff_time, defaults.node.unchecked_cutoff_time);
|
ASSERT_NE (conf.node.unchecked_cutoff_time, defaults.node.unchecked_cutoff_time);
|
||||||
ASSERT_NE (conf.node.use_memory_pools, defaults.node.use_memory_pools);
|
ASSERT_NE (conf.node.use_memory_pools, defaults.node.use_memory_pools);
|
||||||
ASSERT_NE (conf.node.vote_generator_delay, defaults.node.vote_generator_delay);
|
ASSERT_NE (conf.node.vote_generator.delay, defaults.node.vote_generator.delay);
|
||||||
ASSERT_NE (conf.node.vote_minimum, defaults.node.vote_minimum);
|
ASSERT_NE (conf.node.vote_minimum, defaults.node.vote_minimum);
|
||||||
ASSERT_NE (conf.node.work_peers, defaults.node.work_peers);
|
ASSERT_NE (conf.node.work_peers, defaults.node.work_peers);
|
||||||
ASSERT_NE (conf.node.work_threads, defaults.node.work_threads);
|
ASSERT_NE (conf.node.work_threads, defaults.node.work_threads);
|
||||||
|
|
|
||||||
|
|
@ -177,9 +177,9 @@ nano::node::node (std::shared_ptr<boost::asio::io_context> io_ctx_a, std::filesy
|
||||||
vote_processor{ *vote_processor_impl },
|
vote_processor{ *vote_processor_impl },
|
||||||
vote_cache_processor_impl{ std::make_unique<nano::vote_cache_processor> (config.vote_processor, vote_router, vote_cache, stats, logger) },
|
vote_cache_processor_impl{ std::make_unique<nano::vote_cache_processor> (config.vote_processor, vote_router, vote_cache, stats, logger) },
|
||||||
vote_cache_processor{ *vote_cache_processor_impl },
|
vote_cache_processor{ *vote_cache_processor_impl },
|
||||||
generator_impl{ std::make_unique<nano::vote_generator> (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false, loopback_channel) },
|
generator_impl{ std::make_unique<nano::vote_generator> (config.vote_generator, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false, loopback_channel) },
|
||||||
generator{ *generator_impl },
|
generator{ *generator_impl },
|
||||||
final_generator_impl{ std::make_unique<nano::vote_generator> (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true, loopback_channel) },
|
final_generator_impl{ std::make_unique<nano::vote_generator> (config.vote_generator, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true, loopback_channel) },
|
||||||
final_generator{ *final_generator_impl },
|
final_generator{ *final_generator_impl },
|
||||||
scheduler_impl{ std::make_unique<nano::scheduler::component> (config, *this, ledger, ledger_notifications, bucketing, active, online_reps, vote_cache, cementing_set, stats, logger) },
|
scheduler_impl{ std::make_unique<nano::scheduler::component> (config, *this, ledger, ledger_notifications, bucketing, active, online_reps, vote_cache, cementing_set, stats, logger) },
|
||||||
scheduler{ *scheduler_impl },
|
scheduler{ *scheduler_impl },
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,6 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const
|
||||||
toml.put ("block_processor_batch_max_time", block_processor_batch_max_time.count (), "The maximum time the block processor can continuously process blocks for.\ntype:milliseconds");
|
toml.put ("block_processor_batch_max_time", block_processor_batch_max_time.count (), "The maximum time the block processor can continuously process blocks for.\ntype:milliseconds");
|
||||||
toml.put ("allow_local_peers", allow_local_peers, "Enable or disable local host peering.\ntype:bool");
|
toml.put ("allow_local_peers", allow_local_peers, "Enable or disable local host peering.\ntype:bool");
|
||||||
toml.put ("vote_minimum", vote_minimum.to_string_dec (), "Local representatives do not vote if the delegated weight is under this threshold. Saves on system resources.\ntype:string,amount,raw");
|
toml.put ("vote_minimum", vote_minimum.to_string_dec (), "Local representatives do not vote if the delegated weight is under this threshold. Saves on system resources.\ntype:string,amount,raw");
|
||||||
toml.put ("vote_generator_delay", vote_generator_delay.count (), "Delay before votes are sent to allow for efficient bundling of hashes in votes.\ntype:milliseconds");
|
|
||||||
toml.put ("unchecked_cutoff_time", unchecked_cutoff_time.count (), "Number of seconds before deleting an unchecked entry.\nWarning: lower values (e.g., 3600 seconds, or 1 hour) may result in unsuccessful bootstraps, especially a bootstrap from scratch.\ntype:seconds");
|
toml.put ("unchecked_cutoff_time", unchecked_cutoff_time.count (), "Number of seconds before deleting an unchecked entry.\nWarning: lower values (e.g., 3600 seconds, or 1 hour) may result in unsuccessful bootstraps, especially a bootstrap from scratch.\ntype:seconds");
|
||||||
toml.put ("pow_sleep_interval", pow_sleep_interval.count (), "Time to sleep between batch work generation attempts. Reduces max CPU usage at the expense of a longer generation time.\ntype:nanoseconds");
|
toml.put ("pow_sleep_interval", pow_sleep_interval.count (), "Time to sleep between batch work generation attempts. Reduces max CPU usage at the expense of a longer generation time.\ntype:nanoseconds");
|
||||||
toml.put ("external_address", external_address, "The external address of this node (NAT). If not set, the node will request this information via UPnP.\ntype:string,ip");
|
toml.put ("external_address", external_address, "The external address of this node (NAT). If not set, the node will request this information via UPnP.\ntype:string,ip");
|
||||||
|
|
@ -245,6 +244,10 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const
|
||||||
block_processor.serialize (block_processor_l);
|
block_processor.serialize (block_processor_l);
|
||||||
toml.put_child ("block_processor", block_processor_l);
|
toml.put_child ("block_processor", block_processor_l);
|
||||||
|
|
||||||
|
nano::tomlconfig vote_generator_l;
|
||||||
|
vote_generator.serialize (vote_generator_l);
|
||||||
|
toml.put_child ("vote_generator", vote_generator_l);
|
||||||
|
|
||||||
nano::tomlconfig vote_processor_l;
|
nano::tomlconfig vote_processor_l;
|
||||||
vote_processor.serialize (vote_processor_l);
|
vote_processor.serialize (vote_processor_l);
|
||||||
toml.put_child ("vote_processor", vote_processor_l);
|
toml.put_child ("vote_processor", vote_processor_l);
|
||||||
|
|
@ -406,6 +409,12 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml)
|
||||||
block_processor.deserialize (config_l);
|
block_processor.deserialize (config_l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toml.has_key ("vote_generator"))
|
||||||
|
{
|
||||||
|
auto config_l = toml.get_required_child ("vote_generator");
|
||||||
|
vote_generator.deserialize (config_l);
|
||||||
|
}
|
||||||
|
|
||||||
if (toml.has_key ("vote_processor"))
|
if (toml.has_key ("vote_processor"))
|
||||||
{
|
{
|
||||||
auto config_l = toml.get_required_child ("vote_processor");
|
auto config_l = toml.get_required_child ("vote_processor");
|
||||||
|
|
@ -562,10 +571,6 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml)
|
||||||
toml.get_error ().set ("vote_minimum contains an invalid decimal amount");
|
toml.get_error ().set ("vote_minimum contains an invalid decimal amount");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto delay_l = vote_generator_delay.count ();
|
|
||||||
toml.get ("vote_generator_delay", delay_l);
|
|
||||||
vote_generator_delay = std::chrono::milliseconds (delay_l);
|
|
||||||
|
|
||||||
auto block_processor_batch_max_time_l = block_processor_batch_max_time.count ();
|
auto block_processor_batch_max_time_l = block_processor_batch_max_time.count ();
|
||||||
toml.get ("block_processor_batch_max_time", block_processor_batch_max_time_l);
|
toml.get ("block_processor_batch_max_time", block_processor_batch_max_time_l);
|
||||||
block_processor_batch_max_time = std::chrono::milliseconds (block_processor_batch_max_time_l);
|
block_processor_batch_max_time = std::chrono::milliseconds (block_processor_batch_max_time_l);
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
#include <nano/node/transport/tcp_config.hpp>
|
#include <nano/node/transport/tcp_config.hpp>
|
||||||
#include <nano/node/transport/tcp_listener.hpp>
|
#include <nano/node/transport/tcp_listener.hpp>
|
||||||
#include <nano/node/vote_cache.hpp>
|
#include <nano/node/vote_cache.hpp>
|
||||||
|
#include <nano/node/vote_generator.hpp>
|
||||||
#include <nano/node/vote_processor.hpp>
|
#include <nano/node/vote_processor.hpp>
|
||||||
#include <nano/node/vote_rebroadcaster.hpp>
|
#include <nano/node/vote_rebroadcaster.hpp>
|
||||||
#include <nano/node/websocketconfig.hpp>
|
#include <nano/node/websocketconfig.hpp>
|
||||||
|
|
@ -85,7 +86,6 @@ public:
|
||||||
nano::amount receive_minimum{ nano::nano_ratio / 1000 / 1000 }; // 0.000001 nano
|
nano::amount receive_minimum{ nano::nano_ratio / 1000 / 1000 }; // 0.000001 nano
|
||||||
nano::amount vote_minimum{ nano::Knano_ratio }; // 1000 nano
|
nano::amount vote_minimum{ nano::Knano_ratio }; // 1000 nano
|
||||||
nano::amount rep_crawler_weight_minimum{ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" };
|
nano::amount rep_crawler_weight_minimum{ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" };
|
||||||
std::chrono::milliseconds vote_generator_delay{ std::chrono::milliseconds (100) };
|
|
||||||
nano::amount online_weight_minimum{ 60000 * nano::Knano_ratio }; // 60 million nano
|
nano::amount online_weight_minimum{ 60000 * nano::Knano_ratio }; // 60 million nano
|
||||||
/*
|
/*
|
||||||
* The minimum vote weight that a representative must have for its vote to be counted.
|
* The minimum vote weight that a representative must have for its vote to be counted.
|
||||||
|
|
@ -150,6 +150,7 @@ public:
|
||||||
nano::rep_crawler_config rep_crawler;
|
nano::rep_crawler_config rep_crawler;
|
||||||
nano::block_processor_config block_processor;
|
nano::block_processor_config block_processor;
|
||||||
nano::active_elections_config active_elections;
|
nano::active_elections_config active_elections;
|
||||||
|
nano::vote_generator_config vote_generator;
|
||||||
nano::vote_processor_config vote_processor;
|
nano::vote_processor_config vote_processor;
|
||||||
nano::peer_history_config peer_history;
|
nano::peer_history_config peer_history;
|
||||||
nano::transport::tcp_config tcp;
|
nano::transport::tcp_config tcp;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <nano/lib/utility.hpp>
|
#include <nano/lib/utility.hpp>
|
||||||
#include <nano/node/local_vote_history.hpp>
|
#include <nano/node/local_vote_history.hpp>
|
||||||
#include <nano/node/network.hpp>
|
#include <nano/node/network.hpp>
|
||||||
|
#include <nano/node/node.hpp>
|
||||||
#include <nano/node/nodeconfig.hpp>
|
#include <nano/node/nodeconfig.hpp>
|
||||||
#include <nano/node/transport/inproc.hpp>
|
#include <nano/node/transport/inproc.hpp>
|
||||||
#include <nano/node/vote_generator.hpp>
|
#include <nano/node/vote_generator.hpp>
|
||||||
|
|
@ -16,21 +17,21 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
nano::vote_generator::vote_generator (nano::node_config const & config_a, nano::node & node_a, nano::ledger & ledger_a, nano::wallets & wallets_a, nano::vote_processor & vote_processor_a, nano::local_vote_history & history_a, nano::network & network_a, nano::stats & stats_a, nano::logger & logger_a, bool is_final_a, std::shared_ptr<nano::transport::channel> inproc_channel_a) :
|
nano::vote_generator::vote_generator (vote_generator_config const & config_a, nano::node & node_a, nano::ledger & ledger_a, nano::wallets & wallets_a, nano::vote_processor & vote_processor_a, nano::local_vote_history & history_a, nano::network & network_a, nano::stats & stats_a, nano::logger & logger_a, bool is_final_a, std::shared_ptr<nano::transport::channel> inproc_channel_a) :
|
||||||
config (config_a),
|
config (config_a),
|
||||||
node (node_a),
|
node (node_a),
|
||||||
ledger (ledger_a),
|
ledger (ledger_a),
|
||||||
wallets (wallets_a),
|
wallets (wallets_a),
|
||||||
vote_processor (vote_processor_a),
|
vote_processor (vote_processor_a),
|
||||||
history (history_a),
|
history (history_a),
|
||||||
spacing_impl{ std::make_unique<nano::vote_spacing> (config_a.network_params.voting.delay) },
|
spacing_impl{ std::make_unique<nano::vote_spacing> (node_a.network_params.voting.delay) },
|
||||||
spacing{ *spacing_impl },
|
spacing{ *spacing_impl },
|
||||||
network (network_a),
|
network (network_a),
|
||||||
stats (stats_a),
|
stats (stats_a),
|
||||||
logger (logger_a),
|
logger (logger_a),
|
||||||
is_final (is_final_a),
|
is_final (is_final_a),
|
||||||
inproc_channel{ inproc_channel_a },
|
inproc_channel{ inproc_channel_a },
|
||||||
vote_generation_queue{ stats, nano::stat::type::vote_generator, is_final ? nano::thread_role::name::voting_final : nano::thread_role::name::voting, /* single threaded */ 1, /* max queue size */ 1024 * 32, /* max batch size */ 256 }
|
vote_generation_queue{ stats, nano::stat::type::vote_generator, is_final ? nano::thread_role::name::voting_final : nano::thread_role::name::voting, /* single threaded */ 1, config.max_queue, config.batch_size }
|
||||||
{
|
{
|
||||||
vote_generation_queue.process_batch = [this] (auto & batch) {
|
vote_generation_queue.process_batch = [this] (auto & batch) {
|
||||||
process_batch (batch);
|
process_batch (batch);
|
||||||
|
|
@ -246,7 +247,7 @@ void nano::vote_generator::reply (nano::unique_lock<nano::mutex> & lock_a, reque
|
||||||
stats.add (nano::stat::type::requests, nano::stat::detail::requests_generated_hashes, stat::dir::in, hashes.size ());
|
stats.add (nano::stat::type::requests, nano::stat::detail::requests_generated_hashes, stat::dir::in, hashes.size ());
|
||||||
|
|
||||||
vote (hashes, roots, [this, channel = request_a.second] (std::shared_ptr<nano::vote> const & vote_a) {
|
vote (hashes, roots, [this, channel = request_a.second] (std::shared_ptr<nano::vote> const & vote_a) {
|
||||||
nano::confirm_ack confirm{ config.network_params.network, vote_a };
|
nano::confirm_ack confirm{ node.network_params.network, vote_a };
|
||||||
channel->send (confirm, nano::transport::traffic_type::vote_reply);
|
channel->send (confirm, nano::transport::traffic_type::vote_reply);
|
||||||
stats.inc (nano::stat::type::requests, nano::stat::detail::requests_generated_votes, stat::dir::in);
|
stats.inc (nano::stat::type::requests, nano::stat::detail::requests_generated_votes, stat::dir::in);
|
||||||
});
|
});
|
||||||
|
|
@ -292,8 +293,8 @@ void nano::vote_generator::run ()
|
||||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||||
while (!stopped)
|
while (!stopped)
|
||||||
{
|
{
|
||||||
// Wait for at most vote_generator_delay in case no further notification is received
|
// Wait for at most delay in case no further notification is received
|
||||||
condition.wait_for (lock, config.vote_generator_delay, [this] () {
|
condition.wait_for (lock, config.delay, [this] () {
|
||||||
return stopped || broadcast_predicate () || !requests.empty ();
|
return stopped || broadcast_predicate () || !requests.empty ();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -315,7 +316,7 @@ void nano::vote_generator::run ()
|
||||||
if (broadcast_predicate ())
|
if (broadcast_predicate ())
|
||||||
{
|
{
|
||||||
broadcast (lock);
|
broadcast (lock);
|
||||||
next_broadcast = std::chrono::steady_clock::now () + config.vote_generator_delay;
|
next_broadcast = std::chrono::steady_clock::now () + config.delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!requests.empty ())
|
if (!requests.empty ())
|
||||||
|
|
@ -363,3 +364,25 @@ nano::log::type nano::vote_generator::log_type () const
|
||||||
{
|
{
|
||||||
return is_final ? nano::log::type::vote_generator_final : nano::log::type::vote_generator;
|
return is_final ? nano::log::type::vote_generator_final : nano::log::type::vote_generator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* vote_generator_config
|
||||||
|
*/
|
||||||
|
|
||||||
|
nano::error nano::vote_generator_config::serialize (nano::tomlconfig & toml) const
|
||||||
|
{
|
||||||
|
toml.put ("max_queue", max_queue, "Maximum number of entries in the vote generation queue. \ntype:uint64");
|
||||||
|
toml.put ("batch_size", batch_size, "Maximum number of entries to process in a single batch. \ntype:uint64");
|
||||||
|
toml.put ("delay", delay.count (), "Delay before votes are sent to allow for efficient bundling of hashes in votes. \ntype:milliseconds");
|
||||||
|
|
||||||
|
return toml.get_error ();
|
||||||
|
}
|
||||||
|
|
||||||
|
nano::error nano::vote_generator_config::deserialize (nano::tomlconfig & toml)
|
||||||
|
{
|
||||||
|
toml.get ("max_queue", max_queue);
|
||||||
|
toml.get ("batch_size", batch_size);
|
||||||
|
toml.get_duration ("delay", delay);
|
||||||
|
|
||||||
|
return toml.get_error ();
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,18 @@ namespace mi = boost::multi_index;
|
||||||
|
|
||||||
namespace nano
|
namespace nano
|
||||||
{
|
{
|
||||||
|
class vote_generator_config final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
nano::error serialize (nano::tomlconfig & toml) const;
|
||||||
|
nano::error deserialize (nano::tomlconfig & toml);
|
||||||
|
|
||||||
|
public:
|
||||||
|
size_t max_queue{ 1024 * 32 };
|
||||||
|
size_t batch_size{ 256 };
|
||||||
|
std::chrono::milliseconds delay{ 100ms };
|
||||||
|
};
|
||||||
|
|
||||||
class vote_generator final
|
class vote_generator final
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
@ -34,7 +46,7 @@ private:
|
||||||
std::chrono::steady_clock::time_point next_broadcast = { std::chrono::steady_clock::now () };
|
std::chrono::steady_clock::time_point next_broadcast = { std::chrono::steady_clock::now () };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
vote_generator (nano::node_config const &, nano::node &, nano::ledger &, nano::wallets &, nano::vote_processor &, nano::local_vote_history &, nano::network &, nano::stats &, nano::logger &, bool is_final, std::shared_ptr<nano::transport::channel> inproc_channel);
|
vote_generator (vote_generator_config const &, nano::node &, nano::ledger &, nano::wallets &, nano::vote_processor &, nano::local_vote_history &, nano::network &, nano::stats &, nano::logger &, bool is_final, std::shared_ptr<nano::transport::channel> inproc_channel);
|
||||||
~vote_generator ();
|
~vote_generator ();
|
||||||
|
|
||||||
/** Queue items for vote generation, or broadcast votes already in cache */
|
/** Queue items for vote generation, or broadcast votes already in cache */
|
||||||
|
|
@ -63,7 +75,7 @@ private:
|
||||||
nano::log::type log_type () const;
|
nano::log::type log_type () const;
|
||||||
|
|
||||||
private: // Dependencies
|
private: // Dependencies
|
||||||
nano::node_config const & config;
|
vote_generator_config const & config;
|
||||||
nano::node & node;
|
nano::node & node;
|
||||||
nano::ledger & ledger;
|
nano::ledger & ledger;
|
||||||
nano::wallets & wallets;
|
nano::wallets & wallets;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue