RPC overhaul

High-level details:

This redesigns the RPC layer to make it much easier to work with,
decouples it from an embedded HTTP server, and gets the vast majority of
the RPC serialization and dispatch code out of a very commonly included
header.

There is unfortunately rather a lot of interconnected code here that
cannot be easily separated out into separate commits.  The full details
of what happens here are as follows:

Major details:
- All of the RPC code is now in a `cryptonote::rpc` namespace; this
  renames quite a bit to be less verbose: e.g. CORE_RPC_STATUS_OK
  becomes `rpc::STATUS_OK`, and `cryptonote::COMMAND_RPC_SOME_LONG_NAME`
  becomes `rpc::SOME_LONG_NAME` (or just SOME_LONG_NAME for code already
  working in the `rpc` namespace).
- `core_rpc_server` is now completely decoupled from providing any
  request protocol: it is now *just* the core RPC call handler.
- The HTTP RPC interface now lives in a new rpc/http_server.h; this code
  handles listening for HTTP requests and dispatching them to
  core_rpc_server, then sending the results back to the caller.
- There is similarly a rpc/lmq_server.h for LMQ RPC code; more details
  on this (and other LMQ specifics) below.
- RPC implementing code now returns the response object and throws when
  things go wrong which simplifies much of the rpc error handling.  They
  can throw anything; generic exceptions get logged and a generic
  "internal error" message gets returned to the caller, but there is
  also an `rpc_error` class to return an error code and message used by
  some json-rpc commands.
- RPC implementing functions now overload `core_rpc_server::invoke`
  following the pattern:

    RPC_BLAH_BLAH::response core_rpc_server::invoke(RPC_BLAH_BLAH::request&& req, rpc_context context);

  This overloading makes the code vastly simpler: all instantiations are
  now done with a small amount of generic instantiation code in a single
  .cpp rather than needing to go to hell and back with a nest of epee
  macros in a core header.
- each RPC endpoint is now defined by the RPC types themselves,
  including its accessible names and permissions, in
  core_rpc_server_commands_defs.h:
  - every RPC structure now has a static `names()` function that returns
    the names by which the end point is accessible.  (The first one is
    the primary, the others are for deprecated aliases).
  - RPC command wrappers define their permissions and type by inheriting
    from special tag classes:
    - rpc::RPC_COMMAND is a basic, admin-only, JSON command, available
      via JSON RPC.  *All* JSON commands are now available via JSON RPC,
      instead of the previous mix of some being at /foo and others at
      /json_rpc.  (Ones that were previously at /foo are still there for
      backwards compatibility; see `rpc::LEGACY` below).
    - rpc::PUBLIC specifies that the command should be available via a
      restricted RPC connection.
    - rpc::BINARY specifies that the command is not JSON, but rather is
      accessible as /name and takes and returns values in the magic epee
      binary "portable storage" (lol) data format.
    - rpc::LEGACY specifies that the command should be available via the
      non-json-rpc interface at `/name` for backwards compatibility (in
      addition to the JSON-RPC interface).
- some epee serialization got unwrapped and de-templatized so that it
  can be moved into a .cpp file with just declarations in the .h.  (This
  makes a *huge* difference for core_rpc_server_commands_defs.h and for
  every compilation unit that includes it which previously had to
  compile all the serialization code and then throw all by one copy away
  at link time).  This required some new macros so as to not break a ton
  of places that will use the old way putting everything in the headers;
  The RPC code uses this as does a few other places; there are comments
  in contrib/epee/include/serialization/keyvalue_serialization.h as to
  how to use it.
- Detemplatized a bunch of epee/storages code.  Most of it should have
  have been using templates at all (because it can only ever be called
  with one type!), and now it isn't.  This broke some things that didn't
  properly compile because of missing headers or (in one case) a messed
  up circular dependency.
- Significantly simplified a bunch of over-templatized serialization
  code.
- All RPC serialization definitions is now out of
  core_rpc_server_commands_defs.h and into a single .cpp file
  (core_rpc_server_commands_defs.cpp).
- core RPC no longer uses the disgusting
  BEGIN_URI_MAP2/MAP_URI_BLAH_BLAH macros.  This was a terrible design
  that forced slamming tons of code into a common header that didn't
  need to be there.
- epee::struct_init is gone.  It was a horrible hack that instiated
  multiple templates just so the coder could be so lazy and write
  `some_type var;` instead of properly value initializing with
  `some_type var{};`.
- Removed a bunch of useless crap from epee.  In particular, forcing
  extra template instantiations all over the place in order to nest
  return objects inside JSON RPC values is no longer needed, as are a
  bunch of stuff related to the above de-macroization of the code.
- get_all_service_nodes, get_service_nodes, and get_n_service_nodes are
  now combined into a single `get_service_nodes` (with deprecated
  aliases for the others), which eliminates a fair amount of
  duplication.  The biggest obstacle here was getting the requested
  fields reference passed through: this is now done by a new ability to
  stash a context in the serialization object that can be retrieved by a
  sub-serialized type.

LMQ-specifics:

- The LokiMQ instance moves into `cryptonote::core` rather than being
  inside cryptonote_protocol.  Currently the instance is used both for
  qnet and rpc calls (and so needs to be in a common place), but I also
  intend future PRs to use the batching code for job processing
  (replacing the current threaded job queue).
- rpc/lmq_server.h handles the actual LMQ-request-to-core-RPC glue.
  Unlike http_server it isn't technically running the whole LMQ stack
  from here, but the parallel name with http_server seemed appropriate.
- All RPC endpoints are supported by LMQ under the same names as defined
  generically, but prefixed with `rpc.` for public commands and `admin.`
  for restricted ones.
- service node keys are now always available, even when not running in
  `--service-node` mode: this is because we want the x25519 key for
  being able to offer CURVE encryption for lmq RPC end-points, and
  because it doesn't hurt to have them available all the time.  In the
  RPC layer this is now called "get_service_keys" (with
  "get_service_node_key" as an alias) since they aren't strictly only
  for service nodes.  This also means code needs to check
  m_service_node, and not m_service_node_keys, to tell if it is running
  as a service node.  (This is also easier to notice because
  m_service_node_keys got renamed to `m_service_keys`).
- Added block and mempool monitoring LMQ RPC endpoints: `sub.block` and
  `sub.mempool` subscribes the connection for new block and new mempool
  TX notifications.  The latter can notify on just blink txes, or all
  new mempool txes (but only new ones -- txes dumped from a block don't
  trigger it).  The client gets pushed a [`notify.block`, `height`,
  `hash`] or [`notify.tx`, `txhash`, `blob`] message when something
  arrives.

Minor details:
- rpc::version_t is now a {major,minor} pair.  Forcing everyone to pack
  and unpack a uint32_t was gross.
- Changed some macros to constexprs (e.g. CORE_RPC_ERROR_CODE_...).
  (This immediately revealed a couple of bugs in the RPC code that was
  assigning CORE_RPC_ERROR_CODE_... to a string, and it worked because
  the macro allows implicit conversion to a char).
- De-templatizing useless templates in epee (i.e. a bunch of templated
  types that were never invoked with different types) revealed a painful
  circular dependency between epee and non-epee code for tor_address and
  i2p_address.  This crap is now handled in a suitably named
  `net/epee_network_address_hack.cpp` hack because it really isn't
  trivial to extricate this mess.
- Removed `epee/include/serialization/serialize_base.h`.  Amazingly the
  code somehow still all works perfectly with this previously vital
  header removed.
- Removed bitrotted, unused epee "crypted_storage" and
  "gzipped_inmemstorage" code.
- Replaced a bunch of epee::misc_utils::auto_scope_leave_caller with
  LOKI_DEFERs.  The epee version involves quite a bit more instantiation
  and is ugly as sin.  Also made the `loki::defer` class invokable for
  some edge cases that need calling before destruction in particular
  conditions.
- Moved the systemd code around; it makes much more sense to do the
  systemd started notification as in daemon.cpp as late as possible
  rather than in core (when we can still have startup failures, e.g. if
  the RPC layer can't start).
- Made the systemd short status string available in the get_info RPC
  (and no longer require building with systemd).
- during startup, print (only) the x25519 when not in SN mode, and
  continue to print all three when in SN mode.
- DRYed out some RPC implementation code (such as set_limit)
- Made wallet_rpc stop using a raw m_wallet pointer
This commit is contained in:
Jason Rhinelander 2020-04-27 20:25:43 -03:00
parent d3a8baf9e7
commit 0e3f173c7f
101 changed files with 6553 additions and 7080 deletions

View File

@ -489,15 +489,16 @@ eof:
#endif
}
/// Throws std::out_of_range on bad command with what() set to the command name, otherwise
/// returns the result of the command (true generally means success, false means failure).
bool process_command(const std::vector<std::string>& cmd)
{
if(!cmd.size())
return false;
throw std::out_of_range{"(empty)"};
auto it = m_command_handlers.find(cmd.front());
if(it == m_command_handlers.end())
return false;
std::vector<std::string> cmd_local(cmd.begin()+1, cmd.end());
return it->second.first(cmd_local);
if (it == m_command_handlers.end())
throw std::out_of_range{cmd.front()};
return it->second.first(std::vector<std::string>{cmd.begin()+1, cmd.end()});
}
bool process_command(const std::string& cmd)

View File

@ -153,10 +153,5 @@ namespace misc_utils
return slc;
}
template<typename T> struct struct_init: T
{
struct_init(): T{} {}
};
}
}

View File

@ -40,6 +40,9 @@
#define MAX_LOG_FILE_SIZE 104850000 // 100 MB - 7600 bytes
#define MAX_LOG_FILES 50
#define CLOG_ENABLED(level, cat) ELPP->vRegistry()->allowed(el::Level::level, cat)
#define LOG_ENABLED(level) CLOG_ENABLED(level, LOKI_DEFAULT_LOG_CATEGORY)
#define MCLOG_TYPE(level, cat, type, x) do { \
if (ELPP->vRegistry()->allowed(level, cat)) { \
el::base::Writer(level, __FILE__, __LINE__, ELPP_FUNC, type).construct(cat) << x; \

View File

@ -56,8 +56,6 @@
#define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, &m_conn_context);
#define MAP_URI_AUTO_XML2(s_pattern, callback_f, command_type) //TODO: don't think i ever again will use xml - ambiguous and "overtagged" format
#define MAP_URI_AUTO_JON2_IF(s_pattern, callback_f, command_type, cond) \
else if((query_info.m_URI == s_pattern) && (cond)) \
{ \
@ -160,7 +158,7 @@
return true; \
} \
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> resp{}; \
epee::json_rpc::response<command_type::response> resp{}; \
resp.jsonrpc = "2.0"; \
resp.id = req.id;

View File

@ -1,167 +0,0 @@
#ifndef JSONRPC_PROTOCOL_HANDLER_H
#define JSONRPC_PROTOCOL_HANDLER_H
#include <cstdint>
#include <string>
#include "net/net_utils_base.h"
#include "jsonrpc_structs.h"
#include "storages/portable_storage.h"
#include "storages/portable_storage_template_helper.h"
namespace epee
{
namespace net_utils
{
namespace jsonrpc2
{
inline
std::string& make_error_resp_json(int64_t code, const std::string& message,
std::string& response_data,
const epee::serialization::storage_entry& id = nullptr)
{
epee::json_rpc::error_response rsp;
rsp.id = id;
rsp.jsonrpc = "2.0";
rsp.error.code = code;
rsp.error.message = message;
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_data, 0, false);
response_data += "\n";
return response_data;
}
template<class t_connection_context>
struct i_jsonrpc2_server_handler
{
virtual ~i_jsonrpc2_server_handler()
{}
virtual bool handle_rpc_request(const std::string& req_data,
std::string& resp_data,
t_connection_context& conn_context) = 0;
virtual bool init_server_thread()
{ return true; }
virtual bool deinit_server_thread()
{ return true; }
};
template<class t_connection_context>
struct jsonrpc2_server_config
{
i_jsonrpc2_server_handler<t_connection_context>* m_phandler;
critical_section m_lock;
};
template<class t_connection_context = net_utils::connection_context_base>
class jsonrpc2_connection_handler
{
public:
typedef t_connection_context connection_context;
typedef jsonrpc2_server_config<t_connection_context> config_type;
jsonrpc2_connection_handler(i_service_endpoint* psnd_hndlr,
config_type& config,
t_connection_context& conn_context)
: m_psnd_hndlr(psnd_hndlr),
m_config(config),
m_conn_context(conn_context),
m_is_stop_handling(false)
{}
virtual ~jsonrpc2_connection_handler()
{}
bool release_protocol()
{
return true;
}
virtual bool thread_init()
{
return true;
}
virtual bool thread_deinit()
{
return true;
}
void handle_qued_callback()
{}
bool after_init_connection()
{
return true;
}
virtual bool handle_recv(const void* ptr, size_t cb)
{
std::string buf((const char*)ptr, cb);
LOG_PRINT_L0("JSONRPC2_RECV: " << ptr << "\r\n" << buf);
bool res = handle_buff_in(buf);
return res;
}
private:
bool handle_buff_in(std::string& buf)
{
if(m_cache.size())
m_cache += buf;
else
m_cache.swap(buf);
m_is_stop_handling = false;
while (!m_is_stop_handling) {
std::string::size_type pos = match_end_of_request(m_cache);
if (std::string::npos == pos) {
m_is_stop_handling = true;
if (m_cache.size() > 4096) {
LOG_ERROR("jsonrpc2_connection_handler::handle_buff_in: Too long request");
return false;
}
break;
} else {
extract_cached_request_and_handle(pos);
}
if (!m_cache.size()) {
m_is_stop_handling = true;
}
}
return true;
}
bool extract_cached_request_and_handle(std::string::size_type pos)
{
std::string request_data(m_cache.begin(), m_cache.begin() + pos);
m_cache.erase(0, pos);
return handle_request_and_send_response(request_data);
}
bool handle_request_and_send_response(const std::string& request_data)
{
CHECK_AND_ASSERT_MES(m_config.m_phandler, false, "m_config.m_phandler is NULL!!!!");
std::string response_data;
LOG_PRINT_L3("JSONRPC2_REQUEST: >> \r\n" << request_data);
bool rpc_result = m_config.m_phandler->handle_rpc_request(request_data, response_data, m_conn_context);
LOG_PRINT_L3("JSONRPC2_RESPONSE: << \r\n" << response_data);
m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size());
return rpc_result;
}
std::string::size_type match_end_of_request(const std::string& buf)
{
std::string::size_type res = buf.find("\n");
if(std::string::npos != res) {
return res + 2;
}
return res;
}
protected:
i_service_endpoint* m_psnd_hndlr;
private:
config_type& m_config;
t_connection_context& m_conn_context;
std::string m_cache;
bool m_is_stop_handling;
};
}
}
}
#endif /* JSONRPC_PROTOCOL_HANDLER_H */

View File

@ -1,84 +0,0 @@
#ifndef JSONRPC_SERVER_HANDLERS_MAP_H
#define JSONRPC_SERVER_HANDLERS_MAP_H
#include <string>
#include "serialization/keyvalue_serialization.h"
#include "storages/portable_storage_template_helper.h"
#include "storages/portable_storage_base.h"
#include "jsonrpc_structs.h"
#include "jsonrpc_protocol_handler.h"
#define BEGIN_JSONRPC2_MAP(t_connection_context) \
bool handle_rpc_request(const std::string& req_data, \
std::string& resp_data, \
t_connection_context& m_conn_context) \
{ \
bool handled = false; \
uint64_t ticks = epee::misc_utils::get_tick_count(); \
epee::serialization::portable_storage ps; \
if (!ps.load_from_json(req_data)) \
{ \
epee::net_utils::jsonrpc2::make_error_resp_json(-32700, "Parse error", resp_data); \
return true; \
} \
epee::serialization::storage_entry id_; \
id_ = epee::serialization::storage_entry(std::string()); \
if (!ps.get_value("id", id_, nullptr)) \
{ \
epee::net_utils::jsonrpc2::make_error_resp_json(-32600, "Invalid Request", resp_data); \
return true; \
} \
std::string callback_name; \
if (!ps.get_value("method", callback_name, nullptr)) \
{ \
epee::net_utils::jsonrpc2::make_error_resp_json(-32600, "Invalid Request", resp_data, id_); \
return true; \
} \
if (false) return true; //just a stub to have "else if"
#define PREPARE_JSONRPC2_OBJECTS_FROM_JSON(command_type) \
handled = true; \
epee::json_rpc::request<command_type::request> req{}; \
if(!req.load(ps)) \
{ \
epee::net_utils::jsonrpc2::make_error_resp_json(-32602, "Invalid params", resp_data, req.id); \
return true; \
} \
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> resp{}; \
resp.jsonrpc = "2.0"; \
resp.id = req.id;
#define FINALIZE_JSONRPC2_OBJECTS_TO_JSON(method_name) \
uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
epee::serialization::store_t_to_json(resp, resp_data, 0, false); \
resp_data += "\n"; \
uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
LOG_PRINT("[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2);
#define MAP_JSONRPC2_WE(method_name, callback_f, command_type) \
else if (callback_name == method_name) \
{ \
PREPARE_JSONRPC2_OBJECTS_FROM_JSON(command_type) \
epee::json_rpc::error_response fail_resp{}; \
fail_resp.jsonrpc = "2.0"; \
fail_resp.id = req.id; \
if(!callback_f(req.params, resp.result, fail_resp.error, m_conn_context)) \
{ \
epee::serialization::store_t_to_json(fail_resp, resp_data, 0, false); \
resp_data += "\n"; \
return true; \
} \
FINALIZE_JSONRPC2_OBJECTS_TO_JSON(method_name) \
return true; \
}
#define END_JSONRPC2_MAP() \
epee::net_utils::jsonrpc2::make_error_resp_json(-32601, "Method not found", resp_data, id_); \
return true; \
}
#endif /* JSONRPC_SERVER_HANDLERS_MAP_H */

View File

@ -1,84 +0,0 @@
#ifndef JSONRPC_SERVER_IMPL_BASE_H
#define JSONRPC_SERVER_IMPL_BASE_H
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include "net/jsonrpc_protocol_handler.h"
#include "net/jsonrpc_server_handlers_map.h"
#include "net/abstract_tcp_server2.h"
namespace epee
{
template<class t_child_class, class t_connection_context = epee::net_utils::connection_context_base>
class jsonrpc_server_impl_base: public net_utils::jsonrpc2::i_jsonrpc2_server_handler<t_connection_context>
{
public:
jsonrpc_server_impl_base()
: m_net_server()
{}
explicit jsonrpc_server_impl_base(boost::asio::io_service& external_io_service)
: m_net_server(external_io_service)
{}
bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0")
{
//set self as callback handler
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port);
bool res = m_net_server.init_server(bind_port, bind_ip);
if (!res)
{
LOG_ERROR("Failed to bind server");
return false;
}
return true;
}
bool run(size_t threads_count, bool wait = true)
{
//go to loop
LOG_PRINT("Run net_service loop( " << threads_count << " threads)...", LOG_LEVEL_0);
if(!m_net_server.run_server(threads_count, wait))
{
LOG_ERROR("Failed to run net tcp server!");
}
if(wait)
LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0);
return true;
}
bool deinit()
{
return m_net_server.deinit_server();
}
bool timed_wait_server_stop(uint64_t ms)
{
return m_net_server.timed_wait_server_stop(ms);
}
bool send_stop_signal()
{
m_net_server.send_stop_signal();
return true;
}
int get_binded_port()
{
return m_net_server.get_binded_port();
}
protected:
net_utils::boosted_tcp_server<net_utils::jsonrpc2::jsonrpc2_connection_handler<t_connection_context> > m_net_server;
};
}
#endif /* JSONRPC_SERVER_IMPL_BASE_H */

View File

@ -15,10 +15,8 @@ namespace epee
{
std::string jsonrpc;
std::string method;
epee::serialization::storage_entry id;
t_param params;
request(): id{}, params{} {}
epee::serialization::storage_entry id{};
t_param params{};
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(jsonrpc)
@ -30,71 +28,40 @@ namespace epee
struct error
{
int64_t code;
int64_t code{0};
std::string message;
error(): code(0) {}
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(code)
KV_SERIALIZE(message)
END_KV_SERIALIZE_MAP()
};
struct dummy_error
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
struct dummy_result
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
template<typename t_param, typename t_error>
template<typename t_param, bool with_error = false>
struct response
{
std::string jsonrpc;
t_param result;
epee::serialization::storage_entry id;
t_error error;
response(): result{}, id(), error{} {}
t_param result{};
epee::serialization::storage_entry id{};
json_rpc::error error{};
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(jsonrpc)
KV_SERIALIZE(id)
KV_SERIALIZE(result)
KV_SERIALIZE(error)
if (with_error)
KV_SERIALIZE(error)
END_KV_SERIALIZE_MAP()
};
template<typename t_param>
struct response<t_param, dummy_error>
template<typename T>
using response_with_error = response<T, true>;
struct error_response
{
std::string jsonrpc;
t_param result;
epee::serialization::storage_entry id;
response(): result{}, id{} {}
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(jsonrpc)
KV_SERIALIZE(id)
KV_SERIALIZE(result)
END_KV_SERIALIZE_MAP()
};
template<typename t_error>
struct response<dummy_result, t_error>
{
std::string jsonrpc;
t_error error;
epee::serialization::storage_entry id;
response(): error{}, id{} {}
json_rpc::error error{};
epee::serialization::storage_entry id{};
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(jsonrpc)
@ -102,8 +69,6 @@ namespace epee
KV_SERIALIZE(error)
END_KV_SERIALIZE_MAP()
};
typedef response<dummy_result, error> error_response;
}
}

View File

@ -51,12 +51,6 @@
#define GET_IO_SERVICE(s) ((s).get_io_service())
#endif
namespace net
{
class tor_address;
class i2p_address;
}
namespace epee
{
namespace net_utils
@ -303,32 +297,22 @@ namespace net_utils
bool is_blockable() const { return self ? self->is_blockable() : false; }
template<typename Type> const Type &as() const { return as_mutable<const Type>(); }
BEGIN_KV_SERIALIZE_MAP()
// need to `#include "net/[i2p|tor]_address.h"` when serializing `network_address`
static constexpr std::integral_constant<bool, is_store> is_store_{};
std::uint8_t type = std::uint8_t(is_store ? this_ref.get_type_id() : address_type::invalid);
if (!epee::serialization::selector<is_store>::serialize(type, stg, hparent_section, "type"))
return false;
switch (address_type(type))
{
case address_type::ipv4:
return this_ref.template serialize_addr<ipv4_network_address>(is_store_, stg, hparent_section);
case address_type::ipv6:
return this_ref.template serialize_addr<ipv6_network_address>(is_store_, stg, hparent_section);
case address_type::tor:
return this_ref.template serialize_addr<net::tor_address>(is_store_, stg, hparent_section);
case address_type::i2p:
return this_ref.template serialize_addr<net::i2p_address>(is_store_, stg, hparent_section);
case address_type::invalid:
default:
break;
}
MERROR("Unsupported network address type: " << (unsigned)type);
return false;
END_KV_SERIALIZE_MAP()
// This serialization is unspeakably disgusting: someone (in Monero PR #5091) decided to add
// code outside of epee but then put a circular dependency inside this serialization code so
// that the code won't even compile unless epee links to code outside epee. But because it
// was all hidden in templated code (with a template type that NEVER CHANGES) compilation
// got deferred -- but would fail if anything tried to access this serialization code
// *without* the external tor/i2p dependencies. To deal with this unspeakably disgusting
// hack, this serialization implementation is outside of epee, in
// src/net/epee_network_address.cpp.
//
// They left this comment in the serialization code, which I preserve here as a HUGE red
// flag that the code stinks, and yet it was still approved by upstream Monero without even
// a comment about this garbage:
//
// // need to `#include "net/[i2p|tor]_address.h"` when serializing `network_address`
//
KV_MAP_SERIALIZABLE
};
inline bool operator==(const network_address& lhs, const network_address& rhs)

View File

@ -29,6 +29,7 @@
#include "misc_log_ex.h"
#include "enableable.h"
#include "keyvalue_serialization_overloads.h"
#include "../storages/portable_storage.h"
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "serialization"
@ -38,20 +39,59 @@ namespace epee
/************************************************************************/
/* Serialize map declarations */
/************************************************************************/
/// New way: allows putting serialization implementation into cpp, like this:
///
/// blah.h:
/// class Foo {
/// int val;
/// KV_MAP_SERIALIZABLE
/// }:
///
/// blah.cpp:
/// KV_SERIALIZE_MAP_CODE_BEGIN(Foo)
/// KV_SERIALIZE(val)
/// KV_SERIALIZE_MAP_CODE_END()
#define KV_MAP_SERIALIZABLE \
public: \
bool store(epee::serialization::portable_storage& st, epee::serialization::section* hparent_section = nullptr) const; \
bool _load(epee::serialization::portable_storage& st, epee::serialization::section* hparent_section = nullptr); \
bool load(epee::serialization::portable_storage& st, epee::serialization::section* hparent_section = nullptr); \
template <bool is_store> bool _serialize_map(epee::serialization::portable_storage& stg, epee::serialization::section* hparent_section) const;
#define KV_SERIALIZE_MAP_CODE_BEGIN(Class) \
bool Class::store(epee::serialization::portable_storage& st, epee::serialization::section* hparent_section) const \
{ return _serialize_map<true>(st, hparent_section); } \
bool Class::_load(epee::serialization::portable_storage& st, epee::serialization::section* hparent_section) \
{ return _serialize_map<false>(st, hparent_section); } \
bool Class::load(epee::serialization::portable_storage& st, epee::serialization::section* hparent_section) \
{ \
try { return _load(st, hparent_section); } \
catch (const std::exception& err) { LOG_ERROR("Deserialization exception: " << err.what()); } \
catch (...) { LOG_ERROR("Unknown deserialization exception"); } \
return false; \
} \
template <bool is_store> \
bool Class::_serialize_map(epee::serialization::portable_storage& stg, epee::serialization::section* hparent_section) const { \
/* de-const if we're being called (from the above non-const _load method) to deserialize */ \
auto& this_ref = const_cast<std::conditional_t<is_store, const Class, Class>&>(*this);
#define KV_SERIALIZE_MAP_CODE_END() return true; }
/// Old deprecated way: puts every last bit of serialization code in the header. Use this if you
/// are worried about having too much unused memory on your system.
#define BEGIN_KV_SERIALIZE_MAP() \
public: \
template<class t_storage> \
bool store( t_storage& st, typename t_storage::hsection hparent_section = nullptr) const\
bool store( epee::serialization::portable_storage& st, epee::serialization::section* hparent_section = nullptr) const\
{\
return serialize_map<true>(*this, st, hparent_section);\
}\
template<class t_storage> \
bool _load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\
bool _load( epee::serialization::portable_storage& stg, epee::serialization::section* hparent_section = nullptr)\
{\
return serialize_map<false>(*this, stg, hparent_section);\
}\
template<class t_storage> \
bool load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\
bool load( epee::serialization::portable_storage& stg, epee::serialization::section* hparent_section = nullptr)\
{\
try{\
return serialize_map<false>(*this, stg, hparent_section);\
@ -59,7 +99,7 @@ public: \
catch(const std::exception& err) \
{ \
(void)(err); \
LOG_ERROR("Exception on unserializing: " << err.what());\
LOG_ERROR("Exception on deserializing: " << err.what());\
return false; \
}\
}\
@ -87,7 +127,7 @@ do { \
if (!epee::serialization::selector<is_store>::serialize(this_ref.variable, stg, hparent_section, val_name)) { \
epee::serialize_default(this_ref.variable, default_value); \
} else { \
this_ref.explicitly_set = true; \
this_ref.explicitly_set(); \
} \
} while (0);
@ -117,8 +157,8 @@ do { \
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(variable) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(variable, #variable) //skip is_pod compile time check
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(variable) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(variable, #variable)
#define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value)
/// same as KV_SERIALIZE_OPT, but will set `explicitly_set` to true if non-default value found
#define KV_SERIALIZE_OPT2(variable,default_value) KV_SERIALIZE_OPT_N2(variable, #variable, default_value)
/// same as KV_SERIALIZE_OPT, but will call `explicitly_set()` if non-defaulted value found
#define KV_SERIALIZE_OPT2(variable,default_value) KV_SERIALIZE_OPT_N2(variable, #variable, default_value)
#define KV_SERIALIZE_ENUM(enum_) do { \
using enum_t = std::remove_const_t<decltype(this_ref.enum_)>; \
using int_t = std::underlying_type_t<enum_t>; \
@ -128,6 +168,15 @@ do { \
const_cast<enum_t&>(this_ref.enum_) = static_cast<enum_t>(int_value); \
} while(0);
// Stashes `this` in the storage object's context for a dependent type that needs to access it.
#define KV_SERIALIZE_DEPENDENT_N(variable, val_name) do { \
stg.set_context(&this_ref); \
KV_SERIALIZE_N(variable, val_name) \
stg.clear_context(); \
} while (0);
#define KV_SERIALIZE_DEPENDENT(variable) KV_SERIALIZE_DEPENDENT_N(variable, #variable)
}

View File

@ -233,7 +233,7 @@ namespace epee
{
bool res = false;
container.clear();
typename stl_container::value_type val = typename stl_container::value_type();
typename stl_container::value_type val{};
typename t_storage::hsection hchild_section = nullptr;
typename t_storage::harray hsec_array = stg.get_first_section(pname, hchild_section, hparent_section);
if(!hsec_array || !hchild_section) return false;
@ -241,7 +241,7 @@ namespace epee
container.insert(container.end(), std::move(val));
while(stg.get_next_section(hsec_array, hchild_section))
{
typename stl_container::value_type val_l = typename stl_container::value_type();
typename stl_container::value_type val_l{};
res |= val_l._load(stg, hchild_section);
container.insert(container.end(), std::move(val_l));
}
@ -271,9 +271,8 @@ namespace epee
return res;
}
//--------------------------------------------------------------------------------------------------------------------
template<bool> struct selector;
template<>
struct selector<true>
template <bool Serializing = true>
struct selector
{
template<class t_type, class t_storage>
static bool serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)

View File

@ -1,2 +0,0 @@
#pragma once

View File

@ -1,62 +0,0 @@
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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.
//
#ifndef _CRYPTED_STORAGE_H_
#define _CRYPTED_STORAGE_H_
#include "cryptopp_helper.h"
namespace epee
{
template<class t_base_storage, class crypt_provider, class t_key_provider>
class crypted_storage: public t_base_storage
{
public:
size_t PackToSolidBuffer(std::string& targetObj)
{
size_t res = t_base_storage::PackToSolidBuffer(targetObj);
if(res <= 0)
return res;
if(!crypt_provider::encrypt(targetObj, t_key_provider::get_storage_default_key()))
return 0;
return targetObj.size();
}
size_t LoadFromSolidBuffer(const std::string& pTargetObj)
{
std::string buff_to_decrypt = pTargetObj;
if(crypt_provider::decrypt(buff_to_decrypt, t_key_provider::get_storage_default_key()))
return t_base_storage::LoadFromSolidBuffer(buff_to_decrypt);
return 0;
}
};
}
#endif //_CRYPTED_STORAGE_H_

View File

@ -1,68 +0,0 @@
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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.
//
#ifndef _GZIPPED_INMEMSTORAGE_H_
#define _GZIPPED_INMEMSTORAGE_H_
#include "zlib_helper.h"
namespace epee
{
namespace StorageNamed
{
template<class t_base_storage>
class gziped_storage: public t_base_storage
{
public:
size_t PackToSolidBuffer(std::string& targetObj)
{
size_t res = t_base_storage::PackToSolidBuffer(targetObj);
if(res <= 0)
return res;
if(!zlib_helper::pack(targetObj))
return 0;
return targetObj.size();
}
size_t LoadFromSolidBuffer(const std::string& pTargetObj)
{
std::string buff_to_ungzip = pTargetObj;
if(zlib_helper::unpack(buff_to_ungzip))
return t_base_storage::LoadFromSolidBuffer(buff_to_ungzip);
return 0;
}
private:
};
}
}
#endif

View File

@ -108,7 +108,7 @@ namespace epee
req_t.id = req_id;
req_t.method = std::move(method_name);
req_t.params = out_struct;
epee::json_rpc::response<t_response, epee::json_rpc::error> resp_t{};
epee::json_rpc::response<t_response, true> resp_t{};
if(!epee::net_utils::invoke_http_json(uri, req_t, resp_t, transport, timeout, http_method))
{
return false;

View File

@ -51,8 +51,8 @@ namespace epee
typedef epee::serialization::hsection hsection;
typedef epee::serialization::harray harray;
portable_storage(){}
virtual ~portable_storage(){}
portable_storage() = default;
virtual ~portable_storage() = default;
hsection open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist = false);
template<class t_value>
bool get_value(const std::string& value_name, t_value& val, hsection hparent_section);
@ -83,11 +83,22 @@ namespace epee
bool store_to_binary(std::string& target);
bool load_from_binary(const epee::span<const uint8_t> target);
bool load_from_binary(const std::string& target) { return load_from_binary(epee::strspan<uint8_t>(target)); }
template<class trace_policy>
bool dump_as_xml(std::string& targetObj, const std::string& root_name = "");
bool dump_as_json(std::string& targetObj, size_t indent = 0, bool insert_newlines = true);
bool load_from_json(const std::string& source);
/// Lets you store a pointer to some arbitrary context object; typically used to pass some
/// context to dependent child objects.
template <typename T> void set_context(const T* obj) { context_type = &typeid(T); context = obj; }
/// Clears a context pointer stored with set_context
void clear_context() { context_type = nullptr; context = nullptr; }
/// Retrieves context set by set_context(). Returns nullptr if the stored type doesn't match
/// `T`, or if no context pointer is stored at all.
template <typename T> const T* get_context() {
return (context && context_type && *context_type == typeid(T))
? static_cast<const T*>(context)
: nullptr;
}
private:
section m_root;
hsection get_root_section() {return &m_root;}
@ -97,6 +108,9 @@ namespace epee
hsection insert_new_section(const std::string& pentry_name, hsection psection);
const void* context = nullptr;
const std::type_info* context_type = nullptr;
#pragma pack(push)
#pragma pack(1)
struct storage_block_header
@ -125,12 +139,6 @@ namespace epee
CATCH_ENTRY("portable_storage::load_from_json", false)
}
template<class trace_policy>
bool portable_storage::dump_as_xml(std::string& targetObj, const std::string& root_name)
{
return false;//TODO: don't think i ever again will use xml - ambiguous and "overtagged" format
}
inline
bool portable_storage::store_to_binary(std::string& target)
{

View File

@ -24,158 +24,43 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#include "misc_language.h"
#include "portable_storage_base.h"
#include "parserse_base_utils.h"
namespace epee
{
namespace serialization
{
// parameters to all of these:
// - strm - the output stream
// - ... - the value
// - indent - the level of nesting (0, 1, ...)
// - pretty - if true, add newlines, spaces between elements, and indents. If false make it ugly.
void dump_as_json(std::ostream& strm, const array_entry& ae, size_t indent, bool pretty);
void dump_as_json(std::ostream& strm, const storage_entry& se, size_t indent, bool pretty);
void dump_as_json(std::ostream& strm, const std::string& v, size_t indent, bool pretty);
void dump_as_json(std::ostream& strm, const section& sec, size_t indent, bool pretty);
template<class t_stream>
void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent, bool insert_newlines);
template<class t_stream>
void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent, bool insert_newlines);
template<class t_stream>
void dump_as_json(t_stream& strm, const std::string& v, size_t indent, bool insert_newlines);
template<class t_stream>
void dump_as_json(t_stream& strm, const int8_t& v, size_t indent, bool insert_newlines);
template<class t_stream>
void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent, bool insert_newlines);
template<class t_stream>
void dump_as_json(t_stream& strm, const bool& v, size_t indent, bool insert_newlines);
template<class t_stream, class t_type>
void dump_as_json(t_stream& strm, const t_type& v, size_t indent, bool insert_newlines);
template<class t_stream>
void dump_as_json(t_stream& strm, const section& sec, size_t indent, bool insert_newlines);
inline std::string make_indent(size_t indent)
{
return std::string(indent*2, ' ');
}
template<class t_stream>
struct array_entry_store_to_json_visitor: public boost::static_visitor<void>
{
t_stream& m_strm;
size_t m_indent;
bool m_insert_newlines;
array_entry_store_to_json_visitor(t_stream& strm, size_t indent,
bool insert_newlines = true)
: m_strm(strm), m_indent(indent), m_insert_newlines(insert_newlines)
{}
template<class t_type>
void operator()(const array_entry_t<t_type>& a)
{
m_strm << "[";
if(a.m_array.size())
{
auto last_it = --a.m_array.end();
for(auto it = a.m_array.begin(); it != a.m_array.end(); it++)
{
dump_as_json(m_strm, *it, m_indent, m_insert_newlines);
if(it != last_it)
m_strm << ",";
}
}
m_strm << "]";
}
};
template<class t_stream>
struct storage_entry_store_to_json_visitor: public boost::static_visitor<void>
{
t_stream& m_strm;
size_t m_indent;
bool m_insert_newlines;
storage_entry_store_to_json_visitor(t_stream& strm, size_t indent,
bool insert_newlines = true)
: m_strm(strm), m_indent(indent), m_insert_newlines(insert_newlines)
{}
//section, array_entry
template<class visited_type>
void operator()(const visited_type& v)
{
dump_as_json(m_strm, v, m_indent, m_insert_newlines);
}
};
template<class t_stream>
void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent, bool insert_newlines)
{
array_entry_store_to_json_visitor<t_stream> aesv(strm, indent, insert_newlines);
boost::apply_visitor(aesv, ae);
}
template<class t_stream>
void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent, bool insert_newlines)
{
storage_entry_store_to_json_visitor<t_stream> sv(strm, indent, insert_newlines);
boost::apply_visitor(sv, se);
}
template<class t_stream>
void dump_as_json(t_stream& strm, const std::string& v, size_t indent, bool insert_newlines)
{
strm << "\"" << misc_utils::parse::transform_to_escape_sequence(v) << "\"";
}
template<class t_stream>
void dump_as_json(t_stream& strm, const int8_t& v, size_t indent, bool insert_newlines)
inline void dump_as_json(std::ostream& strm, const int8_t& v, size_t indent, bool pretty)
{
strm << static_cast<int32_t>(v);
}
template<class t_stream>
void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent, bool insert_newlines)
inline void dump_as_json(std::ostream& strm, const uint8_t& v, size_t indent, bool pretty)
{
strm << static_cast<int32_t>(v);
}
template<class t_stream>
void dump_as_json(t_stream& strm, const bool& v, size_t indent, bool insert_newlines)
inline void dump_as_json(std::ostream& strm, const bool& v, size_t indent, bool pretty)
{
if(v)
strm << "true";
else
strm << "false";
strm << (v ? "true" : "false");
}
template<class t_stream, class t_type>
void dump_as_json(t_stream& strm, const t_type& v, size_t indent, bool insert_newlines)
template <typename T>
void dump_as_json(std::ostream& strm, const T& v, size_t indent, bool pretty)
{
strm << v;
}
template<class t_stream>
void dump_as_json(t_stream& strm, const section& sec, size_t indent, bool insert_newlines)
{
size_t local_indent = indent + 1;
std::string newline = insert_newlines ? "\r\n" : "";
strm << "{" << newline;
std::string indent_str = make_indent(local_indent);
if(sec.m_entries.size())
{
auto it_last = --sec.m_entries.end();
for(auto it = sec.m_entries.begin(); it!= sec.m_entries.end();it++)
{
strm << indent_str << "\"" << misc_utils::parse::transform_to_escape_sequence(it->first) << "\"" << ": ";
dump_as_json(strm, it->second, local_indent, insert_newlines);
if(it_last != it)
strm << ",";
strm << newline;
}
}
strm << make_indent(indent) << "}";
}
}
}

View File

@ -30,19 +30,21 @@
#include <time.h>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
#include "misc_language.h"
#include "portable_storage_base.h"
#include "parserse_base_utils.h"
#include "warnings.h"
namespace epee
{
namespace serialization
{
#define ASSERT_AND_THROW_WRONG_CONVERSION() ASSERT_MES_AND_THROW("WRONG DATA CONVERSION: from type=" << typeid(from).name() << " to type " << typeid(to).name())
#define ASSERT_AND_THROW_WRONG_CONVERSION() ASSERT_MES_AND_THROW("WRONG DATA CONVERSION @ " << __FILE__ << ":" << __LINE__ << ": " << typeid(from).name() << " to " << typeid(to).name())
template<typename from_type, typename to_type>
void convert_int_to_uint(const from_type& from, to_type& to)
template <typename from_type, typename to_type, std::enable_if_t<std::is_signed<from_type>::value && std::is_unsigned<to_type>::value, int> = 0>
void convert_int(const from_type& from, to_type& to)
{
PUSH_WARNINGS
DISABLE_VS_WARNINGS(4018)
@ -52,8 +54,8 @@ DISABLE_GCC_AND_CLANG_WARNING(sign-compare)
to = static_cast<to_type>(from);
POP_WARNINGS
}
template<typename from_type, typename to_type>
void convert_int_to_int(const from_type& from, to_type& to)
template <typename from_type, typename to_type, std::enable_if_t<std::is_signed<from_type>::value && std::is_signed<to_type>::value, int> = 0>
void convert_int(const from_type& from, to_type& to)
{
CHECK_AND_ASSERT_THROW_MES(from >= boost::numeric::bounds<to_type>::lowest(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with lowest possible value = " << boost::numeric::bounds<to_type>::lowest());
PUSH_WARNINGS
@ -62,8 +64,8 @@ DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare)
POP_WARNINGS
to = static_cast<to_type>(from);
}
template<typename from_type, typename to_type>
void convert_uint_to_any_int(const from_type& from, to_type& to)
template<typename from_type, typename to_type, std::enable_if_t<std::is_unsigned<from_type>::value, int> = 0>
void convert_int(const from_type& from, to_type& to)
{
PUSH_WARNINGS
DISABLE_VS_WARNINGS(4018)
@ -73,74 +75,33 @@ DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare)
POP_WARNINGS
}
template<typename from_type, typename to_type, bool, bool> //is from signed, is from to signed
struct convert_to_signed_unsigned;
template<typename from_type, typename to_type>
struct convert_to_signed_unsigned<from_type, to_type, true, true>
template<typename from_type, typename to_type, typename SFINAE = void>
struct converter
{
static void convert(const from_type& from, to_type& to)
{//from signed to signed
convert_int_to_int(from, to);
}
};
template<typename from_type, typename to_type>
struct convert_to_signed_unsigned<from_type, to_type, true, false>
{
static void convert(const from_type& from, to_type& to)
{//from signed to unsigned
convert_int_to_uint(from, to);
}
};
template<typename from_type, typename to_type>
struct convert_to_signed_unsigned<from_type, to_type, false, true>
{
static void convert(const from_type& from, to_type& to)
{//from unsigned to signed
convert_uint_to_any_int(from, to);
}
};
template<typename from_type, typename to_type>
struct convert_to_signed_unsigned<from_type, to_type, false, false>
{
static void convert(const from_type& from, to_type& to)
{
//from unsigned to unsigned
convert_uint_to_any_int(from, to);
}
};
template<typename from_type, typename to_type, bool>
struct convert_to_integral;
template<typename from_type, typename to_type>
struct convert_to_integral<from_type, to_type, true>
{
static void convert(const from_type& from, to_type& to)
{
convert_to_signed_unsigned<from_type, to_type, std::is_signed<from_type>::value, std::is_signed<to_type>::value>::convert(from, to);
}
};
template<typename from_type, typename to_type>
struct convert_to_integral<from_type, to_type, false>
{
static void convert(const from_type& from, to_type& to)
void operator()(const from_type& from, to_type& to)
{
ASSERT_AND_THROW_WRONG_CONVERSION();
}
};
template<typename from_type, typename to_type>
struct converter<from_type, to_type, std::enable_if_t<!std::is_same<to_type, from_type>::value &&
std::is_integral<to_type>::value && std::is_integral<from_type>::value &&
!std::is_same<from_type, bool>::value && !std::is_same<to_type, bool>::value>>
{
void operator()(const from_type& from, to_type& to)
{
convert_int(from, to);
}
};
// For MyMonero/OpenMonero backend compatibility
// MyMonero backend sends amount, fees and timestamp values as strings.
// Until MM backend is updated, this is needed for compatibility between OpenMonero and MyMonero.
template<>
struct convert_to_integral<std::string, uint64_t, false>
struct converter<std::string, uint64_t>
{
static void convert(const std::string& from, uint64_t& to)
void operator()(const std::string& from, uint64_t& to)
{
MTRACE("Converting std::string to uint64_t. Source: " << from);
// String only contains digits
@ -164,39 +125,20 @@ POP_WARNINGS
}
};
template<class from_type, class to_type>
struct is_convertable: std::integral_constant<bool,
std::is_integral<to_type>::value &&
std::is_integral<from_type>::value &&
!std::is_same<from_type, bool>::value &&
!std::is_same<to_type, bool>::value > {};
template<typename from_type, typename to_type, bool>
struct convert_to_same;
template<typename from_type, typename to_type>
struct convert_to_same<from_type, to_type, true>
struct converter<from_type, to_type, std::enable_if_t<std::is_same<to_type, from_type>::value>>
{
static void convert(const from_type& from, to_type& to)
void operator()(const from_type& from, to_type& to)
{
to = from;
}
};
template<typename from_type, typename to_type>
struct convert_to_same<from_type, to_type, false>
{
static void convert(const from_type& from, to_type& to)
{
convert_to_integral<from_type, to_type, is_convertable<from_type, to_type>::value>::convert(from, to);// ASSERT_AND_THROW_WRONG_CONVERSION();
}
};
template<class from_type, class to_type>
void convert_t(const from_type& from, to_type& to)
{
convert_to_same<from_type, to_type, std::is_same<to_type, from_type>::value>::convert(from, to);
converter<from_type, to_type>{}(from, to);
}
}
}

View File

@ -27,7 +27,7 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
add_library(epee hex.cpp http_auth.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c
connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp)
connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp portable_storage.cpp)
if (USE_READLINE AND (GNU_READLINE_FOUND OR (DEPENDS AND NOT MINGW)))
add_library(epee_readline readline_buffer.cpp)
@ -40,9 +40,7 @@ else()
add_library(epee_readline INTERFACE)
endif()
if(HAVE_C11)
SET_PROPERTY(SOURCE memwipe.c PROPERTY COMPILE_FLAGS -std=c11)
endif()
set_property(SOURCE memwipe.c PROPERTY C_STANDARD 11)
# Build and install libepee if we're building for GUI
if (BUILD_GUI_DEPS)

View File

@ -86,6 +86,11 @@ namespace epee { namespace net_utils
return self_->is_same_host(*other_self);
}
// should be here, but network_address is perverted with a circular dependency into src/net, so
// this is in src/net/epee_network_address_hack.cpp instead.
//KV_SERIALIZE_MAP_CODE_BEGIN(network_address)
std::string print_connection_context(const connection_context_base& ctx)
{
std::stringstream ss;

View File

@ -0,0 +1,119 @@
#include "storages/portable_storage_to_json.h"
namespace epee {
namespace serialization {
namespace {
struct array_entry_store_to_json_visitor: public boost::static_visitor<void>
{
std::ostream& m_strm;
size_t m_indent;
bool m_pretty; // If true: use 2-space indents, newlines, and spaces between elements. If false, don't.
array_entry_store_to_json_visitor(std::ostream& strm, size_t indent,
bool pretty = true)
: m_strm(strm), m_indent(indent), m_pretty(pretty)
{}
template<class t_type>
void operator()(const array_entry_t<t_type>& a)
{
m_strm << '[';
if (!a.m_array.empty())
{
for (auto it = a.m_array.begin(); it != a.m_array.end(); it++)
{
if (it != a.m_array.begin()) m_strm << ',';
dump_as_json(m_strm, *it, m_indent, m_pretty);
}
}
m_strm << "]";
}
};
struct storage_entry_store_to_json_visitor: public boost::static_visitor<void>
{
std::ostream& m_strm;
size_t m_indent;
bool m_pretty;
storage_entry_store_to_json_visitor(std::ostream& strm, size_t indent,
bool pretty = true)
: m_strm(strm), m_indent(indent), m_pretty(pretty)
{}
//section, array_entry
template<class visited_type>
void operator()(const visited_type& v)
{
dump_as_json(m_strm, v, m_indent, m_pretty);
}
};
}
void dump_as_json(std::ostream& strm, const array_entry& ae, size_t indent, bool pretty)
{
array_entry_store_to_json_visitor aesv(strm, indent, pretty);
boost::apply_visitor(aesv, ae);
}
void dump_as_json(std::ostream& strm, const storage_entry& se, size_t indent, bool pretty)
{
storage_entry_store_to_json_visitor sv(strm, indent, pretty);
boost::apply_visitor(sv, se);
}
void dump_as_json(std::ostream& s, const std::string& v, size_t, bool)
{
s << '"';
// JSON strings may only contain 0x20 and above, except for " and \\ which must be escaped.
// For values below 0x20 we can use \u00XX escapes, except for the really common \n and \t (we
// could also use \b, \f, \r, but it really isn't worth the bother.
for (char c : v) {
switch(c) {
case '"':
case '\\':
s << '\\' << c;
break;
case '\n': s << "\\n"; break;
case '\t': s << "\\t"; break;
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
case 0x08: /*\t=0x09: \n=0x0a*/ case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
s << "\\u00" << (c >= 0x10 ? '1' : '0');
c &= 0xf;
s << (c < 0xa ? '0' + c : ('a' - 10) + c);
break;
default:
s << c;
}
}
s << '"';
}
void dump_as_json(std::ostream& strm, const section& sec, size_t indent, bool pretty)
{
strm << '{';
if(sec.m_entries.empty())
strm << '}';
else
{
size_t local_indent = indent + 1;
std::string line_sep(pretty * (1 + 2*local_indent), ' ');
if (pretty) line_sep[0] = '\n';
for (auto it = sec.m_entries.begin(); it != sec.m_entries.end(); ++it)
{
if (it != sec.m_entries.begin()) strm << ',';
strm << line_sep;
dump_as_json(strm, it->first, local_indent, pretty);
strm << ':';
if (pretty) strm << ' ';
dump_as_json(strm, it->second, local_indent, pretty);
}
if (pretty)
line_sep.resize(line_sep.size() - 2);
strm << line_sep << '}';
}
}
}
}

View File

@ -40,7 +40,7 @@ find_package(PkgConfig REQUIRED)
if(NOT STATIC)
pkg_check_modules(MINIUPNPC miniupnpc>=2.1)
pkg_check_modules(UNBOUND libunbound)
pkg_check_modules(LOKIMQ liblokimq>=1.1.0)
pkg_check_modules(LOKIMQ liblokimq>=1.1.3)
endif()
if(MINIUPNPC_FOUND)

@ -1 +1 @@
Subproject commit cb238cb1f134accc4200217d9511115a8f61c6cb
Subproject commit 31a0073c62738827b48d725facd3766879429124

View File

@ -251,7 +251,7 @@ static void init(std::string cache_filename)
dbr = mdb_txn_begin(env, NULL, 0, &txn);
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
dbr = mdb_dbi_open(txn, "relative_rings", MDB_CREATE | MDB_INTEGERKEY, &dbi_relative_rings);
@ -359,7 +359,7 @@ static bool for_all_transactions(const std::string &filename, uint64_t &start_id
dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
dbr = mdb_dbi_open(txn, "txs_pruned", MDB_INTEGERKEY, &dbi);
@ -430,7 +430,11 @@ static uint64_t find_first_diverging_transaction(const std::string &first_filena
MDB_val k;
MDB_val v[2];
epee::misc_utils::auto_scope_leave_caller txn_dtor[2];
LOKI_DEFER {
for (int i = 0; i < 2; i++)
if (tx_active[i]) mdb_txn_abort(txn[i]);
};
for (int i = 0; i < 2; ++i)
{
dbr = mdb_env_create(&env[i]);
@ -444,7 +448,6 @@ static uint64_t find_first_diverging_transaction(const std::string &first_filena
dbr = mdb_txn_begin(env[i], NULL, MDB_RDONLY, &txn[i]);
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
txn_dtor[i] = epee::misc_utils::create_scope_leave_handler([&, i](){if (tx_active[i]) mdb_txn_abort(txn[i]);});
tx_active[i] = true;
dbr = mdb_dbi_open(txn[i], "txs_pruned", MDB_INTEGERKEY, &dbi[i]);
@ -515,7 +518,7 @@ static uint64_t get_num_spent_outputs()
int dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
MDB_cursor *cur;
@ -651,7 +654,7 @@ static uint64_t get_processed_txidx(const std::string &name)
int dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
uint64_t height = 0;
@ -929,7 +932,7 @@ static crypto::hash get_genesis_block_hash(const std::string &filename)
dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
dbr = mdb_dbi_open(txn, "block_info", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi);

View File

@ -157,10 +157,10 @@ static void copy_table(MDB_env *env0, MDB_env *env1, const char *table, unsigned
MINFO("Copying " << table);
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){
LOKI_DEFER {
if (tx_active1) mdb_txn_abort(txn1);
if (tx_active0) mdb_txn_abort(txn0);
});
};
dbr = mdb_txn_begin(env0, NULL, MDB_RDONLY, &txn0);
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
@ -254,10 +254,10 @@ static void prune(MDB_env *env0, MDB_env *env1)
MGINFO("Creating pruned txs_prunable");
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){
LOKI_DEFER {
if (tx_active1) mdb_txn_abort(txn1);
if (tx_active0) mdb_txn_abort(txn0);
});
};
dbr = mdb_txn_begin(env0, NULL, MDB_RDONLY, &txn0);
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));

View File

@ -57,7 +57,6 @@ const char *encode(epee::span<const uint8_t> src, stack_t &stack)
template <typename stack_t>
const char *encode(std::string const &src, stack_t &stack)
{
char *result = encode(epee::strspan<uint8_t>(src), stack);
return result;
return encode(epee::strspan<uint8_t>(src), stack);
}
};

View File

@ -60,7 +60,8 @@ private:
bool cancelled = false;
public:
deferred(lambda_t lambda) : lambda(lambda) {}
void cancel() { cancelled = true; }
void invoke() { lambda(); cancelled = true; } // Invoke early instead of at destruction
void cancel() { cancelled = true; } // Cancel invocation at destruction
~deferred() { if (!cancelled) lambda(); }
};

View File

@ -103,7 +103,7 @@ namespace tools
return false;
}
ok = epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT());
if (!ok || res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ?
if (!ok || res.status != cryptonote::rpc::STATUS_OK) // TODO - handle cryptonote::rpc::STATUS_BUSY ?
{
fail_msg_writer() << fail_msg << " -- json_rpc_request: " << res.status;
return false;
@ -131,7 +131,7 @@ namespace tools
return false;
}
ok = epee::net_utils::invoke_http_json(relative_url, req, res, m_http_client, t_http_connection::TIMEOUT());
if (!ok || res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ?
if (!ok || res.status != cryptonote::rpc::STATUS_OK) // TODO - handle cryptonote::rpc::STATUS_BUSY ?
{
fail_msg_writer() << fail_msg << "-- rpc_request: " << res.status;
return false;

View File

@ -34,7 +34,7 @@
namespace cryptonote
{
struct core_stat_info_t
struct core_stat_info
{
uint64_t tx_pool_size;
uint64_t blockchain_height;
@ -50,5 +50,4 @@ namespace cryptonote
KV_SERIALIZE(top_block_id_str)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<core_stat_info_t> core_stat_info;
}

View File

@ -110,10 +110,6 @@ static_assert(STAKING_PORTIONS % 12 == 0, "Use a multiple of twelve, so that it
#define MEMPOOL_PRUNE_NON_STANDARD_TX_LIFETIME (2 * 60 * 60) // seconds, 2 hours
#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000
#define COMMAND_RPC_GET_CHECKPOINTS_MAX_COUNT 256
#define COMMAND_RPC_GET_QUORUM_STATE_MAX_COUNT 256
#define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000
#define P2P_LOCAL_GRAY_PEERLIST_LIMIT 5000

View File

@ -2283,7 +2283,7 @@ crypto::public_key Blockchain::get_output_key(uint64_t amount, uint64_t global_i
}
//------------------------------------------------------------------
bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
bool Blockchain::get_outs(const rpc::GET_OUTPUTS_BIN::request& req, rpc::GET_OUTPUTS_BIN::response& res) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
auto lock = tools::unique_lock(*this);

View File

@ -514,7 +514,7 @@ namespace cryptonote
*
* @return true
*/
bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const;
bool get_outs(const rpc::GET_OUTPUTS_BIN::request& req, rpc::GET_OUTPUTS_BIN::response& res) const;
/**
* @brief gets an output's key and unlocked state
@ -1028,6 +1028,8 @@ namespace cryptonote
/**
* @brief add a hook for processing new blocks and rollbacks for reorgs
*
* TODO: replace these with more versatile std::functions
*/
void hook_block_added (BlockAddedHook& hook) { m_block_added_hooks.push_back(&hook); }
void hook_blockchain_detached(BlockchainDetachedHook& hook) { m_blockchain_detached_hooks.push_back(&hook); }

View File

@ -215,6 +215,11 @@ namespace cryptonote
val;
}
};
static const command_line::arg_descriptor<bool> arg_lmq_quorumnet_public{
"lmq-public-quorumnet",
"Allow the curve-enabled quorumnet address (for a Service Node) to be used for public RPC commands as if passed to --lmq-curve-public. "
"Note that even without this option the quorumnet port can be used for RPC commands by --lmq-admin and --lmq-user pubkeys.",
false};
static const command_line::arg_descriptor<std::string> arg_block_notify = {
"block-notify"
, "Run a program for each new block, '%s' will be replaced by the block hash"
@ -264,17 +269,15 @@ namespace cryptonote
[[noreturn]] static void need_core_init() {
throw std::logic_error("Internal error: quorumnet::init_core_callbacks() should have been called");
}
void *(*quorumnet_new)(core &, const std::string &bind);
void (*quorumnet_delete)(void *&self);
void (*quorumnet_refresh_sns)(void *self);
void (*quorumnet_relay_obligation_votes)(void *self, const std::vector<service_nodes::quorum_vote_t> &);
std::future<std::pair<blink_result, std::string>> (*quorumnet_send_blink)(void *self, const std::string &tx_blob);
void *(*quorumnet_new)(core&);
void (*quorumnet_delete)(void*&self);
void (*quorumnet_relay_obligation_votes)(void* self, const std::vector<service_nodes::quorum_vote_t>&);
std::future<std::pair<blink_result, std::string>> (*quorumnet_send_blink)(core& core, const std::string& tx_blob);
static bool init_core_callback_stubs() {
quorumnet_new = [](core &, const std::string &) -> void * { need_core_init(); };
quorumnet_delete = [](void *&) { need_core_init(); };
quorumnet_refresh_sns = [](void *) { need_core_init(); };
quorumnet_relay_obligation_votes = [](void *, const std::vector<service_nodes::quorum_vote_t> &) { need_core_init(); };
quorumnet_send_blink = [](void *, const std::string &) -> std::future<std::pair<blink_result, std::string>> { need_core_init(); };
quorumnet_new = [](core&) -> void* { need_core_init(); };
quorumnet_delete = [](void*&) { need_core_init(); };
quorumnet_relay_obligation_votes = [](void*, const std::vector<service_nodes::quorum_vote_t>&) { need_core_init(); };
quorumnet_send_blink = [](core&, const std::string&) -> std::future<std::pair<blink_result, std::string>> { need_core_init(); };
return false;
}
bool init_core_callback_complete = init_core_callback_stubs();
@ -369,6 +372,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_public_ip);
command_line::add_arg(desc, arg_storage_server_port);
command_line::add_arg(desc, arg_quorumnet_port);
command_line::add_arg(desc, arg_pad_transactions);
command_line::add_arg(desc, arg_block_notify);
#if 0 // TODO(loki): Pruning not supported because of Service Node List
@ -410,11 +414,9 @@ namespace cryptonote
if (command_line::get_arg(vm, arg_dev_allow_local))
m_service_node_list.debug_allow_local_ips = true;
bool service_node = command_line::get_arg(vm, arg_service_node);
if (service_node) {
m_service_node_keys = std::make_unique<service_node_keys>(); // Will be updated or generated later, in init()
m_service_node = command_line::get_arg(vm, arg_service_node);
if (m_service_node) {
/// TODO: parse these options early, before we start p2p server etc?
m_storage_port = command_line::get_arg(vm, arg_storage_server_port);
@ -525,7 +527,6 @@ namespace cryptonote
return m_blockchain_storage.get_alternative_blocks_count();
}
#ifdef ENABLE_SYSTEMD
static std::string time_ago_str(time_t now, time_t then) {
if (then >= now)
return "now"s;
@ -539,21 +540,21 @@ namespace cryptonote
// Returns a string for systemd status notifications such as:
// Height: 1234567, SN: active, proof: 55m12s, storage: 4m48s, lokinet: 47s
static std::string get_systemd_status_string(const core &c)
std::string core::get_status_string() const
{
std::string s;
s.reserve(128);
s += 'v'; s += LOKI_VERSION_STR;
s += "; Height: ";
s += std::to_string(c.get_blockchain_storage().get_current_blockchain_height());
s += std::to_string(get_blockchain_storage().get_current_blockchain_height());
s += ", SN: ";
auto keys = c.get_service_node_keys();
if (!keys)
if (!service_node())
s += "no";
else
{
auto &snl = c.get_service_node_list();
auto states = snl.get_service_node_list_state({ keys->pub });
auto& snl = get_service_node_list();
const auto& pubkey = get_service_keys().pub;
auto states = snl.get_service_node_list_state({ pubkey });
if (states.empty())
s += "not registered";
else
@ -567,19 +568,18 @@ namespace cryptonote
s += "decomm.";
uint64_t last_proof = 0;
snl.access_proof(keys->pub, [&](auto &proof) { last_proof = proof.timestamp; });
snl.access_proof(pubkey, [&](auto& proof) { last_proof = proof.timestamp; });
s += ", proof: ";
time_t now = std::time(nullptr);
s += time_ago_str(now, last_proof);
s += ", storage: ";
s += time_ago_str(now, c.m_last_storage_server_ping);
s += time_ago_str(now, m_last_storage_server_ping);
s += ", lokinet: ";
s += time_ago_str(now, c.m_last_lokinet_ping);
s += time_ago_str(now, m_last_lokinet_ping);
}
}
return s;
}
#endif
//-----------------------------------------------------------------------------------------------
bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */)
@ -641,11 +641,12 @@ namespace cryptonote
bool const prune_blockchain = false; /* command_line::get_arg(vm, arg_prune_blockchain); */
bool keep_alt_blocks = command_line::get_arg(vm, arg_keep_alt_blocks);
if (m_service_node_keys)
r = init_service_keys();
CHECK_AND_ASSERT_MES(r, false, "Failed to create or load service keys");
if (m_service_node)
{
r = init_service_node_keys();
CHECK_AND_ASSERT_MES(r, false, "Failed to create or load service node key");
m_service_node_list.set_my_service_node_keys(m_service_node_keys.get());
// Only use our service keys for our service node if we are running in SN mode:
m_service_node_list.set_my_service_node_keys(&m_service_keys);
}
boost::filesystem::path folder(m_config_folder);
@ -905,22 +906,7 @@ namespace cryptonote
}
}
if (m_service_node_keys)
{
std::lock_guard<std::mutex> lock{m_quorumnet_init_mutex};
// quorumnet_new takes a zmq bind string, e.g. "tcp://1.2.3.4:5678"
std::string listen_ip = vm["p2p-bind-ip"].as<std::string>();
if (listen_ip.empty())
listen_ip = "0.0.0.0";
std::string qnet_listen = "tcp://" + listen_ip + ":" + std::to_string(m_quorumnet_port);
m_quorumnet_obj = quorumnet_new(*this, qnet_listen);
}
// Otherwise we may still need quorumnet in remote-only mode, but we construct it on demand
#ifdef ENABLE_SYSTEMD
sd_notify(0, ("READY=1\nSTATUS=" + get_systemd_status_string(*this)).c_str());
#endif
init_lokimq(vm);
return true;
}
@ -962,9 +948,9 @@ namespace cryptonote
}
//-----------------------------------------------------------------------------------------------
bool core::init_service_node_keys()
bool core::init_service_keys()
{
auto &keys = *m_service_node_keys;
auto& keys = m_service_keys;
// Primary SN pubkey (monero NIH curve25519 algo)
if (!init_key(m_config_folder + "/key", keys.key, keys.pub,
crypto::secret_key_to_public_key,
@ -976,8 +962,6 @@ namespace cryptonote
)
return false;
MGINFO_YELLOW("Service node primary pubkey is " << epee::string_tools::pod_to_hex(keys.pub));
static_assert(
sizeof(crypto::ed25519_public_key) == crypto_sign_ed25519_PUBLICKEYBYTES &&
sizeof(crypto::ed25519_secret_key) == crypto_sign_ed25519_SECRETKEYBYTES &&
@ -998,36 +982,142 @@ namespace cryptonote
)
return false;
MGINFO_YELLOW("Service node ed25519 pubkey is " << epee::string_tools::pod_to_hex(keys.pub_ed25519));
// Standard x25519 keys generated from the ed25519 keypair, used for encrypted communication between SNs
int rc = crypto_sign_ed25519_pk_to_curve25519(keys.pub_x25519.data, keys.pub_ed25519.data);
CHECK_AND_ASSERT_MES(rc == 0, false, "failed to convert ed25519 pubkey to x25519");
crypto_sign_ed25519_sk_to_curve25519(keys.key_x25519.data, keys.key_ed25519.data);
MGINFO_YELLOW("Service node x25519 pubkey is " << epee::string_tools::pod_to_hex(keys.pub_x25519));
if (m_service_node) {
MGINFO_YELLOW("Service node public keys:");
MGINFO_YELLOW("- primary: " << epee::string_tools::pod_to_hex(keys.pub));
MGINFO_YELLOW("- ed25519: " << epee::string_tools::pod_to_hex(keys.pub_ed25519));
// Same as ed25519, but encoded with base32z:
char b32z[52] = {};
base32z::encode(std::string{reinterpret_cast<const char*>(keys.pub_ed25519.data), 32}, b32z);
MGINFO_YELLOW("- lokinet: " << lokimq::string_view(b32z, sizeof(b32z)) << ".snode");
MGINFO_YELLOW("- x25519: " << epee::string_tools::pod_to_hex(keys.pub_x25519));
} else {
MGINFO_YELLOW("x25519 public key: " << epee::string_tools::pod_to_hex(keys.pub_x25519));
}
return true;
}
static constexpr el::Level easylogging_level(lokimq::LogLevel level) {
using namespace lokimq;
switch (level) {
case LogLevel::fatal: return el::Level::Fatal;
case LogLevel::error: return el::Level::Error;
case LogLevel::warn: return el::Level::Warning;
case LogLevel::info: return el::Level::Info;
case LogLevel::debug: return el::Level::Debug;
case LogLevel::trace: return el::Level::Trace;
default: return el::Level::Unknown;
}
}
lokimq::AuthLevel core::lmq_check_access(const crypto::x25519_public_key& pubkey) const {
auto it = m_lmq_auth.find(pubkey);
if (it != m_lmq_auth.end())
return it->second;
return lokimq::AuthLevel::denied;
}
// Builds an allow function; takes `*this`, the default auth level, and whether this connection
// should allow incoming SN connections.
//
// default_auth should be AuthLevel::denied if only pre-approved connections may connect,
// AuthLevel::basic for public RPC, AuthLevel::admin for a (presumably localhost) unrestricted
// port, and AuthLevel::none for a super restricted mode (generally this is useful when there are
// also SN-restrictions on commands, i.e. for quorumnet).
//
// check_sn is whether we check an incoming key against known service nodes (and thus return
// "true" for the service node access if it checks out).
//
lokimq::AuthLevel core::lmq_allow(lokimq::string_view ip, lokimq::string_view x25519_pubkey_str, lokimq::AuthLevel default_auth) {
using namespace lokimq;
AuthLevel auth = default_auth;
if (x25519_pubkey_str.size() == sizeof(crypto::x25519_public_key)) {
crypto::x25519_public_key x25519_pubkey;
std::memcpy(x25519_pubkey.data, x25519_pubkey_str.data(), x25519_pubkey_str.size());
auto user_auth = lmq_check_access(x25519_pubkey);
if (user_auth >= AuthLevel::basic) {
if (user_auth > auth)
auth = user_auth;
MCINFO("lmq", "Incoming " << auth << "-authenticated connection");
}
MCINFO("lmq", "Incoming [" << auth << "] curve connection from " << ip << "/" << x25519_pubkey);
}
else {
MCINFO("lmq", "Incoming [" << auth << "] plain connection from " << ip);
}
return auth;
}
void core::init_lokimq(const boost::program_options::variables_map& vm) {
using namespace lokimq;
MGINFO("Starting lokimq");
m_lmq = std::make_unique<LokiMQ>(
tools::copy_guts(m_service_keys.pub_x25519),
tools::copy_guts(m_service_keys.key_x25519),
m_service_node,
[this](string_view x25519_pk) { return m_service_node_list.remote_lookup(x25519_pk); },
[](LogLevel level, const char *file, int line, std::string msg) {
// What a lovely interface (<-- sarcasm)
if (ELPP->vRegistry()->allowed(easylogging_level(level), "lmq"))
el::base::Writer(easylogging_level(level), file, line, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct("lmq") << msg;
},
lokimq::LogLevel::trace
);
// ping.ping: a simple debugging target for pinging the lmq listener
m_lmq->add_category("ping", Access{AuthLevel::none})
.add_request_command("ping", [](Message& m) {
MCINFO("lmq", "Received ping from " << m.conn);
m.send_reply("pong");
})
;
if (m_service_node)
{
// Service nodes always listen for quorumnet data on the p2p IP, quorumnet port
std::string listen_ip = vm["p2p-bind-ip"].as<std::string>();
if (listen_ip.empty())
listen_ip = "0.0.0.0";
std::string qnet_listen = "tcp://" + listen_ip + ":" + std::to_string(m_quorumnet_port);
MGINFO("- listening on " << qnet_listen << " (quorumnet)");
m_lmq->listen_curve(qnet_listen,
[this, public_=command_line::get_arg(vm, arg_lmq_quorumnet_public)](string_view ip, string_view pk, bool) {
return lmq_allow(ip, pk, public_ ? AuthLevel::basic : AuthLevel::none);
});
m_quorumnet_state = quorumnet_new(*this);
}
}
void core::start_lokimq() { m_lmq->start(); }
//-----------------------------------------------------------------------------------------------
bool core::set_genesis_block(const block& b)
{
return m_blockchain_storage.reset_and_set_genesis_block(b);
}
//-----------------------------------------------------------------------------------------------
bool core::deinit()
void core::deinit()
{
#ifdef ENABLE_SYSTEMD
sd_notify(0, "STOPPING=1\nSTATUS=Shutting down");
#endif
if (m_quorumnet_obj)
quorumnet_delete(m_quorumnet_obj);
if (m_quorumnet_state)
quorumnet_delete(m_quorumnet_state);
m_lmq.reset();
m_long_poll_wake_up_clients.notify_all();
m_service_node_list.store();
m_miner.stop();
m_mempool.deinit();
m_blockchain_storage.deinit();
return true;
}
//-----------------------------------------------------------------------------------------------
void core::test_drop_download()
@ -1470,13 +1560,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
std::future<std::pair<blink_result, std::string>> core::handle_blink_tx(const std::string &tx_blob)
{
if (!m_quorumnet_obj) {
assert(!m_service_node_keys);
std::lock_guard<std::mutex> lock{m_quorumnet_init_mutex};
if (!m_quorumnet_obj)
m_quorumnet_obj = quorumnet_new(*this, "" /* don't listen */);
}
return quorumnet_send_blink(m_quorumnet_obj, tx_blob);
return quorumnet_send_blink(*this, tx_blob);
}
//-----------------------------------------------------------------------------------------------
bool core::get_stat_info(core_stat_info& st_inf) const
@ -1691,7 +1775,7 @@ namespace cryptonote
{
cryptonote_connection_context fake_context{};
tx_verification_context tvc{};
NOTIFY_NEW_TRANSACTIONS::request r;
NOTIFY_NEW_TRANSACTIONS::request r{};
for (auto it = txs.begin(); it != txs.end(); ++it)
{
r.txs.push_back(it->second);
@ -1704,15 +1788,15 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::submit_uptime_proof()
{
if (!m_service_node_keys)
if (!m_service_node)
return true;
NOTIFY_UPTIME_PROOF::request req = m_service_node_list.generate_uptime_proof(*m_service_node_keys, m_sn_public_ip, m_storage_port, m_storage_lmq_port, m_quorumnet_port);
NOTIFY_UPTIME_PROOF::request req = m_service_node_list.generate_uptime_proof(m_sn_public_ip, m_storage_port, m_storage_lmq_port, m_quorumnet_port);
cryptonote_connection_context fake_context{};
bool relayed = get_protocol()->relay_uptime_proof(req, fake_context);
if (relayed)
MGINFO("Submitted uptime-proof for Service Node (yours): " << m_service_node_keys->pub);
MGINFO("Submitted uptime-proof for Service Node (yours): " << m_service_keys.pub);
return true;
}
@ -1744,8 +1828,8 @@ namespace cryptonote
auto quorum_votes = m_quorum_cop.get_relayable_votes(height, hf_version, true);
auto p2p_votes = m_quorum_cop.get_relayable_votes(height, hf_version, false);
if (!quorum_votes.empty() && m_quorumnet_obj && m_service_node_keys)
quorumnet_relay_obligation_votes(m_quorumnet_obj, quorum_votes);
if (!quorum_votes.empty() && m_quorumnet_state && m_service_node)
quorumnet_relay_obligation_votes(m_quorumnet_state, quorum_votes);
if (!p2p_votes.empty())
{
@ -1782,7 +1866,7 @@ namespace cryptonote
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_count);
}
//-----------------------------------------------------------------------------------------------
bool core::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
bool core::get_outs(const rpc::GET_OUTPUTS_BIN::request& req, rpc::GET_OUTPUTS_BIN::response& res) const
{
return m_blockchain_storage.get_outs(req, res);
}
@ -1981,8 +2065,10 @@ namespace cryptonote
void core::update_lmq_sns()
{
if (m_quorumnet_obj)
quorumnet_refresh_sns(m_quorumnet_obj);
// TODO: let callers (e.g. lokinet, ss) subscribe to callbacks when this fires
lokimq::pubkey_set active_sns;
m_service_node_list.copy_active_x25519_pubkeys(std::inserter(active_sns, active_sns.end()));
m_lmq->set_active_sns(std::move(active_sns));
}
//-----------------------------------------------------------------------------------------------
crypto::hash core::get_tail_id() const
@ -2034,7 +2120,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
void core::do_uptime_proof_call()
{
std::vector<service_nodes::service_node_pubkey_info> const states = get_service_node_list_state({ m_service_node_keys->pub });
std::vector<service_nodes::service_node_pubkey_info> const states = get_service_node_list_state({ m_service_keys.pub });
// wait one block before starting uptime proofs.
if (!states.empty() && (states[0].info->registration_height + 1) < get_current_blockchain_height())
@ -2044,14 +2130,14 @@ namespace cryptonote
// proof if we are within half a tick of the target time. (Essentially our target proof
// window becomes the first time this triggers in the 57.5-62.5 minute window).
uint64_t next_proof_time = 0;
m_service_node_list.access_proof(m_service_node_keys->pub, [&](auto &proof) { next_proof_time = proof.timestamp; });
m_service_node_list.access_proof(m_service_keys.pub, [&](auto &proof) { next_proof_time = proof.timestamp; });
next_proof_time += UPTIME_PROOF_FREQUENCY_IN_SECONDS - UPTIME_PROOF_TIMER_SECONDS/2;
if ((uint64_t) std::time(nullptr) < next_proof_time)
return;
auto pubkey = m_service_node_list.get_pubkey_from_x25519(m_service_node_keys->pub_x25519);
if (pubkey != crypto::null_pkey && pubkey != m_service_node_keys->pub)
auto pubkey = m_service_node_list.get_pubkey_from_x25519(m_service_keys.pub_x25519);
if (pubkey != crypto::null_pkey && pubkey != m_service_keys.pub)
{
MGINFO_RED(
"Failed to submit uptime proof: another service node on the network is using the same ed/x25519 keys as "
@ -2125,7 +2211,7 @@ namespace cryptonote
time_t const lifetime = time(nullptr) - get_start_time();
int proof_delay = m_nettype == FAKECHAIN ? 5 : UPTIME_PROOF_INITIAL_DELAY_SECONDS;
if (m_service_node_keys && lifetime > proof_delay) // Give us some time to connect to peers before sending uptimes
if (m_service_node && lifetime > proof_delay) // Give us some time to connect to peers before sending uptimes
{
do_uptime_proof_call();
}
@ -2139,7 +2225,7 @@ namespace cryptonote
#endif
#ifdef ENABLE_SYSTEMD
m_systemd_notify_interval.do_call([this] { sd_notify(0, ("WATCHDOG=1\nSTATUS=" + get_systemd_status_string(*this)).c_str()); });
m_systemd_notify_interval.do_call([this] { sd_notify(0, ("WATCHDOG=1\nSTATUS=" + get_status_string()).c_str()); });
#endif
return true;
@ -2469,10 +2555,6 @@ namespace cryptonote
return m_quorum_cop.handle_vote(vote, vvc);
}
//-----------------------------------------------------------------------------------------------
const core::service_node_keys* core::get_service_node_keys() const
{
return m_service_node_keys.get();
}
uint32_t core::get_blockchain_pruning_seed() const
{
return get_blockchain_storage().get_blockchain_pruning_seed();

View File

@ -36,6 +36,7 @@
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <lokimq/lokimq.h>
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
@ -78,20 +79,19 @@ namespace cryptonote
// cryptonote_protocol/quorumnet.cpp's quorumnet::init_core_callbacks(). This indirection is here
// so that core doesn't need to link against cryptonote_protocol (plus everything it depends on).
// Starts the quorumnet listener. Return an opaque pointer (void *) that gets passed into all the
// other callbacks below so that the callbacks can recast it into whatever it should be. `bind`
// will be null if the quorumnet object is started in remote-only (non-listening) mode, which only
// happens on-demand when running in non-SN mode.
extern void *(*quorumnet_new)(core &core, const std::string &bind);
// Stops the quorumnet listener; is expected to delete the object and reset the pointer to nullptr.
extern void (*quorumnet_delete)(void *&self);
// Called when a block is added to let LokiMQ update the active set of SNs
extern void (*quorumnet_refresh_sns)(void* self);
// Initializes quorumnet state (for service nodes only). This is called after the LokiMQ object
// has been set up but before it starts listening. Return an opaque pointer (void *) that gets
// passed into all the other callbacks below so that the callbacks can recast it into whatever it
// should be.
extern void* (*quorumnet_new)(core& core);
// Destroys the quorumnet state; called on shutdown *after* the LokiMQ object has been destroyed.
// Should destroy the state object and set the pointer reference to nullptr.
extern void (*quorumnet_delete)(void*& self);
// Relays votes via quorumnet.
extern void (*quorumnet_relay_obligation_votes)(void *self, const std::vector<service_nodes::quorum_vote_t> &votes);
// Sends a blink tx to the current blink quorum, returns a future that can be used to wait for the
// result.
extern std::future<std::pair<blink_result, std::string>> (*quorumnet_send_blink)(void *self, const std::string &tx_blob);
extern std::future<std::pair<blink_result, std::string>> (*quorumnet_send_blink)(core& core, const std::string& tx_blob);
extern bool init_core_callback_complete;
@ -410,11 +410,9 @@ namespace cryptonote
/**
* @brief performs safe shutdown steps for core and core components
*
* Uninitializes the miner instance, transaction pool, and Blockchain
*
* @return true
* Uninitializes the miner instance, lokimq, transaction pool, and Blockchain
*/
bool deinit();
void deinit();
/**
* @brief sets to drop blocks downloaded (for testing)
@ -541,6 +539,12 @@ namespace cryptonote
*/
size_t get_alternative_blocks_count() const;
/**
* Returns a short daemon status summary string. Used when built with systemd support and
* running as a Type=notify daemon.
*/
std::string get_status_string() const;
/**
* @brief set the pointer to the cryptonote protocol object to use
*
@ -613,7 +617,7 @@ namespace cryptonote
*
* @note see Blockchain::get_outs
*/
bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const;
bool get_outs(const rpc::GET_OUTPUTS_BIN::request& req, rpc::GET_OUTPUTS_BIN::response& res) const;
/**
* @copydoc Blockchain::get_output_distribution
@ -662,6 +666,10 @@ namespace cryptonote
/// @brief return a reference to the service node list
tx_memory_pool &get_pool() { return m_mempool; }
/// Returns a reference to the LokiMQ object. Must not be called before init(), and should not
/// be used for any lmq communication until after start_lokimq() has been called.
lokimq::LokiMQ& get_lmq() { return *m_lmq; }
/**
* @copydoc miner::on_synchronized
*
@ -884,14 +892,25 @@ namespace cryptonote
*/
bool add_service_node_vote(const service_nodes::quorum_vote_t& vote, vote_verification_context &vvc);
using service_node_keys = service_nodes::service_node_keys;
using service_keys = service_nodes::service_node_keys;
/**
* @brief Get the keys for this service node.
* @brief Returns true if this node is operating in service node mode.
*
* @return pointer to service node keys, or nullptr if this node is not running as a service node.
* Note that this does not mean the node is currently a registered service node, only that it
* is capable of performing service node duties if a registration hits the network.
*/
const service_node_keys* get_service_node_keys() const;
bool service_node() const { return m_service_node; }
/**
* @brief Get the service keys for this node.
*
* Note that these exists even if the node is not currently operating as a service node as they
* can be used for services other than service nodes (e.g. authenticated public RPC).
*
* @return reference to service keys.
*/
const service_keys& get_service_keys() const { return m_service_keys; }
/**
* @brief attempts to submit an uptime proof to the network, if this is running in service node mode
@ -1082,7 +1101,45 @@ namespace cryptonote
*
* @return true on success, false otherwise
*/
bool init_service_node_keys();
bool init_service_keys();
/**
* Checks the given x25519 pubkey against the configured access lists and, if allowed, returns
* the access level; otherwise returns `denied`.
*/
lokimq::AuthLevel lmq_check_access(const crypto::x25519_public_key& pubkey) const;
/**
* @brief Initializes LokiMQ object, called during init().
*
* Does not start it: this gets called to initialize it, then it gets configured with endpoints
* and listening addresses, then finally a call to `start_lokimq()` should happen to actually
* start it.
*/
void init_lokimq(const boost::program_options::variables_map& vm);
public:
/**
* @brief Starts LokiMQ listening.
*
* Called after all LokiMQ initialization is done.
*/
void start_lokimq();
/**
* Returns whether to allow the connection and, if so, at what authentication level.
*/
lokimq::AuthLevel lmq_allow(lokimq::string_view ip, lokimq::string_view x25519_pubkey, lokimq::AuthLevel default_auth);
/**
* @brief Internal use only!
*
* This returns a mutable reference to the internal auth level map that LokiMQ uses, for
* internal use only.
*/
std::unordered_map<crypto::x25519_public_key, lokimq::AuthLevel>& _lmq_auth_level_map() { return m_lmq_auth; }
private:
/**
* @brief do the uptime proof logic and calls for idle loop.
@ -1142,16 +1199,24 @@ namespace cryptonote
std::atomic_flag m_checkpoints_updating; //!< set if checkpoints are currently updating to avoid multiple threads attempting to update at once
std::unique_ptr<service_node_keys> m_service_node_keys;
bool m_service_node; // True if running in service node mode
service_keys m_service_keys; // Always set, even for non-SN mode -- these can be used for public lokimq rpc
/// Service Node's public IP and storage server port (http and lokimq)
uint32_t m_sn_public_ip;
uint16_t m_storage_port;
uint16_t m_quorumnet_port;
std::string m_quorumnet_bind_ip; // Currently just copied from p2p-bind-ip
void *m_quorumnet_obj = nullptr;
std::mutex m_quorumnet_init_mutex;
/// LokiMQ main object. Gets created during init().
std::unique_ptr<lokimq::LokiMQ> m_lmq;
// Internal opaque data object managed by cryptonote_protocol/quorumnet.cpp. void pointer to
// avoid linking issues (protocol does not link against core).
void* m_quorumnet_state = nullptr;
/// Stores x25519 -> access level for LMQ authentication.
/// Not to be modified after the LMQ listener starts.
std::unordered_map<crypto::x25519_public_key, lokimq::AuthLevel> m_lmq_auth;
size_t block_sync_size;

View File

@ -83,7 +83,8 @@ namespace cryptonote
m_total_hashes(0),
m_do_print_hashrate(false),
m_do_mining(false),
m_current_hash_rate(0)
m_current_hash_rate(0),
m_block_reward(0)
{
m_attrs.set_stack_size(THREAD_STACK_SIZE);
}
@ -94,12 +95,13 @@ namespace cryptonote
catch (...) { /* ignore */ }
}
//-----------------------------------------------------------------------------------------------------
bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height)
bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height, uint64_t block_reward)
{
CRITICAL_REGION_LOCAL(m_template_lock);
m_template = bl;
m_diffic = di;
m_height = height;
m_block_reward = block_reward;
++m_template_no;
m_starter_nonce = crypto::rand<uint32_t>();
return true;
@ -131,7 +133,7 @@ namespace cryptonote
LOG_ERROR("Failed to get_block_template(), stopping mining");
return false;
}
set_block_template(bl, di, height);
set_block_template(bl, di, height, expected_reward);
return true;
}
//-----------------------------------------------------------------------------------------------------

View File

@ -66,7 +66,7 @@ namespace cryptonote
~miner();
bool init(const boost::program_options::variables_map& vm, network_type nettype);
static void init_options(boost::program_options::options_description& desc);
bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height);
bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height, uint64_t block_reward);
bool on_block_chain_update();
bool start(const account_public_address& adr, size_t threads_count, uint64_t stop_after = 0, bool slow_mining = false);
uint64_t get_speed() const;
@ -82,6 +82,7 @@ namespace cryptonote
void pause();
void resume();
void do_print_hashrate(bool do_hr);
uint64_t get_block_reward() const { return m_block_reward; }
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
std::atomic<bool> m_debug_mine_singular_block;
@ -146,5 +147,6 @@ namespace cryptonote
bool m_do_mining;
std::vector<std::pair<uint64_t, uint64_t>> m_threads_autodetect;
boost::thread::attributes m_attrs;
std::atomic<uint64_t> m_block_reward;
};
}

View File

@ -1935,8 +1935,10 @@ namespace service_nodes
}
cryptonote::NOTIFY_UPTIME_PROOF::request service_node_list::generate_uptime_proof(
const service_node_keys &keys, uint32_t public_ip, uint16_t storage_port, uint16_t storage_lmq_port, uint16_t quorumnet_port) const
uint32_t public_ip, uint16_t storage_port, uint16_t storage_lmq_port, uint16_t quorumnet_port) const
{
assert(m_service_node_keys);
const auto& keys = *m_service_node_keys;
cryptonote::NOTIFY_UPTIME_PROOF::request result = {};
result.snode_version = LOKI_VERSION;
result.timestamp = time(nullptr);
@ -2169,6 +2171,39 @@ namespace service_nodes
}
}
std::string service_node_list::remote_lookup(lokimq::string_view xpk) {
if (xpk.size() != sizeof(crypto::x25519_public_key))
return "";
crypto::x25519_public_key x25519_pub;
std::memcpy(x25519_pub.data, xpk.data(), xpk.size());
auto pubkey = get_pubkey_from_x25519(x25519_pub);
if (!pubkey) {
MDEBUG("no connection available: could not find primary pubkey from x25519 pubkey " << x25519_pub);
return "";
}
bool found = false;
uint32_t ip = 0;
uint16_t port = 0;
for_each_service_node_info_and_proof(&pubkey, &pubkey + 1, [&](auto&, auto&, auto& proof) {
found = true;
ip = proof.public_ip;
port = proof.quorumnet_port;
});
if (!found) {
MDEBUG("no connection available: primary pubkey " << pubkey << " is not registered");
return "";
}
if (!(ip && port)) {
MDEBUG("no connection available: service node " << pubkey << " has no associated ip and/or port");
return "";
}
return "tcp://" + epee::string_tools::get_ip_string_from_int32(ip) + ":" + std::to_string(port);
}
void service_node_list::record_checkpoint_vote(crypto::public_key const &pubkey, uint64_t height, bool voted)
{
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);

View File

@ -32,6 +32,7 @@
#include <mutex>
#include <shared_mutex>
#include <boost/thread/shared_mutex.hpp>
#include <lokimq/string_view.h>
#include "serialization/serialization.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_core/service_node_rules.h"
@ -353,6 +354,10 @@ namespace service_nodes
/// Initializes the x25519 map from current pubkey state; called during initialization
void initialize_x25519_map();
/// Remote SN lookup address function for LokiMQ: given a string_view of a x25519 pubkey, this
/// returns that service node's quorumnet contact information, if we have it, else empty string.
std::string remote_lookup(lokimq::string_view x25519_pk);
/// Does something read-only for each registered service node in the range of pubkeys. The SN
/// lock is held while iterating, so the "something" should be quick. Func should take
/// arguments:
@ -391,8 +396,7 @@ namespace service_nodes
bool store();
/// Record public ip and storage port and add them to the service node list
cryptonote::NOTIFY_UPTIME_PROOF::request generate_uptime_proof(const service_node_keys& keys,
uint32_t public_ip,
cryptonote::NOTIFY_UPTIME_PROOF::request generate_uptime_proof(uint32_t public_ip,
uint16_t storage_port,
uint16_t storage_lmq_port,
uint16_t quorumnet_port) const;

View File

@ -210,8 +210,8 @@ namespace service_nodes
uint64_t const REORG_SAFETY_BUFFER_BLOCKS = (hf_version >= cryptonote::network_version_12_checkpointing)
? REORG_SAFETY_BUFFER_BLOCKS_POST_HF12
: REORG_SAFETY_BUFFER_BLOCKS_PRE_HF12;
auto my_keys = m_core.get_service_node_keys();
bool voting_enabled = my_keys && m_core.is_service_node(my_keys->pub, /*require_active=*/true);
const auto& my_keys = m_core.get_service_keys();
bool voting_enabled = m_core.service_node() && m_core.is_service_node(my_keys.pub, /*require_active=*/true);
uint64_t const height = cryptonote::get_block_height(block);
uint64_t const latest_height = std::max(m_core.get_current_blockchain_height(), m_core.get_target_blockchain_height());
@ -285,7 +285,7 @@ namespace service_nodes
if (!alive_for_min_time)
continue;
if (!my_keys)
if (!m_core.service_node())
continue;
auto quorum = m_core.get_quorum(quorum_type::obligations, m_obligations_height);
@ -297,7 +297,7 @@ namespace service_nodes
}
if (quorum->workers.empty()) continue;
int index_in_group = voting_enabled ? find_index_in_quorum_group(quorum->validators, my_keys->pub) : -1;
int index_in_group = voting_enabled ? find_index_in_quorum_group(quorum->validators, my_keys.pub) : -1;
if (index_in_group >= 0)
{
//
@ -373,7 +373,7 @@ namespace service_nodes
}
}
quorum_vote_t vote = service_nodes::make_state_change_vote(m_obligations_height, static_cast<uint16_t>(index_in_group), node_index, vote_for_state, *my_keys);
quorum_vote_t vote = service_nodes::make_state_change_vote(m_obligations_height, static_cast<uint16_t>(index_in_group), node_index, vote_for_state, my_keys);
cryptonote::vote_verification_context vvc;
if (!handle_vote(vote, vvc))
LOG_ERROR("Failed to add state change vote; reason: " << print_vote_verification_context(vvc, &vote));
@ -381,21 +381,21 @@ namespace service_nodes
if (good > 0)
LOG_PRINT_L2(good << " of " << total << " service nodes are active and passing checks; no state change votes required");
}
else if (!tested_myself_once_per_block && (find_index_in_quorum_group(quorum->workers, my_keys->pub) >= 0))
else if (!tested_myself_once_per_block && (find_index_in_quorum_group(quorum->workers, my_keys.pub) >= 0))
{
// NOTE: Not in validating quorum , check if we're the ones
// being tested. If so, check if we would be decommissioned
// based on _our_ data and if so, report it to the user so they
// know about it.
const auto states_array = m_core.get_service_node_list_state({my_keys->pub});
const auto states_array = m_core.get_service_node_list_state({my_keys.pub});
if (states_array.size())
{
const auto &info = *states_array[0].info;
if (info.can_be_voted_on(m_obligations_height))
{
tested_myself_once_per_block = true;
auto my_test_results = check_service_node(obligations_height_hf_version, my_keys->pub, info);
auto my_test_results = check_service_node(obligations_height_hf_version, my_keys.pub, info);
if (info.is_active())
{
if (!my_test_results.passed())
@ -451,14 +451,14 @@ namespace service_nodes
continue;
}
int index_in_group = find_index_in_quorum_group(quorum->validators, my_keys->pub);
int index_in_group = find_index_in_quorum_group(quorum->validators, my_keys.pub);
if (index_in_group <= -1) continue;
//
// NOTE: I am in the quorum, handle checkpointing
//
crypto::hash block_hash = m_core.get_block_id_by_height(m_last_checkpointed_height);
quorum_vote_t vote = make_checkpointing_vote(checkpointed_height_hf_version, block_hash, m_last_checkpointed_height, static_cast<uint16_t>(index_in_group), *my_keys);
quorum_vote_t vote = make_checkpointing_vote(checkpointed_height_hf_version, block_hash, m_last_checkpointed_height, static_cast<uint16_t>(index_in_group), my_keys);
cryptonote::vote_verification_context vvc = {};
if (!handle_vote(vote, vvc))
LOG_ERROR("Failed to add checkpoint vote; reason: " << print_vote_verification_context(vvc, &vote));

View File

@ -681,5 +681,28 @@ namespace service_nodes
std::memcpy(&st, &blob[86], 2); vote.state_change.state = static_cast<new_state>(boost::endian::little_to_native(st));
}
}
}; // namespace service_nodes
KV_SERIALIZE_MAP_CODE_BEGIN(quorum_vote_t)
KV_SERIALIZE(version)
KV_SERIALIZE_ENUM(type)
KV_SERIALIZE(block_height)
KV_SERIALIZE_ENUM(group)
KV_SERIALIZE(index_in_group)
KV_SERIALIZE_VAL_POD_AS_BLOB(signature)
if (this_ref.type == quorum_type::checkpointing)
{
KV_SERIALIZE_VAL_POD_AS_BLOB_N(checkpoint.block_hash, "checkpoint")
}
else
{
KV_SERIALIZE(state_change.worker_index)
KV_SERIALIZE_ENUM(state_change.state)
}
KV_SERIALIZE_MAP_CODE_END()
} // namespace service_nodes
namespace cryptonote {
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_NEW_SERVICE_NODE_VOTE::request)
KV_SERIALIZE(votes)
KV_SERIALIZE_MAP_CODE_END()
}

View File

@ -96,23 +96,7 @@ namespace service_nodes
checkpoint_vote checkpoint;
};
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(version)
KV_SERIALIZE_ENUM(type)
KV_SERIALIZE(block_height)
KV_SERIALIZE_ENUM(group)
KV_SERIALIZE(index_in_group)
KV_SERIALIZE_VAL_POD_AS_BLOB(signature)
if (this_ref.type == quorum_type::checkpointing)
{
KV_SERIALIZE_VAL_POD_AS_BLOB_N(checkpoint.block_hash, "checkpoint")
}
else
{
KV_SERIALIZE(state_change.worker_index)
KV_SERIALIZE_ENUM(state_change.state)
}
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
// TODO(loki): idk exactly if I want to implement this, but need for core tests to compile. Not sure I care about serializing for core tests at all.
private:

View File

@ -392,10 +392,10 @@ namespace cryptonote
crypto::hash max_used_block_id = null_hash;
uint64_t max_used_block_height = 0;
cryptonote::txpool_tx_meta_t meta;
bool ch_inp_res = check_tx_inputs([&tx]()->cryptonote::transaction&{ return tx; }, id, max_used_block_height, max_used_block_id, tvc, opts.kept_by_block,
bool inputs_okay = check_tx_inputs([&tx]()->cryptonote::transaction&{ return tx; }, id, max_used_block_height, max_used_block_id, tvc, opts.kept_by_block,
opts.approved_blink ? blink_rollback_height : nullptr);
const bool non_standard_tx = !tx.is_transfer();
if(!ch_inp_res)
if (!inputs_okay)
{
// if the transaction was valid before (kept_by_block), then it
// may become valid again, so ignore the failed inputs check.
@ -440,7 +440,8 @@ namespace cryptonote
tvc.m_invalid_input = true;
return false;
}
}else
}
else
{
//update transactions container
meta.weight = tx_weight;
@ -489,6 +490,10 @@ namespace cryptonote
MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)tx_weight));
if (!opts.kept_by_block && !opts.do_not_relay && !m_tx_notify.empty())
for (auto& notify : m_tx_notify)
notify(id, tx, blob, opts);
prune(id);
return true;
@ -957,6 +962,13 @@ namespace cryptonote
{
m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();});
}
void tx_memory_pool::add_notify(std::function<void(const crypto::hash&, const transaction&, const std::string&, const tx_pool_options&)> notify)
{
auto lock = tools::unique_lock(m_transactions_lock);
m_tx_notify.push_back(std::move(notify));
}
//---------------------------------------------------------------------------------
sorted_tx_container::iterator tx_memory_pool::find_tx_in_sorted_container(const crypto::hash& id) const
{
@ -1171,7 +1183,7 @@ namespace cryptonote
}, false, include_unrelayed_txes);
}
//------------------------------------------------------------------
void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes) const
void tx_memory_pool::get_transaction_backlog(std::vector<rpc::tx_backlog_entry>& backlog, bool include_unrelayed_txes) const
{
auto locks = tools::unique_locks(m_transactions_lock, m_blockchain);
@ -1183,12 +1195,12 @@ namespace cryptonote
}, false, include_unrelayed_txes);
}
//------------------------------------------------------------------
void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes) const
void tx_memory_pool::get_transaction_stats(struct rpc::txpool_stats& stats, bool include_unrelayed_txes) const
{
auto locks = tools::unique_locks(m_transactions_lock, m_blockchain);
const uint64_t now = time(NULL);
std::map<uint64_t, txpool_histo> agebytes;
std::map<uint64_t, rpc::txpool_histo> agebytes;
stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes);
std::vector<uint32_t> weights;
weights.reserve(stats.txs_total);
@ -1221,7 +1233,7 @@ namespace cryptonote
/* looking for 98th percentile */
size_t end = stats.txs_total * 0.02;
uint64_t delta, factor;
std::map<uint64_t, txpool_histo>::iterator it, i2;
std::map<uint64_t, rpc::txpool_histo>::iterator it, i2;
if (end)
{
/* If enough txs, spread the first 98% of results across
@ -1261,7 +1273,7 @@ namespace cryptonote
}
//------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data) const
bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<rpc::tx_info>& tx_infos, std::vector<rpc::spent_key_image_info>& key_image_infos, bool include_sensitive_data) const
{
auto tx_lock = tools::unique_lock(m_transactions_lock, std::defer_lock);
auto bc_lock = tools::unique_lock(m_blockchain, std::defer_lock);
@ -1280,7 +1292,7 @@ namespace cryptonote
return true;
}
tx_infos.emplace_back();
tx_info &txi = tx_infos.back();
auto& txi = tx_infos.back();
txi.id_hash = epee::string_tools::pod_to_hex(txid);
txi.tx_blob = *bd;
tx.set_hash(txid);
@ -1308,7 +1320,7 @@ namespace cryptonote
for (const key_images_container::value_type& kee : m_spent_key_images) {
const crypto::key_image& k_image = kee.first;
const std::unordered_set<crypto::hash>& kei_image_set = kee.second;
spent_key_image_info ki;
rpc::spent_key_image_info ki{};
ki.id_hash = epee::string_tools::pod_to_hex(k_image);
for (const crypto::hash& tx_id_hash : kei_image_set)
{
@ -1694,7 +1706,7 @@ end:
return false;//we already sure that this tx is broken for this height
tx_verification_context tvc;
if(!check_tx_inputs([&lazy_tx]()->cryptonote::transaction&{ return lazy_tx(); }, txid, txd.max_used_block_height, txd.max_used_block_id, tvc))
if(!check_tx_inputs(lazy_tx, txid, txd.max_used_block_height, txd.max_used_block_id, tvc))
{
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
@ -1711,7 +1723,7 @@ end:
return false;
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
tx_verification_context tvc;
if(!check_tx_inputs([&lazy_tx]()->cryptonote::transaction&{ return lazy_tx(); }, txid, txd.max_used_block_height, txd.max_used_block_id, tvc))
if(!check_tx_inputs(lazy_tx, txid, txd.max_used_block_height, txd.max_used_block_id, tvc))
{
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
@ -1998,7 +2010,7 @@ end:
{
const bool kept = pass == 1;
bool r = m_blockchain.for_all_txpool_txes([this, &remove, kept](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) {
if (!!kept != !!meta.kept_by_block)
if (kept != (bool)meta.kept_by_block)
return true;
cryptonote::transaction_prefix tx;
if (!parse_and_validate_tx_prefix_from_blob(*bd, tx))

View File

@ -36,6 +36,7 @@
#include <unordered_set>
#include <queue>
#include <boost/serialization/version.hpp>
#include <functional>
#include "string_tools.h"
#include "syncobj.h"
@ -320,6 +321,16 @@ namespace cryptonote
*/
void on_idle();
/**
* Specifies a callback to invoke when one or more transactions is added to the mempool. Note
* that, because incoming blocks have their transactions added to the mempool, this *does*
* trigger for txes that arrive in new blocks.
*
* It does not, however, trigger for transactions that fail verification, that are flagged
* do-not-relay, or that are returned to the pool from a block (i.e. when doing a reorg).
*/
void add_notify(std::function<void(const crypto::hash&, const transaction&, const std::string& blob, const tx_pool_options&)> notify);
/**
* @brief locks the transaction pool
*/
@ -416,7 +427,7 @@ namespace cryptonote
* @param include_unrelayed_txes include unrelayed txes in the result
*
*/
void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes = true) const;
void get_transaction_backlog(std::vector<rpc::tx_backlog_entry>& backlog, bool include_unrelayed_txes = true) const;
/**
* @brief get a summary statistics of all transaction hashes in the pool
@ -425,7 +436,7 @@ namespace cryptonote
* @param include_unrelayed_txes include unrelayed txes in the result
*
*/
void get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const;
void get_transaction_stats(struct rpc::txpool_stats& stats, bool include_unrelayed_txes = true) const;
/**
* @brief get information about all transactions and key images in the pool
@ -438,7 +449,7 @@ namespace cryptonote
*
* @return true
*/
bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data = true) const;
bool get_transactions_and_spent_keys_info(std::vector<rpc::tx_info>& tx_infos, std::vector<rpc::spent_key_image_info>& key_image_infos, bool include_sensitive_data = true) const;
/**
* @brief get information about all transactions and key images in the pool
@ -737,6 +748,9 @@ namespace cryptonote
std::atomic<uint64_t> m_cookie; //!< incremented at each change
/// Callbacks for new tx notifications
std::vector<std::function<void(const crypto::hash&, const transaction&, const std::string& blob, const tx_pool_options&)>> m_tx_notify;
/**
* @brief get an iterator to a transaction in the sorted container
*

View File

@ -29,6 +29,7 @@
add_library(cryptonote_protocol
block_queue.cpp
cryptonote_protocol_handler.inl
cryptonote_protocol_defs.cpp
quorumnet.cpp
core_protocol_instantiation.cpp
p2p_net_node_instantiation.cpp

View File

@ -0,0 +1,125 @@
#include "cryptonote_protocol_defs.h"
namespace cryptonote {
KV_SERIALIZE_MAP_CODE_BEGIN(connection_info)
KV_SERIALIZE(incoming)
KV_SERIALIZE(localhost)
KV_SERIALIZE(local_ip)
KV_SERIALIZE(address)
KV_SERIALIZE(host)
KV_SERIALIZE(ip)
KV_SERIALIZE(port)
KV_SERIALIZE(rpc_port)
KV_SERIALIZE(peer_id)
KV_SERIALIZE(recv_count)
KV_SERIALIZE(recv_idle_time)
KV_SERIALIZE(send_count)
KV_SERIALIZE(send_idle_time)
KV_SERIALIZE(state)
KV_SERIALIZE(live_time)
KV_SERIALIZE(avg_download)
KV_SERIALIZE(current_download)
KV_SERIALIZE(avg_upload)
KV_SERIALIZE(current_upload)
KV_SERIALIZE(support_flags)
KV_SERIALIZE(connection_id)
KV_SERIALIZE(height)
KV_SERIALIZE(pruning_seed)
KV_SERIALIZE(address_type)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(serializable_blink_metadata)
KV_SERIALIZE_VAL_POD_AS_BLOB_N(tx_hash, "#")
KV_SERIALIZE_N(height, "h")
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(quorum, "q")
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(position, "p")
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(signature, "s")
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(block_complete_entry)
KV_SERIALIZE(block)
KV_SERIALIZE(txs)
KV_SERIALIZE(checkpoint)
KV_SERIALIZE(blinks)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_NEW_TRANSACTIONS::request)
KV_SERIALIZE(txs)
KV_SERIALIZE(blinks)
KV_SERIALIZE_OPT(requested, false)
KV_SERIALIZE(_)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_REQUEST_GET_BLOCKS::request)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_RESPONSE_GET_BLOCKS::request)
KV_SERIALIZE(blocks)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missed_ids)
KV_SERIALIZE(current_blockchain_height)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(CORE_SYNC_DATA)
KV_SERIALIZE(current_height)
KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE_VAL_POD_AS_BLOB(top_id)
KV_SERIALIZE_OPT(top_version, (uint8_t)0)
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
KV_SERIALIZE(blink_blocks)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blink_hash)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_REQUEST_CHAIN::request)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_RESPONSE_CHAIN_ENTRY::request)
KV_SERIALIZE(start_height)
KV_SERIALIZE(total_height)
KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_NEW_FLUFFY_BLOCK::request)
KV_SERIALIZE(b)
KV_SERIALIZE(current_blockchain_height)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_REQUEST_FLUFFY_MISSING_TX::request)
KV_SERIALIZE_VAL_POD_AS_BLOB(block_hash)
KV_SERIALIZE(current_blockchain_height)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_UPTIME_PROOF::request)
KV_SERIALIZE_N(snode_version[0], "snode_version_major")
KV_SERIALIZE_N(snode_version[1], "snode_version_minor")
KV_SERIALIZE_N(snode_version[2], "snode_version_patch")
KV_SERIALIZE(timestamp)
KV_SERIALIZE(public_ip)
KV_SERIALIZE(storage_port)
KV_SERIALIZE(storage_lmq_port)
KV_SERIALIZE(qnet_port)
KV_SERIALIZE_VAL_POD_AS_BLOB(pubkey)
KV_SERIALIZE_VAL_POD_AS_BLOB(sig)
KV_SERIALIZE_VAL_POD_AS_BLOB(pubkey_ed25519)
KV_SERIALIZE_VAL_POD_AS_BLOB(sig_ed25519)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_REQUEST_BLOCK_BLINKS::request)
KV_SERIALIZE(heights)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_RESPONSE_BLOCK_BLINKS::request)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_REQUEST_GET_TXS::request)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs)
KV_SERIALIZE_MAP_CODE_END()
// NOTIFY_NEW_SERVICE_NODE_VOTE::request implementation is in service_node_voting.cpp
}

View File

@ -96,32 +96,7 @@ namespace cryptonote
uint8_t address_type;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(incoming)
KV_SERIALIZE(localhost)
KV_SERIALIZE(local_ip)
KV_SERIALIZE(address)
KV_SERIALIZE(host)
KV_SERIALIZE(ip)
KV_SERIALIZE(port)
KV_SERIALIZE(rpc_port)
KV_SERIALIZE(peer_id)
KV_SERIALIZE(recv_count)
KV_SERIALIZE(recv_idle_time)
KV_SERIALIZE(send_count)
KV_SERIALIZE(send_idle_time)
KV_SERIALIZE(state)
KV_SERIALIZE(live_time)
KV_SERIALIZE(avg_download)
KV_SERIALIZE(current_download)
KV_SERIALIZE(avg_upload)
KV_SERIALIZE(current_upload)
KV_SERIALIZE(support_flags)
KV_SERIALIZE(connection_id)
KV_SERIALIZE(height)
KV_SERIALIZE(pruning_seed)
KV_SERIALIZE(address_type)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
/************************************************************************/
@ -134,13 +109,7 @@ namespace cryptonote
std::vector<uint8_t> quorum;
std::vector<uint8_t> position;
std::vector<crypto::signature> signature;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB_N(tx_hash, "#")
KV_SERIALIZE_N(height, "h")
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(quorum, "q")
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(position, "p")
KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(signature, "s")
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
/************************************************************************/
@ -153,12 +122,7 @@ namespace cryptonote
std::vector<blobdata> txs;
blobdata checkpoint;
std::vector<serializable_blink_metadata> blinks;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block)
KV_SERIALIZE(txs)
KV_SERIALIZE(checkpoint)
KV_SERIALIZE(blinks)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
/************************************************************************/
@ -175,12 +139,7 @@ namespace cryptonote
bool requested = false;
std::string _; // padding
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txs)
KV_SERIALIZE(blinks)
KV_SERIALIZE_OPT(requested, false)
KV_SERIALIZE(_)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
/************************************************************************/
@ -193,9 +152,8 @@ namespace cryptonote
struct request
{
std::vector<crypto::hash> blocks;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -209,11 +167,7 @@ namespace cryptonote
std::vector<crypto::hash> missed_ids;
uint64_t current_blockchain_height;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(blocks)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missed_ids)
KV_SERIALIZE(current_blockchain_height)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -228,15 +182,7 @@ namespace cryptonote
std::vector<uint64_t> blink_blocks;
std::vector<crypto::hash> blink_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(current_height)
KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE_VAL_POD_AS_BLOB(top_id)
KV_SERIALIZE_OPT(top_version, (uint8_t)0)
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
KV_SERIALIZE(blink_blocks)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blink_hash)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
struct NOTIFY_REQUEST_CHAIN
@ -247,9 +193,7 @@ namespace cryptonote
{
std::list<crypto::hash> block_ids; // IDs of blocks at linear then exponential drop off, ending in genesis block; see blockchain.cpp for details
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -264,12 +208,7 @@ namespace cryptonote
uint64_t cumulative_difficulty;
std::vector<crypto::hash> m_block_ids;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(start_height)
KV_SERIALIZE(total_height)
KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -285,10 +224,7 @@ namespace cryptonote
block_complete_entry b;
uint64_t current_blockchain_height;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(b)
KV_SERIALIZE(current_blockchain_height)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -305,11 +241,7 @@ namespace cryptonote
uint64_t current_blockchain_height;
std::vector<uint64_t> missing_tx_indices;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB(block_hash)
KV_SERIALIZE(current_blockchain_height)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -334,20 +266,7 @@ namespace cryptonote
uint16_t storage_lmq_port;
uint16_t qnet_port;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_N(snode_version[0], "snode_version_major")
KV_SERIALIZE_N(snode_version[1], "snode_version_minor")
KV_SERIALIZE_N(snode_version[2], "snode_version_patch")
KV_SERIALIZE(timestamp)
KV_SERIALIZE(public_ip)
KV_SERIALIZE(storage_port)
KV_SERIALIZE(storage_lmq_port)
KV_SERIALIZE(qnet_port)
KV_SERIALIZE_VAL_POD_AS_BLOB(pubkey)
KV_SERIALIZE_VAL_POD_AS_BLOB(sig)
KV_SERIALIZE_VAL_POD_AS_BLOB(pubkey_ed25519)
KV_SERIALIZE_VAL_POD_AS_BLOB(sig_ed25519)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -360,9 +279,7 @@ namespace cryptonote
struct request
{
std::vector<uint64_t> heights;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(heights)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -372,9 +289,7 @@ namespace cryptonote
struct request
{
std::vector<crypto::hash> txs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -388,9 +303,7 @@ namespace cryptonote
struct request
{
std::vector<crypto::hash> txs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
@ -401,9 +314,7 @@ namespace cryptonote
{
std::vector<service_nodes::quorum_vote_t> votes;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(votes)
END_KV_SERIALIZE_MAP()
KV_MAP_SERIALIZABLE
};
};
}

View File

@ -69,9 +69,9 @@ struct pending_signature_hash {
using pending_signature_set = std::unordered_set<pending_signature, pending_signature_hash>;
struct SNNWrapper {
LokiMQ lmq;
struct QnetState {
cryptonote::core &core;
LokiMQ &lmq{core.get_lmq()};
// Track submitted blink txes here; unlike the blinks stored in the mempool we store these ones
// more liberally to track submitted blinks, even if unsigned/unacceptable, while the mempool
@ -90,14 +90,11 @@ struct SNNWrapper {
// FIXME:
//std::chrono::steady_clock::time_point last_blink_cleanup = std::chrono::steady_clock::now();
template <typename... Args>
SNNWrapper(cryptonote::core &core, Args &&...args) :
lmq{std::forward<Args>(args)...}, core{core} {
}
QnetState(cryptonote::core &core) : core{core} {}
static SNNWrapper &from(void* obj) {
static QnetState &from(void* obj) {
assert(obj);
return *reinterpret_cast<SNNWrapper*>(obj);
return *reinterpret_cast<QnetState*>(obj);
}
};
@ -143,68 +140,17 @@ std::string get_connect_string(const service_node_list &sn_list, const crypto::x
return "tcp://" + epee::string_tools::get_ip_string_from_int32(ip) + ":" + std::to_string(port);
}
constexpr el::Level easylogging_level(LogLevel level) {
switch (level) {
case LogLevel::fatal: return el::Level::Fatal;
case LogLevel::error: return el::Level::Error;
case LogLevel::warn: return el::Level::Warning;
case LogLevel::info: return el::Level::Info;
case LogLevel::debug: return el::Level::Debug;
case LogLevel::trace: return el::Level::Trace;
};
return el::Level::Unknown;
};
void snn_write_log(LogLevel level, const char *file, int line, std::string msg) {
if (ELPP->vRegistry()->allowed(easylogging_level(level), LOKI_DEFAULT_LOG_CATEGORY))
el::base::Writer(easylogging_level(level), file, line, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(LOKI_DEFAULT_LOG_CATEGORY) << msg;
}
void setup_endpoints(SNNWrapper& snw);
// Called when we add a block to refresh LokiMQ's x25519 pubkeys
void refresh_sns(void* obj) {
auto& snw = SNNWrapper::from(obj);
lokimq::pubkey_set active_sns;
snw.core.get_service_node_list().copy_active_x25519_pubkeys(std::inserter(active_sns, active_sns.end()));
snw.lmq.set_active_sns(std::move(active_sns));
}
void *new_snnwrapper(cryptonote::core &core, const std::string &bind) {
auto keys = core.get_service_node_keys();
auto peer_lookup = [&sn_list = core.get_service_node_list()](string_view x25519_pub) {
return get_connect_string(sn_list, x25519_from_string(x25519_pub));
};
SNNWrapper *obj;
std::string pubkey, seckey;
bool sn;
if (keys) {
MINFO("Starting quorumnet listener on " << bind << " with x25519 pubkey " << keys->pub_x25519);
pubkey = get_data_as_string(keys->pub_x25519);
seckey = get_data_as_string(keys->key_x25519.data);
sn = true;
} else {
MINFO("Starting remote-only lokimq instance");
sn = false;
}
obj = new SNNWrapper(core, pubkey, seckey, sn, std::move(peer_lookup), snn_write_log, LogLevel::trace);
void setup_endpoints(QnetState& qnet);
void *new_qnetstate(cryptonote::core& core) {
QnetState* obj = new QnetState(core);
setup_endpoints(*obj);
refresh_sns(obj);
if (sn)
obj->lmq.listen_curve(bind);
obj->lmq.start();
return obj;
}
void delete_snnwrapper(void *&obj) {
auto *snn = reinterpret_cast<SNNWrapper *>(obj);
MINFO("Shutting down quorumnet listener");
delete snn;
void delete_qnetstate(void *&obj) {
auto* qnet = static_cast<QnetState*>(obj);
delete qnet;
obj = nullptr;
}
@ -244,16 +190,16 @@ public:
/// Singleton wrapper around peer_info
peer_info(
SNNWrapper& snw,
QnetState& qnet,
quorum_type q_type,
std::shared_ptr<const quorum> &quorum,
bool opportunistic = true,
exclude_set exclude = {}
)
: peer_info(snw, q_type, &quorum, &quorum + 1, opportunistic, std::move(exclude)) {}
: peer_info(qnet, q_type, &quorum, &quorum + 1, opportunistic, std::move(exclude)) {}
/// Constructs peer information for the given quorums and quorum position of the caller.
/// \param snw - the SNNWrapper reference
/// \param qnet - the QnetState reference
/// \param q_type - the type of quorum
/// \param qbegin, qend - the iterators to a set of pointers (or other deferenceable type) to quorums
/// \param opportunistic - if true then the peers to relay will also attempt to relay to any
@ -263,24 +209,23 @@ public:
/// pubkey is always added to this exclude list.
template <typename QuorumIt>
peer_info(
SNNWrapper& snw,
QnetState& qnet,
quorum_type q_type,
QuorumIt qbegin, QuorumIt qend,
bool opportunistic = true,
std::unordered_set<crypto::public_key> exclude = {}
)
: lmq{snw.lmq} {
: lmq{qnet.lmq} {
auto keys = snw.core.get_service_node_keys();
assert(keys);
const auto &my_pubkey = keys->pub;
exclude.insert(my_pubkey);
const auto& keys = qnet.core.get_service_keys();
assert(qnet.core.service_node());
exclude.insert(keys.pub);
// Find my positions in the quorums
my_position_count = 0;
for (auto qit = qbegin; qit != qend; ++qit) {
auto &v = (*qit)->validators;
auto found = std::find(v.begin(), v.end(), my_pubkey);
auto found = std::find(v.begin(), v.end(), keys.pub);
if (found == v.end())
my_position.push_back(-1);
else {
@ -305,7 +250,7 @@ public:
}
// Lookup the x25519 and ZMQ connection string for all peers
snw.core.get_service_node_list().for_each_service_node_info_and_proof(need_remotes.begin(), need_remotes.end(),
qnet.core.get_service_node_list().for_each_service_node_info_and_proof(need_remotes.begin(), need_remotes.end(),
[this](const auto &pubkey, const auto &info, const auto &proof) {
if (info.is_active() && proof.pubkey_x25519 && proof.quorumnet_port && proof.public_ip)
remotes.emplace(pubkey, std::make_pair(proof.pubkey_x25519,
@ -482,11 +427,10 @@ quorum_vote_t deserialize_vote(string_view v) {
}
void relay_obligation_votes(void *obj, const std::vector<service_nodes::quorum_vote_t> &votes) {
auto &snw = SNNWrapper::from(obj);
auto &qnet = QnetState::from(obj);
auto my_keys_ptr = snw.core.get_service_node_keys();
assert(my_keys_ptr);
const auto &my_keys = *my_keys_ptr;
const auto& my_keys = qnet.core.get_service_keys();
assert(qnet.core.service_node());
MDEBUG("Starting relay of " << votes.size() << " votes");
std::vector<service_nodes::quorum_vote_t> relayed_votes;
@ -497,7 +441,7 @@ void relay_obligation_votes(void *obj, const std::vector<service_nodes::quorum_v
continue;
}
auto quorum = snw.core.get_service_node_list().get_quorum(vote.type, vote.block_height);
auto quorum = qnet.core.get_service_node_list().get_quorum(vote.type, vote.block_height);
if (!quorum) {
MWARNING("Unable to relay vote: no " << vote.type << " quorum available for height " << vote.block_height);
continue;
@ -511,7 +455,7 @@ void relay_obligation_votes(void *obj, const std::vector<service_nodes::quorum_v
continue;
}
peer_info pinfo{snw, vote.type, quorum};
peer_info pinfo{qnet, vote.type, quorum};
if (!pinfo.my_position_count) {
MWARNING("Invalid vote relay: vote to relay does not include this service node");
continue;
@ -521,10 +465,10 @@ void relay_obligation_votes(void *obj, const std::vector<service_nodes::quorum_v
relayed_votes.push_back(vote);
}
MDEBUG("Relayed " << relayed_votes.size() << " votes");
snw.core.set_service_node_votes_relayed(relayed_votes);
qnet.core.set_service_node_votes_relayed(relayed_votes);
}
void handle_obligation_vote(Message& m, SNNWrapper& snw) {
void handle_obligation_vote(Message& m, QnetState& qnet) {
MDEBUG("Received a relayed obligation vote from " << to_hex(m.conn.pubkey()));
if (m.data.size() != 1) {
@ -541,13 +485,13 @@ void handle_obligation_vote(Message& m, SNNWrapper& snw) {
MWARNING("Received invalid non-obligations vote via quorumnet; ignoring");
return;
}
if (vote.block_height > snw.core.get_current_blockchain_height()) {
if (vote.block_height > qnet.core.get_current_blockchain_height()) {
MDEBUG("Ignoring vote: block height " << vote.block_height << " is too high");
return;
}
cryptonote::vote_verification_context vvc{};
snw.core.add_service_node_vote(vote, vvc);
qnet.core.add_service_node_vote(vote, vvc);
if (vvc.m_verification_failed)
{
MWARNING("Vote verification failed; ignoring vote");
@ -555,7 +499,7 @@ void handle_obligation_vote(Message& m, SNNWrapper& snw) {
}
if (vvc.m_added_to_pool)
relay_obligation_votes(&snw, std::move(vvote));
relay_obligation_votes(&qnet, std::move(vvote));
}
catch (const std::exception &e) {
MWARNING("Deserialization of vote from " << to_hex(m.conn.pubkey()) << " failed: " << e.what());
@ -635,7 +579,7 @@ std::string debug_known_signatures(blink_tx &btx, quorum_array &blink_quorums) {
/// Processes blink signatures; called immediately upon receiving a signature if we know about the
/// tx; otherwise signatures are stored until we learn about the tx and then processed.
void process_blink_signatures(SNNWrapper &snw, const std::shared_ptr<blink_tx> &btxptr, quorum_array &blink_quorums, uint64_t quorum_checksum, std::list<pending_signature> &&signatures,
void process_blink_signatures(QnetState &qnet, const std::shared_ptr<blink_tx> &btxptr, quorum_array &blink_quorums, uint64_t quorum_checksum, std::list<pending_signature> &&signatures,
uint64_t reply_tag, // > 0 if we are expected to send a status update if it becomes accepted/rejected
ConnectionID reply_conn, // who we are supposed to send the status update to
const std::string &received_from = ""s /* x25519 of the peer that sent this, if available (to avoid trying to pointlessly relay back to them) */) {
@ -737,13 +681,13 @@ void process_blink_signatures(SNNWrapper &snw, const std::shared_ptr<blink_tx> &
if (became_approved) {
MINFO("Accumulated enough signatures for blink tx: enabling tx relay");
auto &pool = snw.core.get_pool();
auto &pool = qnet.core.get_pool();
{
auto lock = pool.blink_unique_lock();
pool.add_existing_blink(btxptr);
}
pool.set_relayable({{btx.get_txhash()}});
snw.core.relay_txpool_transactions();
qnet.core.relay_txpool_transactions();
}
if (signatures.empty())
@ -751,13 +695,13 @@ void process_blink_signatures(SNNWrapper &snw, const std::shared_ptr<blink_tx> &
peer_info::exclude_set relay_exclude;
if (!received_from.empty()) {
auto pubkey = snw.core.get_service_node_list().get_pubkey_from_x25519(x25519_from_string(received_from));
auto pubkey = qnet.core.get_service_node_list().get_pubkey_from_x25519(x25519_from_string(received_from));
if (pubkey)
relay_exclude.insert(std::move(pubkey));
}
// We added new signatures that we didn't have before, so relay those signatures to blink peers
peer_info pinfo{snw, quorum_type::blink, blink_quorums.begin(), blink_quorums.end(), true /*opportunistic*/,
peer_info pinfo{qnet, quorum_type::blink, blink_quorums.begin(), blink_quorums.end(), true /*opportunistic*/,
std::move(relay_exclude)};
MDEBUG("Relaying " << signatures.size() << " blink signatures to " << pinfo.strong_peers << " (strong) + " <<
@ -788,10 +732,10 @@ void process_blink_signatures(SNNWrapper &snw, const std::shared_ptr<blink_tx> &
if (reply_tag && reply_conn) {
if (became_approved) {
MINFO("Blink tx became approved; sending result back to originating node");
snw.lmq.send(reply_conn, "bl.good", bt_serialize(bt_dict{{"!", reply_tag}}), send_option::optional{});
qnet.lmq.send(reply_conn, "bl.good", bt_serialize(bt_dict{{"!", reply_tag}}), send_option::optional{});
} else if (became_rejected) {
MINFO("Blink tx became rejected; sending result back to originating node");
snw.lmq.send(reply_conn, "bl.bad", bt_serialize(bt_dict{{"!", reply_tag}}), send_option::optional{});
qnet.lmq.send(reply_conn, "bl.bad", bt_serialize(bt_dict{{"!", reply_tag}}), send_option::optional{});
}
}
}
@ -818,7 +762,7 @@ void process_blink_signatures(SNNWrapper &snw, const std::shared_ptr<blink_tx> &
/// "#" - precomputed tx hash. This much match the actual hash of the transaction (the blink
/// submission will fail immediately if it does not).
///
void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
void handle_blink(lokimq::Message& m, QnetState& qnet) {
// TODO: if someone sends an invalid tx (i.e. one that doesn't get to the distribution stage)
// then put a timeout on that IP during which new submissions from them are dropped for a short
// time.
@ -830,9 +774,10 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
MDEBUG("Received a blink tx from " << (m.conn.sn() ? "SN " : "non-SN ") << to_hex(m.conn.pubkey()));
auto keys = snw.core.get_service_node_keys();
assert(keys);
if (!keys) return;
assert(qnet.core.service_node());
if (!qnet.core.service_node())
return;
const auto& keys = qnet.core.get_service_keys();
if (m.data.size() != 1) {
MINFO("Rejecting blink message: expected one data entry not " << m.data.size());
@ -843,7 +788,7 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
auto tag = get_or<uint64_t>(data, "!", 0);
auto hf_version = snw.core.get_blockchain_storage().get_current_hard_fork_version();
auto hf_version = qnet.core.get_blockchain_storage().get_current_hard_fork_version();
if (hf_version < HF_VERSION_BLINK) {
MWARNING("Rejecting blink message: blink is not available for hardfork " << (int) hf_version);
if (tag)
@ -853,7 +798,7 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
// verify that height is within-2 of current height
auto blink_height = get_int<uint64_t>(data.at("h"));
auto local_height = snw.core.get_current_blockchain_height();
auto local_height = qnet.core.get_current_blockchain_height();
if (blink_height < local_height - 2) {
MINFO("Rejecting blink tx because blink auth height is too low (" << blink_height << " vs. " << local_height << ")");
@ -889,9 +834,9 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
bool already_approved = false, already_rejected = false;
if (tx_hash_str.size() == sizeof(crypto::hash)) {
std::memcpy(tx_hash.data, tx_hash_str.data(), sizeof(crypto::hash));
auto lock = tools::shared_lock(snw.mutex);
auto bit = snw.blinks.find(blink_height);
if (bit != snw.blinks.end()) {
auto lock = tools::shared_lock(qnet.mutex);
auto bit = qnet.blinks.find(blink_height);
if (bit != qnet.blinks.end()) {
auto &umap = bit->second;
auto it = umap.find(tx_hash);
if (it != umap.end() && it->second.btxptr) {
@ -935,7 +880,7 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
quorum_array blink_quorums;
uint64_t checksum = get_int<uint64_t>(data.at("q"));
try {
blink_quorums = get_blink_quorums(blink_height, snw.core.get_service_node_list(), &checksum);
blink_quorums = get_blink_quorums(blink_height, qnet.core.get_service_node_list(), &checksum);
} catch (const std::runtime_error &e) {
MINFO("Rejecting blink tx: " << e.what());
if (tag)
@ -943,8 +888,8 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
return;
}
peer_info pinfo{snw, quorum_type::blink, blink_quorums.begin(), blink_quorums.end(), true /*opportunistic*/,
{snw.core.get_service_node_list().get_pubkey_from_x25519(x25519_from_string(m.conn.pubkey()))} // exclude the peer that just sent it to us
peer_info pinfo{qnet, quorum_type::blink, blink_quorums.begin(), blink_quorums.end(), true /*opportunistic*/,
{qnet.core.get_service_node_list().get_pubkey_from_x25519(x25519_from_string(m.conn.pubkey()))} // exclude the peer that just sent it to us
};
if (pinfo.my_position_count > 0)
@ -997,8 +942,8 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
// signatures for this blink tx that we received or processed before we got here with this tx.
std::list<pending_signature> signatures;
{
auto lock = tools::unique_lock(snw.mutex);
auto &bl_info = snw.blinks[blink_height][tx_hash];
auto lock = tools::unique_lock(qnet.mutex);
auto &bl_info = qnet.blinks[blink_height][tx_hash];
if (bl_info.btxptr) {
MDEBUG("Already seen and forwarded this blink tx, ignoring it.");
return;
@ -1047,7 +992,7 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
} else {
bool already_in_mempool;
cryptonote::tx_verification_context tvc = {};
approved = snw.core.get_pool().add_new_blink(btxptr, tvc, already_in_mempool);
approved = qnet.core.get_pool().add_new_blink(btxptr, tvc, already_in_mempool);
MINFO("Blink TX " << tx_hash << (approved ? " approved and added to mempool" : " rejected"));
if (!approved)
@ -1056,7 +1001,7 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
auto hash_to_sign = btx.hash(approved);
crypto::signature sig;
generate_signature(hash_to_sign, keys->pub, keys->key, sig);
generate_signature(hash_to_sign, keys.pub, keys.key, sig);
// Now that we have the blink tx stored we can add our signature *and* any other pending
// signatures we are holding onto, then blast the entire thing to our peers.
@ -1066,7 +1011,7 @@ void handle_blink(lokimq::Message& m, SNNWrapper& snw) {
signatures.emplace_back(approved, qi, pinfo.my_position[qi], sig);
}
process_blink_signatures(snw, btxptr, blink_quorums, checksum, std::move(signatures), tag, m.conn.pubkey());
process_blink_signatures(qnet, btxptr, blink_quorums, checksum, std::move(signatures), tag, m.conn.pubkey());
}
template <typename T, typename CopyValue>
@ -1105,7 +1050,7 @@ void copy_signature_values(std::list<pending_signature> &signatures, const bt_va
/// each list corresponds to the values at the same position of the other lists.
///
/// Signatures will be forwarded if new; known signatures will be ignored.
void handle_blink_signature(Message& m, SNNWrapper& snw) {
void handle_blink_signature(Message& m, QnetState& qnet) {
MDEBUG("Received a blink tx signature from SN " << to_hex(m.conn.pubkey()));
if (m.data.size() != 1)
@ -1178,14 +1123,14 @@ void handle_blink_signature(Message& m, SNNWrapper& snw) {
if (!(blink_height && saw_hash && saw_checksum && saw_i && saw_r && saw_p && saw_s))
throw std::invalid_argument("Invalid blink signature data: missing required fields");
auto blink_quorums = get_blink_quorums(blink_height, snw.core.get_service_node_list(), &checksum); // throws if bad quorum or checksum mismatch
auto blink_quorums = get_blink_quorums(blink_height, qnet.core.get_service_node_list(), &checksum); // throws if bad quorum or checksum mismatch
uint64_t reply_tag = 0;
ConnectionID reply_conn;
std::shared_ptr<blink_tx> btxptr;
auto find_blink = [&]() {
auto height_it = snw.blinks.find(blink_height);
if (height_it == snw.blinks.end())
auto height_it = qnet.blinks.find(blink_height);
if (height_it == qnet.blinks.end())
return;
auto &blinks_at_height = height_it->second;
auto it = blinks_at_height.find(tx_hash);
@ -1201,19 +1146,19 @@ void handle_blink_signature(Message& m, SNNWrapper& snw) {
// Most of the time we'll already know about the blink and don't need a unique lock to
// extract info we need. If we fail, we'll stash the signature to be processed when we get
// the blink tx itself.
auto lock = tools::shared_lock(snw.mutex);
auto lock = tools::shared_lock(qnet.mutex);
find_blink();
}
if (!btxptr) {
auto lock = tools::unique_lock(snw.mutex);
auto lock = tools::unique_lock(qnet.mutex);
// We probably don't have it, so want to stash the signature until we received it. There's
// a chance, however, that another thread processed it while we were waiting for this
// exclusive mutex, so check it again before we stash a delayed signature.
find_blink();
if (!btxptr) {
MINFO("Blink tx not found in local blink cache; delaying signature verification");
auto &delayed = snw.blinks[blink_height][tx_hash].pending_sigs;
auto &delayed = qnet.blinks[blink_height][tx_hash].pending_sigs;
for (auto &sig : signatures)
delayed.insert(std::move(sig));
return;
@ -1222,7 +1167,7 @@ void handle_blink_signature(Message& m, SNNWrapper& snw) {
MINFO("Found blink tx in local blink cache");
process_blink_signatures(snw, btxptr, blink_quorums, checksum, std::move(signatures), reply_tag, reply_conn, m.conn.pubkey());
process_blink_signatures(qnet, btxptr, blink_quorums, checksum, std::move(signatures), reply_tag, reply_conn, m.conn.pubkey());
}
@ -1240,7 +1185,7 @@ boost::shared_mutex pending_blink_result_mutex;
// Sanity check against runaway active pending blink submissions
constexpr size_t MAX_ACTIVE_PROMISES = 1000;
std::future<std::pair<cryptonote::blink_result, std::string>> send_blink(void *obj, const std::string &tx_blob) {
std::future<std::pair<cryptonote::blink_result, std::string>> send_blink(cryptonote::core& core, const std::string &tx_blob) {
std::promise<std::pair<cryptonote::blink_result, std::string>> promise;
auto future = promise.get_future();
cryptonote::transaction tx;
@ -1287,10 +1232,9 @@ std::future<std::pair<cryptonote::blink_result, std::string>> send_blink(void *o
if (!blink_tag) return future;
try {
auto &snw = SNNWrapper::from(obj);
uint64_t height = snw.core.get_current_blockchain_height();
uint64_t height = core.get_current_blockchain_height();
uint64_t checksum;
auto quorums = get_blink_quorums(height, snw.core.get_service_node_list(), nullptr, &checksum);
auto quorums = get_blink_quorums(height, core.get_service_node_list(), nullptr, &checksum);
// Lookup the x25519 and ZMQ connection string for all possible blink recipients so that we
// know where to send it to, and so that we can immediately exclude SNs that aren't active
@ -1303,7 +1247,7 @@ std::future<std::pair<cryptonote::blink_result, std::string>> send_blink(void *o
std::vector<std::pair<std::string, std::string>> remotes; // x25519 pubkey -> connect string
remotes.reserve(candidates.size());
snw.core.get_service_node_list().for_each_service_node_info_and_proof(candidates.begin(), candidates.end(),
core.get_service_node_list().for_each_service_node_info_and_proof(candidates.begin(), candidates.end(),
[&remotes](const auto &pubkey, const auto &info, const auto &proof) {
if (!info.is_active()) {
MTRACE("Not include inactive node " << pubkey);
@ -1338,7 +1282,7 @@ std::future<std::pair<cryptonote::blink_result, std::string>> send_blink(void *o
for (size_t i : indices) {
MINFO("Relaying blink tx to " << to_hex(remotes[i].first) << " @ " << remotes[i].second);
snw.lmq.send(remotes[i].first, "blink.submit", data, send_option::hint{remotes[i].second});
core.get_lmq().send(remotes[i].first, "blink.submit", data, send_option::hint{remotes[i].second});
}
} catch (...) {
auto lock = tools::unique_lock(pending_blink_result_mutex);
@ -1453,52 +1397,36 @@ void handle_blink_success(Message& m) {
common_blink_response(tag, cryptonote::blink_result::accepted, ""s);
}
void handle_ping(Message& m) {
uint64_t tag = 0;
if (!m.data.empty()) {
auto data = bt_deserialize<bt_dict>(m.data[0]);
tag = get_or<uint64_t>(data, "!", 0);
}
MINFO("Received ping request from " << (m.conn.sn() ? "SN" : "non-SN") << " " << to_hex(m.conn.pubkey()) << ", sending pong");
m.send_back("ping.pong", bt_serialize(bt_dict{{"!", tag}, {"sn", m.conn.sn()}}));
}
void handle_pong(Message& m) {
MINFO("Received pong from " << (m.conn.sn() ? "SN" : "non-SN") << " " << to_hex(m.conn.pubkey()));
}
} // end empty namespace
/// Sets the cryptonote::quorumnet_* function pointers (allowing core to avoid linking to
/// cryptonote_protocol). Called from daemon/daemon.cpp. Also registers quorum command callbacks.
void init_core_callbacks() {
cryptonote::quorumnet_new = new_snnwrapper;
cryptonote::quorumnet_delete = delete_snnwrapper;
cryptonote::quorumnet_refresh_sns = refresh_sns;
cryptonote::quorumnet_new = new_qnetstate;
cryptonote::quorumnet_delete = delete_qnetstate;
cryptonote::quorumnet_relay_obligation_votes = relay_obligation_votes;
cryptonote::quorumnet_send_blink = send_blink;
}
namespace {
void setup_endpoints(SNNWrapper& snw) {
auto& lmq = snw.lmq;
void setup_endpoints(QnetState& qnet) {
auto& lmq = qnet.lmq;
// quorum.*: commands between quorum members, requires that both side of the connection is a SN
lmq.add_category("quorum", Access{AuthLevel::none, true /*remote sn*/, true /*local sn*/}, 2 /*reserved threads*/)
// Receives an obligation vote
.add_command("vote_ob", [&snw](Message& m) { handle_obligation_vote(m, snw); })
.add_command("vote_ob", [&qnet](Message& m) { handle_obligation_vote(m, qnet); })
// Receives blink tx signatures or rejections between quorum members (either original or
// forwarded). These are propagated by the receiver if new
.add_command("blink_sign", [&snw](Message& m) { handle_blink_signature(m, snw); })
.add_command("blink_sign", [&qnet](Message& m) { handle_blink_signature(m, qnet); })
;
// blink.*: commands sent to blink quorum members from anyone (e.g. blink submission)
lmq.add_category("blink", Access{AuthLevel::none, false /*remote sn*/, true /*local sn*/}, 1 /*reserved thread*/)
// Receives a new blink tx submission from an external node, or forward from other quorum
// members who received it from an external node.
.add_command("submit", [&snw](Message& m) { handle_blink(m, snw); })
.add_command("submit", [&qnet](Message& m) { handle_blink(m, qnet); })
;
// bl.*: responses to blinks sent from quorum members back to the node who submitted the blink
@ -1518,13 +1446,6 @@ void setup_endpoints(SNNWrapper& snw) {
.add_command("good", handle_blink_success)
;
// ping.ping, ping.pong: triggers a reply with the auth status, used for quorumnet connectivity
// testing.
lmq.add_category("ping", Access{AuthLevel::none})
.add_command("ping", handle_ping)
.add_command("pong", handle_pong)
;
// Compatibility aliases. No longer used since 7.1.4, but can still be received from previous
// 7.1.x nodes.
// Transition plan:

View File

@ -50,4 +50,5 @@ target_link_libraries(daemon
Boost::filesystem
Boost::program_options
Boost::system
systemd
extra)

View File

@ -49,7 +49,7 @@ command_parser_executor::command_parser_executor(
: m_executor{ip, port, login, ssl_options}
{}
command_parser_executor::command_parser_executor(cryptonote::core_rpc_server& rpc_server)
command_parser_executor::command_parser_executor(cryptonote::rpc::core_rpc_server& rpc_server)
: m_executor{rpc_server}
{}
@ -71,8 +71,8 @@ static bool parse_if_present(std::forward_list<std::string> &list, T &var, const
bool command_parser_executor::print_checkpoints(const std::vector<std::string> &args)
{
uint64_t start_height = cryptonote::COMMAND_RPC_GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE;
uint64_t end_height = cryptonote::COMMAND_RPC_GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE;
uint64_t start_height = cryptonote::rpc::GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE;
uint64_t end_height = cryptonote::rpc::GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE;
std::forward_list<std::string> args_list(args.begin(), args.end());
bool print_json = !args_list.empty() && args_list.front() == "+json";
@ -89,7 +89,7 @@ bool command_parser_executor::print_checkpoints(const std::vector<std::string> &
{
std::cout << "use: print_checkpoints [+json] [start height] [end height]\n"
<< "(omit arguments to print the last "
<< cryptonote::COMMAND_RPC_GET_CHECKPOINTS::NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT << " checkpoints) "
<< cryptonote::rpc::GET_CHECKPOINTS::NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT << " checkpoints) "
<< std::endl;
return false;
}
@ -100,7 +100,7 @@ bool command_parser_executor::print_checkpoints(const std::vector<std::string> &
bool command_parser_executor::print_sn_state_changes(const std::vector<std::string> &args)
{
uint64_t start_height;
uint64_t end_height = cryptonote::COMMAND_RPC_GET_SN_STATE_CHANGES::HEIGHT_SENTINEL_VALUE;
uint64_t end_height = cryptonote::rpc::GET_SN_STATE_CHANGES::HEIGHT_SENTINEL_VALUE;
if (args.empty()) {
std::cout << "Missing first argument start_height" << std::endl;
@ -243,8 +243,8 @@ bool command_parser_executor::print_blockchain_info(const std::vector<std::strin
bool command_parser_executor::print_quorum_state(const std::vector<std::string>& args)
{
uint64_t start_height = cryptonote::COMMAND_RPC_GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE;
uint64_t end_height = cryptonote::COMMAND_RPC_GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE;
uint64_t start_height = cryptonote::rpc::GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE;
uint64_t end_height = cryptonote::rpc::GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE;
std::forward_list<std::string> args_list(args.begin(), args.end());
if (!parse_if_present(args_list, start_height, "start height"))
@ -583,7 +583,7 @@ bool command_parser_executor::set_limit_up(const std::vector<std::string>& args)
{
if(args.size()>1) return false;
if(args.size()==0) {
return m_executor.get_limit_up();
return m_executor.get_limit(true, false);
}
int64_t limit;
try {
@ -601,7 +601,7 @@ bool command_parser_executor::set_limit_down(const std::vector<std::string>& arg
{
if(args.size()>1) return false;
if(args.size()==0) {
return m_executor.get_limit_down();
return m_executor.get_limit(false, true);
}
int64_t limit;
try {
@ -735,7 +735,7 @@ bool command_parser_executor::flush_txpool(const std::vector<std::string>& args)
}
txid = args[0];
}
return m_executor.flush_txpool(txid);
return m_executor.flush_txpool(std::move(txid));
}
bool command_parser_executor::output_histogram(const std::vector<std::string>& args)

View File

@ -52,7 +52,7 @@ public:
);
/// Invokes via local daemon
command_parser_executor(cryptonote::core_rpc_server& rpc_server);
command_parser_executor(cryptonote::rpc::core_rpc_server& rpc_server);
bool print_checkpoints(const std::vector<std::string>& args);

View File

@ -35,13 +35,15 @@
#include "common/loki_integration_test_hooks.h"
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
#include <thread>
#endif
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "daemon"
namespace daemonize {
namespace p = std::placeholders;
command_server::command_server(
uint32_t ip
, uint16_t port
@ -53,13 +55,13 @@ command_server::command_server(
init_commands();
}
command_server::command_server(cryptonote::core_rpc_server& rpc)
command_server::command_server(cryptonote::rpc::core_rpc_server& rpc)
: m_is_rpc{false}, m_parser{rpc}
{
init_commands(&rpc);
}
void command_server::init_commands(cryptonote::core_rpc_server* rpc_server)
void command_server::init_commands(cryptonote::rpc::core_rpc_server* rpc_server)
{
m_command_lookup.set_handler(
"help"
@ -432,25 +434,6 @@ void command_server::init_commands(cryptonote::core_rpc_server* rpc_server)
#endif
}
bool command_server::process_command(const std::string& cmd)
{
return m_command_lookup.process_command(cmd);
}
bool command_server::process_command(const std::vector<std::string>& cmd)
{
bool result = m_command_lookup.process_command(cmd);
if (!result)
{
help(std::vector<std::string>());
}
return result;
}
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
#include <thread>
#endif
bool command_server::start_handling(std::function<void(void)> exit_handler)
{
if (m_is_rpc) return false;

View File

@ -44,7 +44,7 @@ private:
epee::console_handlers_binder m_command_lookup;
public:
/// Remote RPC constructor
/// Remote HTTP RPC constructor
command_server(
uint32_t ip
, uint16_t port
@ -52,19 +52,18 @@ public:
, const epee::net_utils::ssl_options_t& ssl_options
);
/// Non-remote RPC constructor
command_server(cryptonote::core_rpc_server& rpc_server);
/// Non-remote constructor
command_server(cryptonote::rpc::core_rpc_server& rpc_server);
bool process_command(const std::string& cmd);
bool process_command(const std::vector<std::string>& cmd);
template <typename... T>
bool process_command(T&&... args) { return m_command_lookup.process_command(std::forward<T>(args)...); }
bool start_handling(std::function<void(void)> exit_handler = {});
void stop_handling();
private:
void init_commands(cryptonote::core_rpc_server* rpc_server = nullptr);
void init_commands(cryptonote::rpc::core_rpc_server* rpc_server = nullptr);
bool help(const std::vector<std::string>& args);
std::string get_commands_str();

View File

@ -31,12 +31,17 @@
#include <memory>
#include <stdexcept>
#include <boost/algorithm/string/split.hpp>
#include <lokimq/lokimq.h>
#include "misc_log_ex.h"
#if defined(PER_BLOCK_CHECKPOINT)
#include "blocks/blocks.h"
#endif
#include "rpc/daemon_handler.h"
#include "rpc/rpc_args.h"
#include "rpc/http_server.h"
#include "rpc/lmq_server.h"
#include "cryptonote_protocol/quorumnet.h"
#include "common/password.h"
@ -52,19 +57,25 @@
#include <functional>
#ifdef ENABLE_SYSTEMD
extern "C" {
# include <systemd/sd-daemon.h>
}
#endif
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "daemon"
namespace daemonize {
http_rpc_server::http_rpc_server(boost::program_options::variables_map const &vm,
cryptonote::core &core,
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>> &p2p,
cryptonote::rpc::core_rpc_server &corerpc,
const bool restricted,
const std::string &port,
const std::string &description)
: m_server{core, p2p}
, m_description{description}
std::string description)
: m_server{corerpc}
, m_description{std::move(description)}
{
if (!m_server.init(vm, restricted, port))
{
@ -74,7 +85,7 @@ http_rpc_server::http_rpc_server(boost::program_options::variables_map const &vm
void http_rpc_server::run()
{
if (!m_server.run(m_server.m_max_long_poll_connections + cryptonote::core_rpc_server::DEFAULT_RPC_THREADS,
if (!m_server.run(m_server.m_max_long_poll_connections + cryptonote::rpc::http_server::DEFAULT_RPC_THREADS,
false /*wait - for all threads in the pool to exit when terminating*/))
{
throw std::runtime_error("Failed to start " + m_description + " HTTP RPC server.");
@ -101,17 +112,17 @@ http_rpc_server::~http_rpc_server()
static uint16_t parse_public_rpc_port(const boost::program_options::variables_map& vm)
{
const auto& public_node_arg = cryptonote::core_rpc_server::arg_public_node;
const auto& public_node_arg = cryptonote::rpc::http_server::arg_public_node;
const bool public_node = command_line::get_arg(vm, public_node_arg);
if (!public_node)
return 0;
std::string rpc_port_str;
const auto &restricted_rpc_port = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port;
const auto &restricted_rpc_port = cryptonote::rpc::http_server::arg_rpc_restricted_bind_port;
if (!command_line::is_arg_defaulted(vm, restricted_rpc_port))
rpc_port_str = command_line::get_arg(vm, restricted_rpc_port);
else if (command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc))
rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port);
else if (command_line::get_arg(vm, cryptonote::rpc::http_server::arg_restricted_rpc))
rpc_port_str = command_line::get_arg(vm, cryptonote::rpc::http_server::arg_rpc_bind_port);
else
throw std::runtime_error("restricted RPC mode is required for --" + std::string{public_node_arg.name});
@ -140,7 +151,8 @@ daemon::daemon(boost::program_options::variables_map vm_) :
vm{std::move(vm_)},
core{std::make_unique<cryptonote::core>()},
protocol{std::make_unique<protocol_handler>(*core, command_line::get_arg(vm, cryptonote::arg_offline))},
p2p{std::make_unique<node_server>(*protocol)}
p2p{std::make_unique<node_server>(*protocol)},
rpc{std::make_unique<cryptonote::rpc::core_rpc_server>(*core, *p2p)}
{
MGINFO_BLUE("Initializing daemon objects...");
@ -155,20 +167,19 @@ daemon::daemon(boost::program_options::variables_map vm_) :
// Handle circular dependencies
protocol->set_p2p_endpoint(p2p.get());
core->set_cryptonote_protocol(protocol.get());
quorumnet::init_core_callbacks();
{
const auto restricted = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc);
const auto main_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port);
const auto restricted = command_line::get_arg(vm, cryptonote::rpc::http_server::arg_restricted_rpc);
const auto main_rpc_port = command_line::get_arg(vm, cryptonote::rpc::http_server::arg_rpc_bind_port);
MGINFO("- core HTTP RPC server");
http_rpcs.emplace_back(vm, *core, *p2p, restricted, main_rpc_port, "core");
http_rpcs.emplace_back(vm, *rpc, restricted, main_rpc_port, "core");
}
if (!command_line::is_arg_defaulted(vm, cryptonote::core_rpc_server::arg_rpc_restricted_bind_port))
if (!command_line::is_arg_defaulted(vm, cryptonote::rpc::http_server::arg_rpc_restricted_bind_port))
{
auto restricted_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_restricted_bind_port);
auto restricted_rpc_port = command_line::get_arg(vm, cryptonote::rpc::http_server::arg_rpc_restricted_bind_port);
MGINFO("- restricted HTTP RPC server");
http_rpcs.emplace_back(vm, *core, *p2p, true, restricted_rpc_port, "restricted");
http_rpcs.emplace_back(vm, *rpc, true, restricted_rpc_port, "restricted");
}
MGINFO_BLUE("Done daemon object initialization");
@ -210,9 +221,17 @@ daemon::~daemon()
void daemon::init_options(boost::program_options::options_description& option_spec)
{
static bool called = false;
if (called)
throw std::logic_error("daemon::init_options must only be called once");
else
called = true;
cryptonote::core::init_options(option_spec);
node_server::init_options(option_spec);
cryptonote::core_rpc_server::init_options(option_spec);
cryptonote::rpc::core_rpc_server::init_options(option_spec);
cryptonote::rpc::http_server::init_options(option_spec);
cryptonote::rpc::init_lmq_options(option_spec);
quorumnet::init_core_callbacks();
}
bool daemon::run(bool interactive)
@ -253,17 +272,6 @@ bool daemon::run(bool interactive)
rpc.run();
}
// FIXME: this is wonky and needs fixing: if we have no RPC server, then we also get no ability
// to process commands, even in the interactive daemon. It also means a core RPC server needs
// to be usable without having a bound port.
std::unique_ptr<daemonize::command_server> rpc_commands;
if (interactive && http_rpcs.size())
{
MGINFO("Starting command-line processor");
rpc_commands = std::make_unique<daemonize::command_server>(http_rpcs.front().m_server);
rpc_commands->start_handling([this] { stop(); });
}
MGINFO("Starting RPC daemon handler");
cryptonote::rpc::DaemonHandler rpc_daemon_handler(*core, *p2p);
@ -273,7 +281,24 @@ bool daemon::run(bool interactive)
p2p->set_rpc_port(public_rpc_port);
}
MGINFO("Starting LokiMQ");
lmq_rpc = std::make_unique<cryptonote::rpc::lmq_rpc>(*core, *rpc, vm);
core->start_lokimq();
std::unique_ptr<daemonize::command_server> rpc_commands;
if (interactive)
{
MGINFO("Starting command-line processor");
rpc_commands = std::make_unique<daemonize::command_server>(*rpc);
rpc_commands->start_handling([this] { stop(); });
}
MGINFO_GREEN("Starting up main network");
#ifdef ENABLE_SYSTEMD
sd_notify(0, ("READY=1\nSTATUS=" + core->get_status_string()).c_str());
#endif
p2p->run(); // blocks until p2p goes down
MGINFO_YELLOW("Main network stopped");

View File

@ -33,6 +33,8 @@
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "p2p/net_node.h"
#include "rpc/core_rpc_server.h"
#include "rpc/http_server.h"
#include "rpc/lmq_server.h"
#include "blocks/blocks.h"
#include "rpc/core_rpc_server.h"
@ -49,17 +51,15 @@ class http_rpc_server
{
public:
http_rpc_server(boost::program_options::variables_map const &vm,
cryptonote::core &core,
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>> &p2p,
cryptonote::rpc::core_rpc_server &corerpc,
const bool restricted,
const std::string &port,
const std::string &description);
std::string description);
void run();
void stop();
~http_rpc_server();
// FIXME - replace with rpc::http_server
cryptonote::core_rpc_server m_server;
cryptonote::rpc::http_server m_server;
std::string m_description;
};
@ -86,7 +86,9 @@ private:
std::unique_ptr<cryptonote::core> core;
std::unique_ptr<protocol_handler> protocol;
std::unique_ptr<node_server> p2p;
std::unique_ptr<cryptonote::rpc::core_rpc_server> rpc;
std::list<http_rpc_server> http_rpcs;
std::unique_ptr<cryptonote::rpc::lmq_rpc> lmq_rpc;
};
} // namespace daemonize

View File

@ -230,7 +230,7 @@ int main(int argc, char const * argv[])
{
const cryptonote::rpc_args::descriptors arg{};
auto rpc_ip_str = command_line::get_arg(vm, arg.rpc_bind_ip);
auto rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port);
auto rpc_port_str = command_line::get_arg(vm, cryptonote::rpc::http_server::arg_rpc_bind_port);
uint32_t rpc_ip;
uint16_t rpc_port;
@ -254,7 +254,7 @@ int main(int argc, char const * argv[])
login = tools::login::parse(
has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), false, [](bool verify) {
#ifdef HAVE_READLINE
rdln::suspend_readline pause_readline;
rdln::suspend_readline pause_readline;
#endif
return tools::password_container::prompt(verify, "Daemon client password");
}
@ -271,18 +271,17 @@ int main(int argc, char const * argv[])
return 1;
daemonize::command_server rpc_commands{rpc_ip, rpc_port, std::move(login), std::move(*ssl_options)};
if (rpc_commands.process_command(command))
{
return 0;
}
else
{
try {
if (rpc_commands.process_command(command))
return 0;
} catch (const std::out_of_range& e) {
#ifdef HAVE_READLINE
rdln::suspend_readline pause_readline;
#endif
std::cerr << "Unknown command: " << command.front() << std::endl;
return 1;
std::cout << "Unknown command: " << e.what() << ". Try 'help' for available commands\n";
}
return 1;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,8 @@ namespace daemonize {
class rpc_command_executor final {
private:
std::unique_ptr<tools::t_rpc_client> m_rpc_client;
cryptonote::core_rpc_server* m_rpc_server = nullptr;
cryptonote::rpc::core_rpc_server* m_rpc_server = nullptr;
const cryptonote::rpc::rpc_context m_server_context{true};
public:
/// Executor for remote connection RPC
@ -58,9 +59,39 @@ public:
, const epee::net_utils::ssl_options_t& ssl_options
);
/// Executor for local daemon RPC
rpc_command_executor(cryptonote::core_rpc_server& rpc_server)
rpc_command_executor(cryptonote::rpc::core_rpc_server& rpc_server)
: m_rpc_server{&rpc_server} {}
/// Runs some RPC command either via json_rpc or a direct core rpc call.
///
/// @param req the request object (rvalue reference)
/// @param res the response object (lvalue reference)
/// @param error print this (and, on exception, the exception message) on failure. If empty then
/// nothing is printed on failure.
/// @param check_status_ok whether we require res.status == STATUS_OK to consider the request
/// successful
template <typename RPC>
bool invoke(typename RPC::request&& req, typename RPC::response& res, const std::string& error, bool check_status_ok = true)
{
try {
if (m_rpc_client) {
if (!m_rpc_client->json_rpc_request(req, res, std::string{RPC::names()[0]}, error))
return false;
} else {
res = m_rpc_server->invoke(std::move(req), m_server_context);
}
if (!check_status_ok || res.status == cryptonote::rpc::STATUS_OK)
return true;
} catch (const std::exception& e) {
if (!error.empty())
tools::fail_msg_writer() << error << ": " << e.what();
return true;
} catch (...) {}
if (!error.empty())
tools::fail_msg_writer() << error;
return false;
}
bool print_checkpoints(uint64_t start_height, uint64_t end_height, bool print_json);
bool print_sn_state_changes(uint64_t start_height, uint64_t end_height);
@ -87,10 +118,14 @@ public:
bool set_log_level(int8_t level);
bool set_log_categories(const std::string &categories);
bool set_log_categories(std::string categories);
bool print_height();
private:
bool print_block(cryptonote::rpc::GET_BLOCK::request&& req, bool include_hdex);
public:
bool print_block_by_hash(crypto::hash block_hash, bool include_hex);
bool print_block_by_height(uint64_t height, bool include_hex);
@ -115,11 +150,7 @@ public:
bool print_status();
bool get_limit();
bool get_limit_up();
bool get_limit_down();
bool get_limit(bool up = true, bool down = true);
bool set_limit(int64_t limit_down, int64_t limit_up);
@ -131,13 +162,13 @@ public:
bool print_bans();
bool ban(const std::string &address, time_t seconds);
bool ban(const std::string &address, time_t seconds, bool clear_ban = false);
bool unban(const std::string &address);
bool banned(const std::string &address);
bool flush_txpool(const std::string &txid);
bool flush_txpool(std::string txid);
bool output_histogram(const std::vector<uint64_t> &amounts, uint64_t min_count, uint64_t max_count);
@ -157,7 +188,7 @@ public:
bool print_sn_key();
bool print_sn_status(const std::vector<std::string>& args);
bool print_sn_status(std::vector<std::string> args);
bool print_sr(uint64_t height);

View File

@ -34,6 +34,7 @@ loki_add_executable(gen_multisig "loki-gen-trusted-multisig"
target_link_libraries(gen_multisig
PRIVATE
wallet
rpc_commands
cryptonote_core
epee
epee_readline

View File

@ -44,6 +44,7 @@
#include "misc_language.h"
#include "int-util.h"
#include "mnemonics/electrum-words.h"
#include "common/loki.h"
#include <boost/crc.hpp>
#include "chinese_simplified.h"
@ -292,7 +293,7 @@ namespace crypto
}
std::vector<uint32_t> matched_indices;
auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));});
LOKI_DEFER { memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0])); };
Language::Base *language;
if (!find_seed_language(seed, has_checksum, matched_indices, &language))
{

View File

@ -33,6 +33,7 @@ add_library(net
socks.cpp
socks_connect.cpp
tor_address.cpp
epee_network_address_hack.cpp
)
target_link_libraries(net PRIVATE
common

View File

@ -0,0 +1,38 @@
#include "net/net_utils_base.h"
#include "storages/portable_storage.h"
#include "tor_address.h"
#include "i2p_address.h"
// This unholy hack of defining epee implementation outside of epee is here because of Monero's lack
// of quality code review that allowed someone to add circular dependencies between src/net/ and
// epee/net_utils_base.cpp. See the comment in epee/include/net/net_utils_base.h for the sordid
// details.
//
// TODO: epee needs to die.
namespace epee { namespace net_utils {
KV_SERIALIZE_MAP_CODE_BEGIN(network_address)
static constexpr std::integral_constant<bool, is_store> is_store_{};
std::uint8_t type = std::uint8_t(is_store ? this_ref.get_type_id() : address_type::invalid);
if (!epee::serialization::selector<is_store>::serialize(type, stg, hparent_section, "type"))
return false;
switch (address_type(type))
{
case address_type::ipv4:
return this_ref.template serialize_addr<ipv4_network_address>(is_store_, stg, hparent_section);
case address_type::ipv6:
return this_ref.template serialize_addr<ipv6_network_address>(is_store_, stg, hparent_section);
case address_type::tor:
return this_ref.template serialize_addr<net::tor_address>(is_store_, stg, hparent_section);
case address_type::i2p:
return this_ref.template serialize_addr<net::i2p_address>(is_store_, stg, hparent_section);
default:
MERROR("Unsupported network address type: " << (unsigned)type);
return false;
}
KV_SERIALIZE_MAP_CODE_END()
}}

View File

@ -132,19 +132,12 @@ namespace nodetool
struct network_zone;
using connect_func = boost::optional<p2p_connection_context>(network_zone&, epee::net_utils::network_address const&, epee::net_utils::ssl_support_t);
struct config_t
struct config
{
config_t()
: m_net_config(),
m_peer_id(crypto::rand<uint64_t>()),
m_support_flags(0)
{}
network_config m_net_config;
uint64_t m_peer_id;
uint32_t m_support_flags;
network_config m_net_config{};
uint64_t m_peer_id{crypto::rand<uint64_t>()};
uint32_t m_support_flags{0};
};
typedef epee::misc_utils::struct_init<config_t> config;
struct network_zone
{

View File

@ -980,8 +980,8 @@ namespace nodetool
{
network_zone& zone = m_network_zones.at(context_.m_remote_address.get_zone());
typename COMMAND_HANDSHAKE::request arg;
typename COMMAND_HANDSHAKE::response rsp;
typename COMMAND_HANDSHAKE::request arg{};
typename COMMAND_HANDSHAKE::response rsp{};
get_local_node_data(arg.node_data, zone);
m_payload_handler.get_payload_sync_data(arg.payload_data);
@ -991,7 +991,7 @@ namespace nodetool
bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_HANDSHAKE::response>(context_.m_connection_id, COMMAND_HANDSHAKE::ID, arg, zone.m_net_server.get_config_object(),
[this, &pi, &ev, &hsh_result, &just_take_peerlist, &context_](int code, typename COMMAND_HANDSHAKE::response&& rsp, p2p_connection_context& context)
{
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ev.raise();});
LOKI_DEFER { ev.raise(); };
if(code < 0)
{
@ -1919,7 +1919,6 @@ namespace nodetool
rsp.connections_count = get_connections_count();
rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count();
rsp.version = LOKI_VERSION_FULL;
rsp.os_version = tools::get_os_version_string();
m_payload_handler.get_stat_info(rsp.payload_info);
return 1;
}
@ -2082,8 +2081,8 @@ namespace nodetool
LOG_WARNING_CC(ping_context, "back ping connect failed to " << address.str());
return false;
}
COMMAND_PING::request req;
COMMAND_PING::response rsp;
COMMAND_PING::request req{};
COMMAND_PING::response rsp{};
//vc2010 workaround
/*std::string ip_ = ip;
std::string port_=port;
@ -2136,7 +2135,7 @@ namespace nodetool
if(context.m_remote_address.get_zone() != epee::net_utils::zone::public_)
return false;
COMMAND_REQUEST_SUPPORT_FLAGS::request support_flags_request;
COMMAND_REQUEST_SUPPORT_FLAGS::request support_flags_request{};
bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_REQUEST_SUPPORT_FLAGS::response>
(
context.m_connection_id,

View File

@ -181,27 +181,26 @@ namespace nodetool
/************************************************************************/
/* */
/************************************************************************/
template<class t_playload_type>
template<class Payload>
struct COMMAND_HANDSHAKE_T
{
const static int ID = P2P_COMMANDS_POOL_BASE + 1;
struct request_t
struct request
{
basic_node_data node_data;
t_playload_type payload_data;
Payload payload_data;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(node_data)
KV_SERIALIZE(payload_data)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
struct response
{
basic_node_data node_data;
t_playload_type payload_data;
Payload payload_data;
std::vector<peerlist_entry> local_peerlist_new;
BEGIN_KV_SERIALIZE_MAP()
@ -238,31 +237,29 @@ namespace nodetool
}
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
/************************************************************************/
/* */
/************************************************************************/
template<class t_playload_type>
template<class Payload>
struct COMMAND_TIMED_SYNC_T
{
const static int ID = P2P_COMMANDS_POOL_BASE + 2;
struct request_t
struct request
{
t_playload_type payload_data;
Payload payload_data;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(payload_data)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
struct response
{
uint64_t local_time;
t_playload_type payload_data;
Payload payload_data;
std::vector<peerlist_entry> local_peerlist_new;
BEGIN_KV_SERIALIZE_MAP()
@ -299,7 +296,6 @@ namespace nodetool
}
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
/************************************************************************/
@ -317,16 +313,15 @@ namespace nodetool
#define PING_OK_RESPONSE_STATUS_TEXT "OK"
struct request_t
struct request
{
/*actually we don't need to send any real data*/
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
struct response
{
std::string status;
peerid_type peer_id;
@ -336,7 +331,6 @@ namespace nodetool
KV_SERIALIZE(peer_id)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
@ -359,16 +353,15 @@ namespace nodetool
{
const static int ID = P2P_COMMANDS_POOL_BASE + 4;
struct request_t
struct request
{
proof_of_trust tr;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tr)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
struct response
{
std::string version;
std::string os_version;
@ -384,7 +377,6 @@ namespace nodetool
KV_SERIALIZE(payload_info)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
@ -395,16 +387,15 @@ namespace nodetool
{
const static int ID = P2P_COMMANDS_POOL_BASE + 5;
struct request_t
struct request
{
proof_of_trust tr;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tr)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
struct response
{
std::vector<peerlist_entry> local_peerlist_white;
std::vector<peerlist_entry> local_peerlist_gray;
@ -419,7 +410,6 @@ namespace nodetool
KV_SERIALIZE(local_time)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
/************************************************************************/
@ -429,14 +419,13 @@ namespace nodetool
{
const static int ID = P2P_COMMANDS_POOL_BASE + 6;
struct request_t
struct request
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
struct response
{
peerid_type my_id;
@ -444,7 +433,6 @@ namespace nodetool
KV_SERIALIZE(my_id)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
/************************************************************************/
@ -454,14 +442,13 @@ namespace nodetool
{
const static int ID = P2P_COMMANDS_POOL_BASE + 7;
struct request_t
struct request
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
struct response
{
uint32_t support_flags;
@ -469,7 +456,6 @@ namespace nodetool
KV_SERIALIZE(support_flags)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};

View File

@ -27,7 +27,11 @@
# 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.
add_library(rpc_base
add_library(rpc_commands
core_rpc_server_commands_defs.cpp
)
add_library(rpc_args
rpc_args.cpp
)
@ -43,28 +47,36 @@ add_library(daemon_messages
add_library(daemon_rpc_server
daemon_handler.cpp
zmq_server.cpp
http_server.cpp
lmq_server.cpp
)
target_link_libraries(rpc_base
target_link_libraries(rpc_commands
PUBLIC
common
PRIVATE
cryptonote_protocol
extra)
target_link_libraries(rpc_args
PUBLIC
common
epee
Boost::thread
Boost::program_options
OpenSSL::SSL
PRIVATE
extra)
target_link_libraries(rpc
PUBLIC
rpc_base
cryptonote_core
cryptonote_protocol
rpc_commands
rpc_args
net
version
Boost::regex
PRIVATE
cryptonote_protocol
Boost::thread
Boost::program_options
OpenSSL::SSL
Boost::regex
extra)
target_link_libraries(daemon_messages

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
// Copyright (c) 2018-2020, The Loki Project
// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
@ -28,12 +29,9 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#pragma once
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include "net/http_server_impl_base.h"
#include <mapbox/variant.hpp>
#include "net/http_client.h"
#include "core_rpc_server_commands_defs.h"
#include "cryptonote_core/cryptonote_core.h"
@ -47,32 +45,123 @@
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "daemon.rpc"
namespace cryptonote
{
static constexpr auto rpc_long_poll_timeout = 15s;
namespace boost { namespace program_options {
class options_description;
class variables_map;
}}
/************************************************************************/
/* */
/************************************************************************/
class core_rpc_server: public epee::http_server_impl_base<core_rpc_server>
namespace cryptonote { namespace rpc {
using namespace lokimq::literals;
static constexpr auto long_poll_timeout = 15s;
/// Exception when trying to invoke an RPC command that indicate a parameter parse failure (will
/// give an invalid params error for JSON-RPC, for example).
struct parse_error : std::runtime_error { using std::runtime_error::runtime_error; };
/// Exception used to signal various types of errors with a request back to the caller. This
/// exception indicates that the caller did something wrong: bad data, invalid value, etc., but
/// don't indicate a local problem (and so we'll log them only at debug). For more serious,
/// internal errors a command should throw some other stl error (e.g. std::runtime_error or
/// perhaps std::logic_error), which will result in a local daemon warning (and a generic internal
/// error response to the user).
///
/// For JSON RPC these become an error response with the code as the error.code value and the
/// string as the error.message.
/// For HTTP JSON these become a 500 Internal Server Error response with the message as the body.
/// For LokiMQ the code becomes the first part of the response and the message becomes the
/// second part of the response.
struct rpc_error {
/// \param code - a signed, 16-bit numeric code. 0 must not be used (as it is used for a
/// success code in LokiMQ), and values in the -32xxx range are reserved by JSON-RPC.
///
/// \param message - a message to send along with the error code (see general description above).
rpc_error(int16_t code, std::string message)
: code{code}, message{std::move(message)} {}
int16_t code;
std::string message;
};
/// Junk that epee makes us deal with to pass in a generically parsed json value
using jsonrpc_params = std::pair<epee::serialization::portable_storage, epee::serialization::storage_entry>;
enum struct rpc_source : uint8_t { internal, http, lmq };
/// Contains the context of the invocation, which must be filled out by the glue code (e.g. HTTP
/// RPC server) with requester-specific context details.
struct rpc_context {
// Specifies that the requestor has admin permissions (e.g. is on an unrestricted RPC port, or
// is a local internal request). This can be used to provide different results for an admin
// versus non-admin when invoking a public RPC command. (Note that non-public RPC commands do
// not need to check this field for authentication: a non-public invoke() is not called in the
// first place if attempted by a public requestor).
bool admin;
// The RPC engine source of the request, i.e. internal, HTTP, LMQ
rpc_source source;
// A free-form identifier identifiying the remote address of the request; this might be IP:PORT,
// or could contain a pubkey, or ...
lokimq::string_view remote;
};
struct rpc_request {
// The request body; for a non-HTTP-JSON-RPC request the string will be populated with the
// unparsed request body (though may be empty, e.g. for GET requests). For HTTP JSON-RPC
// request, if the request has a "params" value then the epee storage pair will be set to the
// portable_storage entry and the storage entry containing "params". If "params" is omitted
// entirely (or, for LMQ, there is no data part) then the string will be set in the variant (and
// empty).
//
// The returned value in either case is the serialized value to return.
//
// If sometimes goes wrong, throw.
mapbox::util::variant<string_view, jsonrpc_params> body;
// Values to pass through to the invoke() call
rpc_context context;
};
class core_rpc_server;
/// Stores an RPC command callback. These are set up in core_rpc_server.cpp.
struct rpc_command {
// Called with the incoming command data; returns the response body if all goes well,
// otherwise throws an exception.
std::string(*invoke)(rpc_request&&, core_rpc_server&);
bool is_public; // callable via restricted RPC
bool is_binary; // only callable at /name (for HTTP RPC), and binary data, not JSON.
};
/// RPC command registration; to add a new command, define it in core_rpc_server_commands_defs.h
/// and then actually do the registration in core_rpc_server.cpp.
extern const std::unordered_map<std::string, std::shared_ptr<const rpc_command>> rpc_commands;
/**
* Core RPC server.
*
* This class handles all internal core RPC requests, but does not itself listen for anything
* external. It is meant to be used by other RPC server bridge classes (such as rpc::http_server)
* to map incoming HTTP requests into internal core RPC requests through this class, and then send
* them back to the requester.
*
* In order to add a new RPC request object you must:
*
* - add the appropriate NEWTYPE struct with request/response substructs to
* core_rpc_server_commands_defs.h; the base types it inherits from determine the permissions
* and data type, and a static `names()` method determined the rpc name (and any older aliases).
* - add an invoke() method overload declaration here which takes a NEWTYPE::request and rpc_context,
* and returns a NEWTYPE::response.
* - add the invoke() definition in core_rpc_server.cpp, and add NEWTYPE to the list of command
* types near the top of core_rpc_server.cpp.
*/
class core_rpc_server
{
public:
static constexpr int DEFAULT_RPC_THREADS = 2;
static const command_line::arg_descriptor<std::string, false, true, 2> arg_rpc_bind_port;
static const command_line::arg_descriptor<std::string> arg_rpc_restricted_bind_port;
static const command_line::arg_descriptor<bool> arg_restricted_rpc;
static const command_line::arg_descriptor<std::string> arg_rpc_ssl;
static const command_line::arg_descriptor<std::string> arg_rpc_ssl_private_key;
static const command_line::arg_descriptor<std::string> arg_rpc_ssl_certificate;
static const command_line::arg_descriptor<std::string> arg_rpc_ssl_ca_certificates;
static const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_fingerprints;
static const command_line::arg_descriptor<bool> arg_rpc_ssl_allow_any_cert;
static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_address;
static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_login;
static const command_line::arg_descriptor<bool> arg_public_node;
static const command_line::arg_descriptor<int> arg_rpc_long_poll_connections;
typedef epee::net_utils::connection_context_base connection_context;
core_rpc_server(
core& cr
@ -80,217 +169,91 @@ namespace cryptonote
);
static void init_options(boost::program_options::options_description& desc);
bool init(
const boost::program_options::variables_map& vm,
const bool restricted,
const std::string& port
);
void init(const boost::program_options::variables_map& vm);
network_type nettype() const { return m_core.get_nettype(); }
CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map
BEGIN_URI_MAP2()
MAP_URI_AUTO_JON2("/get_height", on_get_height, COMMAND_RPC_GET_HEIGHT)
MAP_URI_AUTO_JON2("/getheight", on_get_height, COMMAND_RPC_GET_HEIGHT)
MAP_URI_AUTO_BIN2("/get_blocks.bin", on_get_blocks, COMMAND_RPC_GET_BLOCKS_FAST)
MAP_URI_AUTO_BIN2("/getblocks.bin", on_get_blocks, COMMAND_RPC_GET_BLOCKS_FAST)
MAP_URI_AUTO_BIN2("/get_blocks_by_height.bin", on_get_blocks_by_height, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT)
MAP_URI_AUTO_BIN2("/getblocks_by_height.bin", on_get_blocks_by_height, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT)
MAP_URI_AUTO_BIN2("/get_hashes.bin", on_get_hashes, COMMAND_RPC_GET_HASHES_FAST)
MAP_URI_AUTO_BIN2("/gethashes.bin", on_get_hashes, COMMAND_RPC_GET_HASHES_FAST)
MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES)
MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs_bin, COMMAND_RPC_GET_OUTPUTS_BIN)
MAP_URI_AUTO_JON2("/get_transactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
MAP_URI_AUTO_JON2("/get_alt_blocks_hashes", on_get_alt_blocks_hashes, COMMAND_RPC_GET_ALT_BLOCKS_HASHES)
MAP_URI_AUTO_JON2("/is_key_image_spent", on_is_key_image_spent, COMMAND_RPC_IS_KEY_IMAGE_SPENT)
MAP_URI_AUTO_JON2("/send_raw_transaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX)
MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX)
MAP_URI_AUTO_JON2_IF("/start_mining", on_start_mining, COMMAND_RPC_START_MINING, !m_restricted)
MAP_URI_AUTO_JON2_IF("/stop_mining", on_stop_mining, COMMAND_RPC_STOP_MINING, !m_restricted)
MAP_URI_AUTO_JON2_IF("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC, !m_restricted)
MAP_URI_AUTO_JON2_IF("/get_peer_list", on_get_peer_list, COMMAND_RPC_GET_PEER_LIST, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted)
MAP_URI_AUTO_JON2("/get_transaction_pool", on_get_transaction_pool, COMMAND_RPC_GET_TRANSACTION_POOL)
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes_bin, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN)
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES)
MAP_URI_AUTO_JON2("/get_transaction_pool_stats", on_get_transaction_pool_stats, COMMAND_RPC_GET_TRANSACTION_POOL_STATS)
MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted)
MAP_URI_AUTO_JON2("/get_info", on_get_info, COMMAND_RPC_GET_INFO)
MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO)
MAP_URI_AUTO_JON2_IF("/get_net_stats", on_get_net_stats, COMMAND_RPC_GET_NET_STATS, !m_restricted)
MAP_URI_AUTO_JON2("/get_limit", on_get_limit, COMMAND_RPC_GET_LIMIT)
MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted)
MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/in_peers", on_in_peers, COMMAND_RPC_IN_PEERS, !m_restricted)
MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS)
MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted)
MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION)
MAP_URI_AUTO_BIN2("/get_output_blacklist.bin", on_get_output_blacklist_bin, COMMAND_RPC_GET_OUTPUT_BLACKLIST)
MAP_URI_AUTO_JON2_IF("/pop_blocks", on_pop_blocks, COMMAND_RPC_POP_BLOCKS, !m_restricted)
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC("get_block_count", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT)
MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT)
MAP_JON_RPC_WE("on_get_block_hash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH)
MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH)
MAP_JON_RPC_WE("get_block_template", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE)
MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE)
MAP_JON_RPC_WE("submit_block", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
MAP_JON_RPC_WE_IF("generateblocks", on_generateblocks, COMMAND_RPC_GENERATEBLOCKS, !m_restricted)
MAP_JON_RPC_WE("get_last_block_header", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER)
MAP_JON_RPC_WE("getlastblockheader", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER)
MAP_JON_RPC_WE("get_block_header_by_hash", on_get_block_header_by_hash, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH)
MAP_JON_RPC_WE("getblockheaderbyhash", on_get_block_header_by_hash, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH)
MAP_JON_RPC_WE("get_block_header_by_height", on_get_block_header_by_height, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT)
MAP_JON_RPC_WE("getblockheaderbyheight", on_get_block_header_by_height, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT)
MAP_JON_RPC_WE("get_block_headers_range", on_get_block_headers_range, COMMAND_RPC_GET_BLOCK_HEADERS_RANGE)
MAP_JON_RPC_WE("getblockheadersrange", on_get_block_headers_range, COMMAND_RPC_GET_BLOCK_HEADERS_RANGE)
MAP_JON_RPC_WE("get_block", on_get_block, COMMAND_RPC_GET_BLOCK)
MAP_JON_RPC_WE("getblock", on_get_block, COMMAND_RPC_GET_BLOCK)
MAP_JON_RPC_WE_IF("get_connections", on_get_connections, COMMAND_RPC_GET_CONNECTIONS, !m_restricted)
MAP_JON_RPC_WE("get_info", on_get_info_json, COMMAND_RPC_GET_INFO)
MAP_JON_RPC_WE("hard_fork_info", on_hard_fork_info, COMMAND_RPC_HARD_FORK_INFO)
MAP_JON_RPC_WE_IF("set_bans", on_set_bans, COMMAND_RPC_SETBANS, !m_restricted)
MAP_JON_RPC_WE_IF("get_bans", on_get_bans, COMMAND_RPC_GETBANS, !m_restricted)
MAP_JON_RPC_WE_IF("banned", on_banned, COMMAND_RPC_BANNED, !m_restricted)
MAP_JON_RPC_WE_IF("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL, !m_restricted)
MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM)
MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION)
MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted)
MAP_JON_RPC_WE("get_fee_estimate", on_get_base_fee_estimate, COMMAND_RPC_GET_BASE_FEE_ESTIMATE)
MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted)
MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted)
MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted)
MAP_JON_RPC_WE("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG)
MAP_JON_RPC_WE("get_output_distribution", on_get_output_distribution, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION)
MAP_JON_RPC_WE_IF("prune_blockchain", on_prune_blockchain, COMMAND_RPC_PRUNE_BLOCKCHAIN, !m_restricted)
//
// Loki
//
MAP_JON_RPC_WE("get_quorum_state", on_get_quorum_state, COMMAND_RPC_GET_QUORUM_STATE)
MAP_JON_RPC_WE_IF("get_service_node_registration_cmd_raw", on_get_service_node_registration_cmd_raw, COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD_RAW, !m_restricted)
MAP_JON_RPC_WE("get_service_node_blacklisted_key_images", on_get_service_node_blacklisted_key_images, COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES)
MAP_JON_RPC_WE_IF("get_service_node_registration_cmd", on_get_service_node_registration_cmd, COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD, !m_restricted)
MAP_JON_RPC_WE_IF("get_service_node_key", on_get_service_node_key, COMMAND_RPC_GET_SERVICE_NODE_KEY, !m_restricted)
MAP_JON_RPC_WE_IF("get_service_node_privkey", on_get_service_node_privkey, COMMAND_RPC_GET_SERVICE_NODE_PRIVKEY, !m_restricted)
MAP_JON_RPC_WE("get_service_node_status", on_get_service_node_status, COMMAND_RPC_GET_SERVICE_NODE_STATUS)
MAP_JON_RPC_WE("get_service_nodes", on_get_service_nodes, COMMAND_RPC_GET_SERVICE_NODES)
MAP_JON_RPC_WE("get_all_service_nodes", on_get_all_service_nodes, COMMAND_RPC_GET_SERVICE_NODES)
MAP_JON_RPC_WE("get_n_service_nodes", on_get_n_service_nodes, COMMAND_RPC_GET_N_SERVICE_NODES)
MAP_JON_RPC_WE("get_staking_requirement", on_get_staking_requirement, COMMAND_RPC_GET_STAKING_REQUIREMENT)
MAP_JON_RPC_WE("get_checkpoints", on_get_checkpoints, COMMAND_RPC_GET_CHECKPOINTS)
MAP_JON_RPC_WE_IF("perform_blockchain_test", on_perform_blockchain_test, COMMAND_RPC_PERFORM_BLOCKCHAIN_TEST, !m_restricted)
MAP_JON_RPC_WE_IF("storage_server_ping", on_storage_server_ping, COMMAND_RPC_STORAGE_SERVER_PING, !m_restricted)
MAP_JON_RPC_WE_IF("lokinet_ping", on_lokinet_ping, COMMAND_RPC_LOKINET_PING, !m_restricted)
MAP_JON_RPC_WE("get_service_nodes_state_changes", on_get_service_nodes_state_changes, COMMAND_RPC_GET_SN_STATE_CHANGES)
MAP_JON_RPC_WE_IF("report_peer_storage_server_status", on_report_peer_storage_server_status, COMMAND_RPC_REPORT_PEER_SS_STATUS, !m_restricted)
MAP_JON_RPC_WE_IF("test_trigger_p2p_resync", on_test_trigger_p2p_resync, COMMAND_RPC_TEST_TRIGGER_P2P_RESYNC, !m_restricted)
MAP_JON_RPC_WE("lns_names_to_owners", on_lns_names_to_owners, COMMAND_RPC_LNS_NAMES_TO_OWNERS)
MAP_JON_RPC_WE("lns_owners_to_names", on_lns_owners_to_names, COMMAND_RPC_LNS_OWNERS_TO_NAMES)
END_JSON_RPC_MAP()
END_URI_MAP2()
bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, const connection_context *ctx = NULL);
bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, const connection_context *ctx = NULL);
bool on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res, const connection_context *ctx = NULL);
bool on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res, const connection_context *ctx = NULL);
bool on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res, const connection_context *ctx = NULL);
bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, const connection_context *ctx = NULL);
bool on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res, const connection_context *ctx = NULL);
bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, const connection_context *ctx = NULL);
bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, const connection_context *ctx = NULL);
bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, const connection_context *ctx = NULL);
bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, const connection_context *ctx = NULL);
bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res, const connection_context *ctx = NULL);
bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res, const connection_context *ctx = NULL);
bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res, const connection_context *ctx = NULL);
bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, const connection_context *ctx = NULL);
bool on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx = NULL);
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, const connection_context *ctx = NULL);
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res, const connection_context *ctx = NULL);
bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx = NULL);
bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res, const connection_context *ctx = NULL);
bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool_hashes_bin(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, const connection_context *ctx = NULL);
bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx = NULL);
bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res, const connection_context *ctx = NULL);
bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res, const connection_context *ctx = NULL);
bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx = NULL);
bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx = NULL);
bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx = NULL);
bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, const connection_context *ctx = NULL);
bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res, const connection_context *ctx = NULL);
//
// Loki
//
bool on_get_output_blacklist_bin(const COMMAND_RPC_GET_OUTPUT_BLACKLIST::request& req, COMMAND_RPC_GET_OUTPUT_BLACKLIST::response& res, const connection_context *ctx = NULL);
//json_rpc
bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, const connection_context *ctx = NULL);
bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_block_headers_range(const COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request& req, COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_block(const COMMAND_RPC_GET_BLOCK::request& req, COMMAND_RPC_GET_BLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_output_distribution(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_prune_blockchain(const COMMAND_RPC_PRUNE_BLOCKCHAIN::request& req, COMMAND_RPC_PRUNE_BLOCKCHAIN::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
//
// Loki
//
bool on_get_quorum_state(const COMMAND_RPC_GET_QUORUM_STATE::request& req, COMMAND_RPC_GET_QUORUM_STATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_service_node_registration_cmd_raw(const COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD_RAW::request& req, COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD_RAW::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_service_node_registration_cmd(const COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD::request& req, COMMAND_RPC_GET_SERVICE_NODE_REGISTRATION_CMD::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_service_node_blacklisted_key_images(const COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::request& req, COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::response& res, epee::json_rpc::error &error_resp, const connection_context *ctx = NULL);
bool on_get_service_node_key(const COMMAND_RPC_GET_SERVICE_NODE_KEY::request& req, COMMAND_RPC_GET_SERVICE_NODE_KEY::response& res, epee::json_rpc::error &error_resp, const connection_context *ctx = NULL);
bool on_get_service_node_privkey(const COMMAND_RPC_GET_SERVICE_NODE_PRIVKEY::request& req, COMMAND_RPC_GET_SERVICE_NODE_PRIVKEY::response& res, epee::json_rpc::error &error_resp, const connection_context *ctx = NULL);
bool on_get_service_node_status(const COMMAND_RPC_GET_SERVICE_NODE_STATUS::request& req, COMMAND_RPC_GET_SERVICE_NODE_STATUS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_service_nodes(const COMMAND_RPC_GET_SERVICE_NODES::request& req, COMMAND_RPC_GET_SERVICE_NODES::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_n_service_nodes(const COMMAND_RPC_GET_N_SERVICE_NODES::request& req, COMMAND_RPC_GET_N_SERVICE_NODES::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_all_service_nodes(const COMMAND_RPC_GET_SERVICE_NODES::request& req, COMMAND_RPC_GET_SERVICE_NODES::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_staking_requirement(const COMMAND_RPC_GET_STAKING_REQUIREMENT::request& req, COMMAND_RPC_GET_STAKING_REQUIREMENT::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
/// Provide a proof that this node holds the blockchain
bool on_perform_blockchain_test(const COMMAND_RPC_PERFORM_BLOCKCHAIN_TEST::request& req, COMMAND_RPC_PERFORM_BLOCKCHAIN_TEST::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_storage_server_ping(const COMMAND_RPC_STORAGE_SERVER_PING::request& req, COMMAND_RPC_STORAGE_SERVER_PING::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_lokinet_ping(const COMMAND_RPC_LOKINET_PING::request& req, COMMAND_RPC_LOKINET_PING::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_checkpoints(const COMMAND_RPC_GET_CHECKPOINTS::request& req, COMMAND_RPC_GET_CHECKPOINTS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_service_nodes_state_changes(const COMMAND_RPC_GET_SN_STATE_CHANGES::request& req, COMMAND_RPC_GET_SN_STATE_CHANGES::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_report_peer_storage_server_status(const COMMAND_RPC_REPORT_PEER_SS_STATUS::request& req, COMMAND_RPC_REPORT_PEER_SS_STATUS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_test_trigger_p2p_resync(const COMMAND_RPC_TEST_TRIGGER_P2P_RESYNC::request& req, COMMAND_RPC_TEST_TRIGGER_P2P_RESYNC::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_lns_names_to_owners(const COMMAND_RPC_LNS_NAMES_TO_OWNERS::request &req, COMMAND_RPC_LNS_NAMES_TO_OWNERS::response &res, epee::json_rpc::error &error_resp, const connection_context *ctx = NULL);
bool on_lns_owners_to_names(const COMMAND_RPC_LNS_OWNERS_TO_NAMES::request &req, COMMAND_RPC_LNS_OWNERS_TO_NAMES::response &res, epee::json_rpc::error &error_resp, const connection_context *ctx = NULL);
//-----------------------
GET_HEIGHT::response invoke(GET_HEIGHT::request&& req, rpc_context context);
GET_BLOCKS_FAST::response invoke(GET_BLOCKS_FAST::request&& req, rpc_context context);
GET_ALT_BLOCKS_HASHES::response invoke(GET_ALT_BLOCKS_HASHES::request&& req, rpc_context context);
GET_BLOCKS_BY_HEIGHT::response invoke(GET_BLOCKS_BY_HEIGHT::request&& req, rpc_context context);
GET_HASHES_FAST::response invoke(GET_HASHES_FAST::request&& req, rpc_context context);
GET_TRANSACTIONS::response invoke(GET_TRANSACTIONS::request&& req, rpc_context context);
IS_KEY_IMAGE_SPENT::response invoke(IS_KEY_IMAGE_SPENT::request&& req, rpc_context context);
GET_TX_GLOBAL_OUTPUTS_INDEXES::response invoke(GET_TX_GLOBAL_OUTPUTS_INDEXES::request&& req, rpc_context context);
SEND_RAW_TX::response invoke(SEND_RAW_TX::request&& req, rpc_context context);
START_MINING::response invoke(START_MINING::request&& req, rpc_context context);
STOP_MINING::response invoke(STOP_MINING::request&& req, rpc_context context);
MINING_STATUS::response invoke(MINING_STATUS::request&& req, rpc_context context);
GET_OUTPUTS_BIN::response invoke(GET_OUTPUTS_BIN::request&& req, rpc_context context);
GET_OUTPUTS::response invoke(GET_OUTPUTS::request&& req, rpc_context context);
GET_INFO::response invoke(GET_INFO::request&& req, rpc_context context);
GET_NET_STATS::response invoke(GET_NET_STATS::request&& req, rpc_context context);
SAVE_BC::response invoke(SAVE_BC::request&& req, rpc_context context);
GET_PEER_LIST::response invoke(GET_PEER_LIST::request&& req, rpc_context context);
SET_LOG_HASH_RATE::response invoke(SET_LOG_HASH_RATE::request&& req, rpc_context context);
SET_LOG_LEVEL::response invoke(SET_LOG_LEVEL::request&& req, rpc_context context);
SET_LOG_CATEGORIES::response invoke(SET_LOG_CATEGORIES::request&& req, rpc_context context);
GET_TRANSACTION_POOL::response invoke(GET_TRANSACTION_POOL::request&& req, rpc_context context);
GET_TRANSACTION_POOL_HASHES_BIN::response invoke(GET_TRANSACTION_POOL_HASHES_BIN::request&& req, rpc_context context);
GET_TRANSACTION_POOL_HASHES::response invoke(GET_TRANSACTION_POOL_HASHES::request&& req, rpc_context context);
GET_TRANSACTION_POOL_STATS::response invoke(GET_TRANSACTION_POOL_STATS::request&& req, rpc_context context);
STOP_DAEMON::response invoke(STOP_DAEMON::request&& req, rpc_context context);
GET_LIMIT::response invoke(GET_LIMIT::request&& req, rpc_context context);
SET_LIMIT::response invoke(SET_LIMIT::request&& req, rpc_context context);
OUT_PEERS::response invoke(OUT_PEERS::request&& req, rpc_context context);
IN_PEERS::response invoke(IN_PEERS::request&& req, rpc_context context);
UPDATE::response invoke(UPDATE::request&& req, rpc_context context);
GET_OUTPUT_DISTRIBUTION::response invoke(GET_OUTPUT_DISTRIBUTION::request&& req, rpc_context context);
GET_OUTPUT_DISTRIBUTION_BIN::response invoke(GET_OUTPUT_DISTRIBUTION_BIN::request&& req, rpc_context context);
POP_BLOCKS::response invoke(POP_BLOCKS::request&& req, rpc_context context);
GETBLOCKCOUNT::response invoke(GETBLOCKCOUNT::request&& req, rpc_context context);
GETBLOCKHASH::response invoke(GETBLOCKHASH::request&& req, rpc_context context);
GETBLOCKTEMPLATE::response invoke(GETBLOCKTEMPLATE::request&& req, rpc_context context);
SUBMITBLOCK::response invoke(SUBMITBLOCK::request&& req, rpc_context context);
GENERATEBLOCKS::response invoke(GENERATEBLOCKS::request&& req, rpc_context context);
GET_LAST_BLOCK_HEADER::response invoke(GET_LAST_BLOCK_HEADER::request&& req, rpc_context context);
GET_BLOCK_HEADER_BY_HASH::response invoke(GET_BLOCK_HEADER_BY_HASH::request&& req, rpc_context context);
GET_BLOCK_HEADER_BY_HEIGHT::response invoke(GET_BLOCK_HEADER_BY_HEIGHT::request&& req, rpc_context context);
GET_BLOCK_HEADERS_RANGE::response invoke(GET_BLOCK_HEADERS_RANGE::request&& req, rpc_context context);
GET_BLOCK::response invoke(GET_BLOCK::request&& req, rpc_context context);
GET_CONNECTIONS::response invoke(GET_CONNECTIONS::request&& req, rpc_context context);
HARD_FORK_INFO::response invoke(HARD_FORK_INFO::request&& req, rpc_context context);
SETBANS::response invoke(SETBANS::request&& req, rpc_context context);
GETBANS::response invoke(GETBANS::request&& req, rpc_context context);
BANNED::response invoke(BANNED::request&& req, rpc_context context);
FLUSH_TRANSACTION_POOL::response invoke(FLUSH_TRANSACTION_POOL::request&& req, rpc_context context);
GET_OUTPUT_HISTOGRAM::response invoke(GET_OUTPUT_HISTOGRAM::request&& req, rpc_context context);
GET_VERSION::response invoke(GET_VERSION::request&& req, rpc_context context);
GET_COINBASE_TX_SUM::response invoke(GET_COINBASE_TX_SUM::request&& req, rpc_context context);
GET_BASE_FEE_ESTIMATE::response invoke(GET_BASE_FEE_ESTIMATE::request&& req, rpc_context context);
GET_ALTERNATE_CHAINS::response invoke(GET_ALTERNATE_CHAINS::request&& req, rpc_context context);
RELAY_TX::response invoke(RELAY_TX::request&& req, rpc_context context);
SYNC_INFO::response invoke(SYNC_INFO::request&& req, rpc_context context);
GET_TRANSACTION_POOL_BACKLOG::response invoke(GET_TRANSACTION_POOL_BACKLOG::request&& req, rpc_context context);
PRUNE_BLOCKCHAIN::response invoke(PRUNE_BLOCKCHAIN::request&& req, rpc_context context);
GET_OUTPUT_BLACKLIST::response invoke(GET_OUTPUT_BLACKLIST::request&& req, rpc_context context);
GET_QUORUM_STATE::response invoke(GET_QUORUM_STATE::request&& req, rpc_context context);
GET_SERVICE_NODE_REGISTRATION_CMD_RAW::response invoke(GET_SERVICE_NODE_REGISTRATION_CMD_RAW::request&& req, rpc_context context);
GET_SERVICE_NODE_REGISTRATION_CMD::response invoke(GET_SERVICE_NODE_REGISTRATION_CMD::request&& req, rpc_context context);
GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::response invoke(GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::request&& req, rpc_context context);
GET_SERVICE_KEYS::response invoke(GET_SERVICE_KEYS::request&& req, rpc_context context);
GET_SERVICE_PRIVKEYS::response invoke(GET_SERVICE_PRIVKEYS::request&& req, rpc_context context);
GET_SERVICE_NODE_STATUS::response invoke(GET_SERVICE_NODE_STATUS::request&& req, rpc_context context);
GET_SERVICE_NODES::response invoke(GET_SERVICE_NODES::request&& req, rpc_context context);
GET_STAKING_REQUIREMENT::response invoke(GET_STAKING_REQUIREMENT::request&& req, rpc_context context);
PERFORM_BLOCKCHAIN_TEST::response invoke(PERFORM_BLOCKCHAIN_TEST::request&& req, rpc_context context);
STORAGE_SERVER_PING::response invoke(STORAGE_SERVER_PING::request&& req, rpc_context context);
LOKINET_PING::response invoke(LOKINET_PING::request&& req, rpc_context context);
GET_CHECKPOINTS::response invoke(GET_CHECKPOINTS::request&& req, rpc_context context);
GET_SN_STATE_CHANGES::response invoke(GET_SN_STATE_CHANGES::request&& req, rpc_context context);
REPORT_PEER_SS_STATUS::response invoke(REPORT_PEER_SS_STATUS::request&& req, rpc_context context);
TEST_TRIGGER_P2P_RESYNC::response invoke(TEST_TRIGGER_P2P_RESYNC::request&& req, rpc_context context);
LNS_NAMES_TO_OWNERS::response invoke(LNS_NAMES_TO_OWNERS::request&& req, rpc_context context);
LNS_OWNERS_TO_NAMES::response invoke(LNS_OWNERS_TO_NAMES::request&& req, rpc_context context);
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
void on_relay_uptime_and_votes()
{
m_core.submit_uptime_proof();
@ -328,34 +291,29 @@ namespace cryptonote
}
#endif
int m_max_long_poll_connections;
private:
std::atomic<int> m_long_poll_active_connections;
bool check_core_busy();
bool check_core_ready();
template<typename response>
void fill_sn_response_entry(response &entry, const service_nodes::service_node_pubkey_info &sn_info, uint64_t current_height);
void fill_sn_response_entry(GET_SERVICE_NODES::response::entry& entry, const service_nodes::service_node_pubkey_info &sn_info, uint64_t current_height);
//utils
uint64_t get_block_reward(const block& blk);
bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash);
enum invoke_http_mode { JON, BIN, JON_RPC };
void fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash);
boost::unique_lock<boost::shared_mutex> should_bootstrap_lock();
template <typename COMMAND_TYPE>
bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r);
bool use_bootstrap_daemon_if_necessary(const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res);
core& m_core;
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_p2p;
std::string m_bootstrap_daemon_address;
epee::net_utils::http::http_simple_client m_http_client;
boost::shared_mutex m_bootstrap_daemon_mutex;
bool m_should_use_bootstrap_daemon;
std::atomic<bool> m_should_use_bootstrap_daemon;
std::chrono::system_clock::time_point m_bootstrap_height_check_time;
bool m_was_bootstrap_ever_used;
network_type m_nettype;
bool m_restricted;
};
}
}} // namespace cryptonote::rpc
BOOST_CLASS_VERSION(nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >, 1);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
// Copyright (c) 2018-2020, The Loki Project
// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
@ -28,20 +29,23 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#pragma once
#include <cstdint>
namespace cryptonote { namespace rpc {
#define CORE_RPC_ERROR_CODE_WRONG_PARAM -1
#define CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT -2
#define CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE -3
#define CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS -4
#define CORE_RPC_ERROR_CODE_INTERNAL_ERROR -5
#define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB -6
#define CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED -7
#define CORE_RPC_ERROR_CODE_CORE_BUSY -9
#define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE -10
#define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11
#define CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS -12
#define CORE_RPC_ERROR_CODE_REGTEST_REQUIRED -13
constexpr int16_t
ERROR_WRONG_PARAM = -1,
ERROR_TOO_BIG_HEIGHT = -2,
ERROR_TOO_BIG_RESERVE_SIZE = -3,
ERROR_WRONG_WALLET_ADDRESS = -4,
ERROR_INTERNAL_ERROR = -5,
ERROR_WRONG_BLOCKBLOB = -6,
ERROR_BLOCK_NOT_ACCEPTED = -7,
ERROR_CORE_BUSY = -9,
ERROR_WRONG_BLOCKBLOB_SIZE = -10,
ERROR_UNSUPPORTED_RPC = -11,
ERROR_MINING_TO_SUBADDRESS = -12,
ERROR_REGTEST_REQUIRED = -13;
}}

View File

@ -53,7 +53,7 @@ namespace rpc
{
std::vector<std::pair<std::pair<blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, blobdata> > > > blocks;
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, GET_BLOCKS_FAST::MAX_COUNT))
{
res.status = Message::STATUS_FAILED;
res.error_details = "core::find_blockchain_supplement() returned false";

288
src/rpc/http_server.cpp Normal file
View File

@ -0,0 +1,288 @@
#include "http_server.h"
#include "rpc/rpc_args.h"
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "daemon.rpc"
namespace cryptonote { namespace rpc {
using namespace lokimq::literals;
const command_line::arg_descriptor<std::string, false, true, 2> http_server::arg_rpc_bind_port = {
"rpc-bind-port"
, "Port for RPC server"
, std::to_string(config::RPC_DEFAULT_PORT)
, {{ &cryptonote::arg_testnet_on, &cryptonote::arg_stagenet_on }}
, [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
if (testnet_stagenet[0] && defaulted)
return std::to_string(config::testnet::RPC_DEFAULT_PORT);
else if (testnet_stagenet[1] && defaulted)
return std::to_string(config::stagenet::RPC_DEFAULT_PORT);
return val;
}
};
const command_line::arg_descriptor<std::string> http_server::arg_rpc_restricted_bind_port = {
"rpc-restricted-bind-port"
, "Port for restricted RPC server"
, ""
};
const command_line::arg_descriptor<bool> http_server::arg_restricted_rpc = {
"restricted-rpc"
, "Restrict RPC to view only commands and do not return privacy sensitive data in RPC calls"
, false
};
const command_line::arg_descriptor<bool> http_server::arg_public_node = {
"public-node"
, "Allow other users to use the node as a remote (restricted RPC mode, view-only commands) and advertise it over P2P"
, false
};
//
// Loki
//
const command_line::arg_descriptor<int> http_server::arg_rpc_long_poll_connections = {
"rpc-long-poll-connections"
, "Number of RPC connections allocated for long polling wallet queries to the TX pool"
, 16
};
constexpr int http_server::DEFAULT_RPC_THREADS;
//-----------------------------------------------------------------------------------
void http_server::init_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_rpc_bind_port);
command_line::add_arg(desc, arg_rpc_restricted_bind_port);
command_line::add_arg(desc, arg_restricted_rpc);
command_line::add_arg(desc, arg_public_node);
command_line::add_arg(desc, arg_rpc_long_poll_connections);
}
//------------------------------------------------------------------------------------------------------------------------------
bool http_server::init(
const boost::program_options::variables_map& vm
, const bool restricted
, const std::string& port
)
{
m_restricted = restricted;
m_net_server.set_threads_prefix("RPC");
m_max_long_poll_connections = command_line::get_arg(vm, arg_rpc_long_poll_connections);
auto rpc_config = cryptonote::rpc_args::process(vm, true);
if (!rpc_config)
return false;
boost::optional<epee::net_utils::http::login> http_login{};
if (rpc_config->login)
http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
return epee::http_server_impl_base<http_server, connection_context>::init(
rng, std::move(port), std::move(rpc_config->bind_ip),
std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
);
}
static constexpr http_response_code
HTTP_OK{200, "OK"_sv},
HTTP_BAD_REQUEST{400, "Bad Request"_sv},
HTTP_FORBIDDEN{403, "Forbidden"_sv},
HTTP_NOT_FOUND{404, "Not Found"_sv},
HTTP_ERROR{500, "Internal Server Error"_sv};
bool http_server::handle_http_request(
const epee::net_utils::http::http_request_info& query_info,
epee::net_utils::http::http_response_info& response,
connection_context& context)
{
std::chrono::steady_clock::time_point start;
bool time_logging = LOG_ENABLED(Debug);
if (time_logging)
start = std::chrono::steady_clock::now();
std::pair<int, lokimq::string_view> http_status = HTTP_ERROR;
std::string exception;
try {
http_status = handle_http(query_info, response, context);
} catch (const std::exception& e) {
exception = ", request raised an exception: "s + e.what();
} catch (...) {
exception = ", request raised an unknown exception";
}
response.m_response_code = http_status.first;
response.m_response_comment = std::string{http_status.second};
std::string elapsed;
if (time_logging)
{
auto dur = std::chrono::steady_clock::now() - start;
std::ostringstream el;
el << ", in ";
el.precision(3);
if (dur >= 1s)
el << std::chrono::duration_cast<std::chrono::milliseconds>(dur).count() / 1000. << 's';
else if (dur >= 1ms)
el << std::chrono::duration_cast<std::chrono::microseconds>(dur).count() / 1000. << "ms";
else if (dur >= 1us)
el << std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() / 1000. << "us";
else
el << std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() << "ns";
elapsed = el.str();
}
MLOG(exception.empty() ? el::Level::Info : el::Level::Warning,
"HTTP [" << context.m_remote_address.host_str() << "] " << query_info.m_http_method_str << " " << query_info.m_URI <<
" >>> " << http_status.first << " " << http_status.second <<
exception << elapsed);
return true;
}
static http_response_code json_rpc_error(int code, std::string message, std::string& body)
{
epee::json_rpc::error_response rsp;
rsp.jsonrpc = "2.0";
rsp.error.code = code;
rsp.error.message = std::move(message);
epee::serialization::store_t_to_json(rsp, body);
return HTTP_OK;
}
http_response_code http_server::handle_http(
const epee::net_utils::http::http_request_info& query_info,
epee::net_utils::http::http_response_info& response_info,
connection_context& context)
{
auto uri = query_info.m_URI.size() > 0 && query_info.m_URI[0] == '/' ? query_info.m_URI.substr(1) : query_info.m_URI;
if (uri == "json_rpc")
return handle_json_rpc_request(query_info, response_info, context);
auto it = rpc_commands.find(uri);
if (it == rpc_commands.end())
return HTTP_NOT_FOUND;
auto& cmd = *it->second;
if (m_restricted && !cmd.is_public)
return HTTP_FORBIDDEN;
rpc_request request;
request.context.admin = m_restricted;
request.body = std::move(query_info.m_body);
// Really, epee, mime "tipe"? I suppose that is for when epee analizes the response lacation to
// see whether the verifivation varialbe failed.
response_info.m_mime_tipe = cmd.is_binary ? "application/octet-stream" : "application/json";
response_info.m_header_info.m_content_type = response_info.m_mime_tipe;
try {
response_info.m_body = cmd.invoke(std::move(request), m_server);
} catch (const parse_error& e) {
// This isn't really WARNable as it's the client fault; log at info level instead.
MINFO("RPC request for '/" << uri << "' called with invalid/unparseable data: " << e.what());
return HTTP_BAD_REQUEST;
} catch (const std::exception& e) {
MWARNING("RPC request '/" << uri << "' request raised an exception: " << e.what());
return HTTP_ERROR;
} catch (...) {
MWARNING("RPC request '/" << uri << "' request raised an unknown exception");
return HTTP_ERROR;
}
}
http_response_code http_server::handle_json_rpc_request(
const epee::net_utils::http::http_request_info& query_info,
epee::net_utils::http::http_response_info& response_info,
connection_context& context)
{
auto& body = response_info.m_body;
rpc_request request;
request.context.admin = m_restricted;
request.body = jsonrpc_params{};
auto& epee_stuff = request.body.get_unchecked<jsonrpc_params>();
auto& ps = epee_stuff.first;
if(!ps.load_from_json(query_info.m_body))
return json_rpc_error(-32700, "Parse error", body);
epee::serialization::storage_entry id{std::string{}};
ps.get_value("id", id, nullptr);
std::string method;
if(!ps.get_value("method", method, nullptr))
return json_rpc_error(-32600, "Invalid Request", body);
auto it = rpc_commands.find(method);
if (it == rpc_commands.end() || it->second->is_binary)
return json_rpc_error(-32601, "Method not found", body);
const auto& command = *it->second;
if (m_restricted && !command.is_public)
return json_rpc_error(403, "Forbidden; this command is not available over public RPC", body);
constexpr std::array<lokimq::string_view, 3> json_rpc_ = {
R"({"jsonrpc": "2.0", "id": )"_sv, R"(, "result": )"_sv, R"(
}
)"_sv};
std::string id_str;
{
std::ostringstream o;
epee::serialization::dump_as_json(o, id, 0 /*indent*/, false /*newlines*/);
id_str = o.str();
}
// Try to load "params" into a generic epee value; if it fails (because there is no "params")
// then we will replace it with an empty string to signal that no params were provided.
if (!ps.get_value("params", epee_stuff.second, nullptr))
request.body = ""_sv;
std::string result;
try {
result = command.invoke(std::move(request), m_server);
} catch (const parse_error& e) {
// This isn't really WARNable as it's the client fault; log at info level instead.
MINFO("JSON RPC request for '" << method << "' called with invalid data: " << e.what());
return json_rpc_error(-32602, "Invalid params", body);
} catch (const std::exception& e) {
MWARNING("json_rpc '" << method << "' request raised an exception: " << e.what());
return json_rpc_error(-32603, "Internal error", body);
} catch (...) {
MWARNING("json_rpc '" << method << "' request raised an unknown exception");
return json_rpc_error(-32603, "Internal error", body);
}
// The string is pre-serialized JSON. But epee serialization is garbage so there's no way we
// can actually JSON serialize this using epee without building a separate wrapper struct for
// each serializable type, which is just stupid (but that's life with epee and divorce is
// currently too expensive) so build it ourselves.
size_t needed = json_rpc_[0].size() + json_rpc_[1].size() + json_rpc_[2].size() + id_str.size() + result.size();
if (result.capacity() >= needed) {
// The returned result has enough spare capacity to avoid a reallocation (though we
// still have to shift the contents).
auto inner_size = result.size();
result.resize(needed);
std::copy_backward(result.begin(), result.begin() + inner_size, result.begin() + (needed - json_rpc_[2].size()));
body = std::move(result);
} else {
// Otherwise we need a new string, so clear and resize it to what we need, then copy in the
// JSON into the right spot.
body.clear();
body.resize(needed);
std::copy(result.begin(), result.end(), body.begin() + json_rpc_[0].size() + json_rpc_[1].size() + id_str.size());
}
// Now copy the prefix, id, and tail into the right locations
auto bodyit = std::copy(json_rpc_[0].begin(), json_rpc_[0].end(), body.begin()); // Prefix
bodyit = std::copy(id_str.begin(), id_str.end(), bodyit); // id (right after prefix)
std::copy(json_rpc_[1].begin(), json_rpc_[1].end(), bodyit); // middle (right after id)
// json data already copied
std::copy(json_rpc_[2].begin(), json_rpc_[2].end(), body.begin() + (needed - json_rpc_[2].size())); // tail at the end
return HTTP_OK;
}
}} // namespace cryptonote::rpc

95
src/rpc/http_server.h Normal file
View File

@ -0,0 +1,95 @@
// Copyright (c) 2018-2020, The Loki Project
// Copyright (c) 2014-2019, 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.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include "net/http_server_impl_base.h"
#include "common/command_line.h"
#include "core_rpc_server.h"
namespace cryptonote { namespace rpc {
using http_response_code = std::pair<int, lokimq::string_view>;
/************************************************************************/
/* Core HTTP RPC server */
/************************************************************************/
class http_server: public epee::http_server_impl_base<http_server>
{
public:
static constexpr int DEFAULT_RPC_THREADS = 2;
static const command_line::arg_descriptor<std::string, false, true, 2> arg_rpc_bind_port;
static const command_line::arg_descriptor<std::string> arg_rpc_restricted_bind_port;
static const command_line::arg_descriptor<bool> arg_restricted_rpc;
static const command_line::arg_descriptor<std::string> arg_rpc_ssl;
static const command_line::arg_descriptor<std::string> arg_rpc_ssl_private_key;
static const command_line::arg_descriptor<std::string> arg_rpc_ssl_certificate;
static const command_line::arg_descriptor<std::string> arg_rpc_ssl_ca_certificates;
static const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_fingerprints;
static const command_line::arg_descriptor<bool> arg_rpc_ssl_allow_any_cert;
static const command_line::arg_descriptor<bool> arg_public_node;
static const command_line::arg_descriptor<int> arg_rpc_long_poll_connections;
typedef epee::net_utils::connection_context_base connection_context;
http_server(core_rpc_server& server) : m_server{server} {}
static void init_options(boost::program_options::options_description& desc);
bool init(
const boost::program_options::variables_map& vm,
const bool restricted,
const std::string& port
);
bool handle_http_request(
const epee::net_utils::http::http_request_info& query_info,
epee::net_utils::http::http_response_info& response,
connection_context& context) override;
http_response_code handle_http(
const epee::net_utils::http::http_request_info& query_info,
epee::net_utils::http::http_response_info& response_info,
connection_context& context);
http_response_code handle_json_rpc_request(
const epee::net_utils::http::http_request_info& query_info,
epee::net_utils::http::http_response_info& response_info,
connection_context& context);
int m_max_long_poll_connections;
private:
core_rpc_server& m_server;
bool m_restricted;
std::atomic<int> m_long_poll_active_connections;
};
}} // namespace cryptonote::rpc

328
src/rpc/lmq_server.cpp Normal file
View File

@ -0,0 +1,328 @@
#include "lmq_server.h"
#include "lokimq/lokimq.h"
#include "common/lock.h"
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "daemon.rpc"
namespace cryptonote { namespace rpc {
using namespace lokimq;
namespace {
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_public{
"lmq-public",
"Adds a public, unencrypted LokiMQ RPC listener (with restricted capabilities) at the given "
"address; can be specified multiple times. Examples: tcp://0.0.0.0:5555 (listen on port 5555), "
"tcp://198.51.100.42:5555 (port 5555 on specific IPv4 address), tcp://[::]:5555, "
"tcp://[2001:db8::abc]:5555 (IPv6), or ipc:///path/to/socket to listen on a unix domain socket"};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_curve_public{
"lmq-curve-public",
"Adds a curve-encrypted LokiMQ RPC listener at the given address that accepts (restricted) rpc "
"commands from any client. Clients must already know this server's public x25519 key to "
"establish an encrypted connection."};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_curve{
"lmq-curve",
"Adds a curve-encrypted LokiMQ RPC listener at the given address that only accepts client connections from whitelisted client x25519 pubkeys. "
"Clients must already know this server's public x25519 key to establish an encrypted connection. When running in service node mode "
"the quorumnet port is already listening as if specified with --lmq-curve."};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_admin{
"lmq-admin",
"Adds an x25519 pubkey of a client permitted to connect to the --lmq-curve, --lmq-curve-public, or quorumnet address(es) with unrestricted (admin) capabilities."};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_user{
"lmq-user",
"Specifies an x25519 pubkey of a client permitted to connect to the --lmq-curve or quorumnet address(es) with restricted capabilities"};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_local_control{
"lmq-local-control",
"Adds an unencrypted LokiMQ RPC listener with full, unrestricted capabilities and no authentication at the given address. "
"WARNING: Do not use this on a publicly accessible address!"};
void check_lmq_listen_addr(lokimq::string_view addr) {
// Crude check for basic validity; you can specify all sorts of invalid things, but at least
// we can check the prefix for something that looks zmq-y.
if (addr.size() < 7 || (addr.substr(0, 6) != "tcp://" && addr.substr(0, 6) != "ipc://"))
throw std::runtime_error("Error: lmq listen address '" + std::string(addr) + "' is invalid: expected tcp://IP:PORT, tcp://[IPv6]:PORT or ipc:///path/to/socket");
}
auto as_x_pubkeys(const std::vector<std::string>& pk_strings) {
std::vector<crypto::x25519_public_key> pks;
pks.reserve(pk_strings.size());
for (const auto& pkstr : pk_strings) {
if (pkstr.size() != 64 || !lokimq::is_hex(pkstr))
throw std::runtime_error("Invalid LMQ login pubkey: '" + pkstr + "'; expected 64-char hex pubkey");
pks.emplace_back();
lokimq::to_hex(pkstr.begin(), pkstr.end(), reinterpret_cast<char *>(&pks.back()));
}
return pks;
}
// LMQ RPC responses consist of [CODE, DATA] for code we (partially) mimic HTTP error codes: 200
// means success, anything else means failure. (We don't have codes for Forbidden or Not Found
// because those happen at the LMQ protocol layer).
constexpr string_view
LMQ_OK{"200"_sv},
LMQ_BAD_REQUEST{"400"_sv},
LMQ_ERROR{"500"_sv};
} // end anonymous namespace
void init_lmq_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_lmq_public);
command_line::add_arg(desc, arg_lmq_curve_public);
command_line::add_arg(desc, arg_lmq_curve);
command_line::add_arg(desc, arg_lmq_admin);
command_line::add_arg(desc, arg_lmq_user);
command_line::add_arg(desc, arg_lmq_local_control);
}
lmq_rpc::lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::program_options::variables_map& vm)
: core_{core}, rpc_{rpc}
{
auto& lmq = core.get_lmq();
auto& auth = core._lmq_auth_level_map();
// Set up any requested listening sockets. (Note: if we are a service node, we'll already have
// the quorumnet listener set up in cryptonote_core).
for (const auto &addr : command_line::get_arg(vm, arg_lmq_public)) {
check_lmq_listen_addr(addr);
MGINFO("LMQ listening on " << addr << " (public unencrypted)");
lmq.listen_plain(addr,
[&core](string_view ip, string_view pk, bool /*sn*/) { return core.lmq_allow(ip, pk, AuthLevel::basic); });
}
for (const auto &addr : command_line::get_arg(vm, arg_lmq_curve_public)) {
check_lmq_listen_addr(addr);
MGINFO("LMQ listening on " << addr << " (public curve)");
lmq.listen_curve(addr,
[&core](string_view ip, string_view pk, bool /*sn*/) { return core.lmq_allow(ip, pk, AuthLevel::basic); });
}
for (const auto &addr : command_line::get_arg(vm, arg_lmq_curve)) {
check_lmq_listen_addr(addr);
MGINFO("LMQ listening on " << addr << " (curve restricted)");
lmq.listen_curve(addr,
[&core](string_view ip, string_view pk, bool /*sn*/) { return core.lmq_allow(ip, pk, AuthLevel::denied); });
}
for (const auto &addr : command_line::get_arg(vm, arg_lmq_local_control)) {
check_lmq_listen_addr(addr);
MGINFO("LMQ listening on " << addr << " (unauthenticated local admin)");
lmq.listen_plain(addr,
[&core](string_view ip, string_view pk, bool /*sn*/) { return core.lmq_allow(ip, pk, AuthLevel::admin); });
}
// Insert our own pubkey so that, e.g., console commands from localhost automatically get full access
{
crypto::x25519_public_key my_pubkey;
const std::string& pk = lmq.get_pubkey();
std::copy(pk.begin(), pk.end(), my_pubkey.data);
auth.emplace(std::move(my_pubkey), AuthLevel::admin);
}
// User-specified admin/user pubkeys
for (auto& pk : as_x_pubkeys(command_line::get_arg(vm, arg_lmq_admin)))
auth.emplace(std::move(pk), AuthLevel::admin);
for (auto& pk : as_x_pubkeys(command_line::get_arg(vm, arg_lmq_user)))
auth.emplace(std::move(pk), AuthLevel::basic);
// basic (non-admin) rpc commands go into the "rpc." category (e.g. 'rpc.get_info')
lmq.add_category("rpc", AuthLevel::basic);
// Admin rpc commands go into "admin.". We also always keep one (potential) thread reserved for
// admin RPC commands; that way even if there are loads of basic commands being processed we'll
// still have room to invoke an admin command without waiting for the basic ones to finish.
constexpr unsigned int admin_reserved_threads = 1;
lmq.add_category("admin", AuthLevel::admin, admin_reserved_threads);
for (auto& cmd : rpc_commands) {
lmq.add_request_command(cmd.second->is_public ? "rpc" : "admin", cmd.first,
[name=string_view{cmd.first}, &call=*cmd.second, this](Message& m) {
if (m.data.size() > 1)
m.send_reply(LMQ_BAD_REQUEST, "Bad request: RPC commands must have at most one data part "
"(received " + std::to_string(m.data.size()) + ")");
rpc_request request;
request.context.admin = m.access.auth >= AuthLevel::admin;
request.context.source = rpc_source::lmq;
request.context.remote = m.remote;
request.body = m.data.empty() ? ""_sv : m.data[0];
try {
m.send_reply(LMQ_OK, call.invoke(std::move(request), rpc_));
} catch (const parse_error& e) {
// This isn't really WARNable as it's the client fault; log at info level instead.
//
// TODO: for various parsing errors there are still some stupid forced ERROR-level
// warnings that get generated deep inside epee, for example when passing a string or
// number instead of a JSON object. If you want to find some, `grep number2 epee` (for
// real).
MINFO("LMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' called with invalid/unparseable data: " << e.what());
m.send_reply(LMQ_BAD_REQUEST, "Unable to parse request: "s + e.what());
return;
} catch (const std::exception& e) {
MWARNING("LMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' "
"raised an exception: " << e.what());
} catch (...) {
MWARNING("LMQ RPC request '" << (call.is_public ? "rpc." : "admin.") << name << "' "
"raised an unknown exception");
}
// Don't include the exception message in case it contains something that we don't want go
// back to the user. If we want to support it eventually we could add some sort of
// `rpc::user_visible_exception` that carries a message to send back to the user.
m.send_reply(LMQ_ERROR, "An exception occured while processing your request");
});
}
// Subscription commands
// The "subscribe" category is for public subscriptions; i.e. anyone on a public RPC node, or
// anyone on a private RPC node with public access level.
lmq.add_category("sub", AuthLevel::basic);
// TX mempool subscriptions: [sub.mempool, blink] or [sub.mempool, all] to subscribe to new
// approved mempool blink txes, or to all new mempool txes. You get back a reply of "OK" or
// "ALREADY" -- the former indicates that you are newly subscribed for tx updates (either because
// you weren't subscribed before, or your subscription type changed); the latter indicates that
// you were already subscribed for the request tx types. Any other value should be considered an
// error.
//
// Subscriptions expire after 30 minutes. It is recommended that the client periodically
// re-subscribe on a much shorter interval than this (perhaps once per minute) and use "OK"
// replies as a indicator that there was some server-side interruption (such as a restart) that
// might necessitate the client rechecking the mempool.
//
// When a tx arrives the node sends back [notify.mempool, txhash, txblob] every time a new
// transaction is added to the mempool (minus some additions that aren't really new transactions
// such as txes that came from an existing block during a rollback). Note that both txhash and
// txblob are binary: in particular, txhash is *not* hex-encoded.
//
lmq.add_request_command("sub", "mempool", [this](Message& m) {
if (m.data.size() != 1) {
m.send_reply("Invalid subscription request: no subscription type given");
return;
}
mempool_sub_type sub_type;
if (m.data[0] == "blink"_sv)
sub_type = mempool_sub_type::blink;
else if (m.data[0] == "all"_sv)
sub_type = mempool_sub_type::all;
else {
m.send_reply("Invalid mempool subscription type '" + std::string{m.data[0]} + "'");
return;
}
{
auto lock = tools::unique_lock(subs_mutex_);
auto expiry = std::chrono::steady_clock::now() + 30min;
auto result = mempool_subs_.emplace(m.conn, mempool_sub{expiry, sub_type});
if (!result.second) {
result.first->second.expiry = expiry;
if (result.first->second.type == sub_type) {
MTRACE("Renewed mempool subscription request from conn id " << m.conn << " @ " << m.remote);
m.send_reply("ALREADY");
return;
}
result.first->second.type = sub_type;
}
MDEBUG("New " << (sub_type == mempool_sub_type::blink ? "blink" : "all") << " mempool subscription request from conn " << m.conn << " @ " << m.remote);
m.send_reply("OK");
}
});
// New block subscriptions: [sub.block]. This sends a notification every time a new block is
// added to the blockchain.
//
// TODO: make this support [sub.block, sn] so that we can receive notification only for blocks
// that change the SN composition.
//
// The subscription request returns the current [height, blockhash] as a reply.
//
// The block notification for new blocks consists of a message [notify.block, height, blockhash]
// containing the latest height/hash. (Note that blockhash is the hash in bytes, *not* the hex
// encoded block hash).
lmq.add_request_command("sub", "block", [this](Message& m) {
auto lock = tools::unique_lock(subs_mutex_);
auto expiry = std::chrono::steady_clock::now() + 30min;
auto result = block_subs_.emplace(m.conn, block_sub{expiry});
if (!result.second) {
result.first->second.expiry = expiry;
MTRACE("Renewed block subscription request from conn id " << m.conn << " @ " << m.remote);
m.send_reply("ALREADY");
} else {
MDEBUG("New block subscription request from conn " << m.conn << " @ " << m.remote);
m.send_reply("OK");
}
});
core_.get_blockchain_storage().hook_block_added(*this);
core_.get_pool().add_notify([this](const crypto::hash& id, const transaction& tx, const std::string& blob, const tx_pool_options& opts) {
send_mempool_notifications(id, tx, blob, opts);
});
}
template <typename Mutex, typename Subs, typename Call>
static void send_notifies(Mutex& mutex, Subs& subs, const char* desc, Call call) {
std::vector<ConnectionID> remove;
{
auto lock = tools::shared_lock(mutex);
if (subs.empty())
return;
auto now = std::chrono::steady_clock::now();
for (const auto& sub_pair : subs) {
auto& conn = sub_pair.first;
auto& sub = sub_pair.second;
if (sub.expiry < now) {
remove.push_back(conn);
continue;
} else {
call(conn, sub);
}
}
}
if (remove.empty())
return;
auto lock = tools::unique_lock(mutex);
auto now = std::chrono::steady_clock::now();
for (auto& conn : remove) {
auto it = subs.find(conn);
if (it != subs.end() && it->second.expiry < now /* recheck: client might have resubscribed in between locks */) {
MDEBUG("Removing " << conn << " from " << desc << " subscriptions: subscription timed out");
subs.erase(it);
}
}
}
bool lmq_rpc::block_added(const block& block, const std::vector<transaction>& txs, const checkpoint_t *)
{
auto& lmq = core_.get_lmq();
std::string height = std::to_string(get_block_height(block));
send_notifies(subs_mutex_, block_subs_, "block", [&](auto& conn, auto& sub) {
lmq.send(conn, "notify.block", height, string_view{block.hash.data, sizeof(block.hash.data)});
});
return true;
}
void lmq_rpc::send_mempool_notifications(const crypto::hash& id, const transaction& tx, const std::string& blob, const tx_pool_options& opts)
{
auto& lmq = core_.get_lmq();
send_notifies(subs_mutex_, mempool_subs_, "mempool", [&](auto& conn, auto& sub) {
if (sub.type == mempool_sub_type::all || opts.approved_blink)
lmq.send(conn, "notify.mempool", string_view{id.data, sizeof(id.data)}, blob);
});
}
}} // namespace cryptonote::rpc

73
src/rpc/lmq_server.h Normal file
View File

@ -0,0 +1,73 @@
// Copyright (c) 2020, The Loki 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 "core_rpc_server.h"
#include "cryptonote_core/blockchain.h"
#include "lokimq/connections.h"
namespace lokimq { class LokiMQ; }
namespace cryptonote { namespace rpc {
void init_lmq_options(boost::program_options::options_description& desc);
/**
* LMQ RPC server class. This doesn't actually hold the LokiMQ instance--that's in
* cryptonote_core--but it works with it to add RPC endpoints, make it listen on RPC ports, and
* handles RPC requests.
*/
class lmq_rpc : public cryptonote::BlockAddedHook {
enum class mempool_sub_type { all, blink };
struct mempool_sub {
std::chrono::steady_clock::time_point expiry;
mempool_sub_type type;
};
struct block_sub {
std::chrono::steady_clock::time_point expiry;
};
cryptonote::core& core_;
core_rpc_server& rpc_;
std::shared_timed_mutex subs_mutex_;
std::unordered_map<lokimq::ConnectionID, mempool_sub> mempool_subs_;
std::unordered_map<lokimq::ConnectionID, block_sub> block_subs_;
public:
lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::program_options::variables_map& vm);
bool block_added(const block& block, const std::vector<transaction>& txs, const checkpoint_t *) override;
void send_mempool_notifications(const crypto::hash& id, const transaction& tx, const std::string& blob, const tx_pool_options& opts);
};
}} // namespace cryptonote::rpc

View File

@ -104,6 +104,8 @@ namespace cryptonote
, rpc_ssl_allow_chained({"rpc-ssl-allow-chained", rpc_args::tr("Allow user (via --rpc-ssl-certificates) chain certificates"), false})
, rpc_ssl_allow_any_cert({"rpc-ssl-allow-any-cert", rpc_args::tr("Allow any peer certificate"), false})
, rpc_public_node({"public-node", rpc_args::tr("Allow other users to use the node as a remote (restricted RPC mode, view-only commands) and advertise it over P2P"), false})
, zmq_rpc_bind_ip({"zmq-rpc-bind-ip", rpc_args::tr("Deprecated option, ignored."), ""})
, zmq_rpc_bind_port({"zmq-rpc-bind-port", rpc_args::tr("Deprecated option, ignored."), ""})
{}
const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); }

View File

@ -66,6 +66,8 @@ namespace cryptonote
const command_line::arg_descriptor<bool> rpc_ssl_allow_chained;
const command_line::arg_descriptor<bool> rpc_ssl_allow_any_cert;
const command_line::arg_descriptor<bool> rpc_public_node;
const command_line::arg_descriptor<std::string> zmq_rpc_bind_ip; // Deprecated & ignored
const command_line::arg_descriptor<std::string> zmq_rpc_bind_port; // Deprecated & ignored
};
// `allow_any_cert` bool toggles `--rpc-ssl-allow-any-cert` configuration

View File

@ -34,7 +34,7 @@ target_link_libraries(simplewallet
PRIVATE
wallet
daemonizer
rpc_base
rpc_commands
cryptonote_core
mnemonics
epee_readline

View File

@ -102,7 +102,6 @@ extern "C"
#endif
using namespace cryptonote;
using boost::lexical_cast;
namespace po = boost::program_options;
namespace string_tools = epee::string_tools;
using sw = cryptonote::simple_wallet;
@ -121,9 +120,7 @@ using sw = cryptonote::simple_wallet;
m_wallet->stop(); \
boost::unique_lock<boost::mutex> lock(m_idle_mutex); \
m_idle_cond.notify_all(); \
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
})
LOKI_DEFER { m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); }
#define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \
LOCK_IDLE_SCOPE(); \
@ -366,11 +363,11 @@ namespace
std::string err;
if (ok)
{
if (status == CORE_RPC_STATUS_BUSY)
if (status == rpc::STATUS_BUSY)
{
err = sw::tr("daemon is busy. Please try again later.");
}
else if (status != CORE_RPC_STATUS_OK)
else if (status != rpc::STATUS_OK)
{
err = status;
}
@ -465,11 +462,6 @@ namespace
return "invalid";
}
std::string get_version_string(uint32_t version)
{
return boost::lexical_cast<std::string>(version >> 16) + "." + boost::lexical_cast<std::string>(version & 0xffff);
}
std::string oa_prompter(const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)
{
if (addresses.empty())
@ -1308,7 +1300,7 @@ bool simple_wallet::import_multisig_main(const std::vector<std::string> &args, b
try
{
m_in_manual_refresh.store(true, std::memory_order_relaxed);
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
LOKI_DEFER { m_in_manual_refresh.store(false, std::memory_order_relaxed); };
size_t n_outputs = m_wallet->import_multisig(info);
// Clear line "Height xxx of xxx"
std::cout << "\r \r";
@ -3352,9 +3344,7 @@ static bool datestr_to_int(const std::string &heightstr, uint16_t &year, uint8_t
//----------------------------------------------------------------------------------------------------
bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){
m_electrum_seed.wipe();
});
LOKI_DEFER { m_electrum_seed.wipe(); };
const bool testnet = tools::wallet2::has_testnet_option(vm);
const bool stagenet = tools::wallet2::has_stagenet_option(vm);
@ -3850,12 +3840,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
if (!m_wallet->explicit_refresh_from_block_height() && m_restoring)
{
uint32_t version;
rpc::version_t version;
bool connected = try_connect_to_daemon(false, &version);
while (true)
{
std::string heightstr;
if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
if (!connected || version < rpc::version_t{1, 6})
heightstr = input_line("Restore from specific blockchain height (optional, default 0)");
else
heightstr = input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD)");
@ -3873,7 +3863,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
catch (const boost::bad_lexical_cast &)
{
if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
if (!connected || version < rpc::version_t{1, 6})
{
fail_msg_writer() << tr("bad m_restore_height parameter: ") << heightstr;
continue;
@ -4008,9 +3998,9 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version)
bool simple_wallet::try_connect_to_daemon(bool silent, rpc::version_t* version)
{
uint32_t version_ = 0;
rpc::version_t version_{};
if (!version)
version = &version_;
if (!m_wallet->check_connection(version))
@ -4021,10 +4011,10 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version)
"Please make sure daemon is running or change the daemon address using the 'set_daemon' command.");
return false;
}
if (!m_allow_mismatched_daemon_version && ((*version >> 16) != CORE_RPC_VERSION_MAJOR))
if (!m_allow_mismatched_daemon_version && version->first != rpc::VERSION.first)
{
if (!silent)
fail_msg_writer() << boost::format(tr("Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version.")) % (*version>>16) % CORE_RPC_VERSION_MAJOR % m_wallet->get_daemon_address();
fail_msg_writer() << boost::format(tr("Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version.")) % version->first % rpc::VERSION.first % m_wallet->get_daemon_address();
return false;
}
return true;
@ -4522,7 +4512,7 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
fail_msg_writer() << tr("wallet is null");
return true;
}
COMMAND_RPC_START_MINING::request req{};
rpc::START_MINING::request req{};
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
bool ok = true;
@ -4545,7 +4535,7 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
return true;
}
COMMAND_RPC_START_MINING::response res;
rpc::START_MINING::response res{};
bool r = m_wallet->invoke_http_json("/start_mining", req, res);
std::string err = interpret_rpc_response(r, res.status);
if (err.empty())
@ -4566,8 +4556,8 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
return true;
}
COMMAND_RPC_STOP_MINING::request req;
COMMAND_RPC_STOP_MINING::response res;
rpc::STOP_MINING::request req{};
rpc::STOP_MINING::response res{};
bool r = m_wallet->invoke_http_json("/stop_mining", req, res);
std::string err = interpret_rpc_response(r, res.status);
if (err.empty())
@ -4650,8 +4640,8 @@ bool simple_wallet::save_bc(const std::vector<std::string>& args)
fail_msg_writer() << tr("wallet is null");
return true;
}
COMMAND_RPC_SAVE_BC::request req;
COMMAND_RPC_SAVE_BC::response res;
rpc::SAVE_BC::request req{};
rpc::SAVE_BC::response res{};
bool r = m_wallet->invoke_http_json("/save_bc", req, res);
std::string err = interpret_rpc_response(r, res.status);
if (err.empty())
@ -4844,7 +4834,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
try
{
m_in_manual_refresh.store(true, std::memory_order_relaxed);
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
LOKI_DEFER { m_in_manual_refresh.store(false, std::memory_order_relaxed); };
m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_money);
if (reset == ResetSoftKeepKI)
@ -5255,12 +5245,9 @@ std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::
//----------------------------------------------------------------------------------------------------
bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr)
{
uint32_t version;
rpc::version_t version;
if (!try_connect_to_daemon(false, &version))
return false;
// available for RPC version 1.4 or higher
if (version < MAKE_CORE_RPC_VERSION(1, 4))
return true;
std::string err;
uint64_t blockchain_height = get_daemon_blockchain_height(err);
if (!err.empty())
@ -5298,14 +5285,14 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
// convert relative offsets of ring member keys into absolute offsets (indices) associated with the amount
std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key.key_offsets);
// get block heights from which those ring member keys originated
COMMAND_RPC_GET_OUTPUTS_BIN::request req{};
rpc::GET_OUTPUTS_BIN::request req{};
req.outputs.resize(absolute_offsets.size());
for (size_t j = 0; j < absolute_offsets.size(); ++j)
{
req.outputs[j].amount = in_key.amount;
req.outputs[j].index = absolute_offsets[j];
}
COMMAND_RPC_GET_OUTPUTS_BIN::response res{};
rpc::GET_OUTPUTS_BIN::response res{};
bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res);
err = interpret_rpc_response(r, res.status);
if (!err.empty())
@ -6029,7 +6016,7 @@ bool simple_wallet::query_locked_stakes(bool print_result)
{
using namespace cryptonote;
boost::optional<std::string> failed;
const std::vector<COMMAND_RPC_GET_SERVICE_NODES::response::entry> response = m_wallet->get_all_service_nodes(failed);
const std::vector<rpc::GET_SERVICE_NODES::response::entry> response = m_wallet->get_all_service_nodes(failed);
if (failed)
{
fail_msg_writer() << *failed;
@ -6037,10 +6024,10 @@ bool simple_wallet::query_locked_stakes(bool print_result)
}
cryptonote::account_public_address const primary_address = m_wallet->get_address();
for (COMMAND_RPC_GET_SERVICE_NODES::response::entry const &node_info : response)
for (rpc::GET_SERVICE_NODES::response::entry const &node_info : response)
{
bool only_once = true;
for (service_node_contributor const &contributor : node_info.contributors)
for (const auto& contributor : node_info.contributors)
{
address_parse_info address_info = {};
if (!cryptonote::get_account_address_from_str(address_info, m_wallet->nettype(), contributor.address))
@ -6054,7 +6041,7 @@ bool simple_wallet::query_locked_stakes(bool print_result)
for (size_t i = 0; i < contributor.locked_contributions.size(); ++i)
{
service_node_contribution const &contribution = contributor.locked_contributions[i];
const auto& contribution = contributor.locked_contributions[i];
has_locked_stakes = true;
if (!print_result)
@ -6103,7 +6090,7 @@ bool simple_wallet::query_locked_stakes(bool print_result)
{
using namespace cryptonote;
boost::optional<std::string> failed;
const std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> response = m_wallet->get_service_node_blacklisted_key_images(failed);
const std::vector<rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> response = m_wallet->get_service_node_blacklisted_key_images(failed);
if (failed)
{
fail_msg_writer() << *failed;
@ -6115,7 +6102,7 @@ bool simple_wallet::query_locked_stakes(bool print_result)
binary_buf.reserve(sizeof(crypto::key_image));
for (size_t i = 0; i < response.size(); ++i)
{
COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry const &entry = response[i];
rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry const &entry = response[i];
binary_buf.clear();
if(!epee::string_tools::parse_hexstr_to_binbuff(entry.key_image, binary_buf) || binary_buf.size() != sizeof(crypto::key_image))
{
@ -6287,7 +6274,7 @@ bool simple_wallet::lns_update_mapping(const std::vector<std::string>& args)
SCOPED_WALLET_UNLOCK();
std::string reason;
std::vector<tools::wallet2::pending_tx> ptx_vector;
std::vector<cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::response_entry> response;
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> response;
try
{
@ -6450,14 +6437,14 @@ bool simple_wallet::lns_print_name_to_owners(const std::vector<std::string>& arg
}
std::string const &name = tools::lowercase_ascii_string(args[name_index]);
cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::request request = {};
rpc::LNS_NAMES_TO_OWNERS::request request = {};
request.entries.push_back({lns::name_to_base64_hash(name), std::move(requested_types)});
cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::request_entry &entry = request.entries.back();
rpc::LNS_NAMES_TO_OWNERS::request_entry &entry = request.entries.back();
if (entry.types.empty()) entry.types.push_back(static_cast<uint16_t>(lns::mapping_type::session));
boost::optional<std::string> failed;
std::vector<cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::response_entry> response = m_wallet->lns_names_to_owners(request, failed);
std::vector<rpc::LNS_NAMES_TO_OWNERS::response_entry> response = m_wallet->lns_names_to_owners(request, failed);
if (failed)
{
fail_msg_writer() << *failed;
@ -6497,14 +6484,14 @@ bool simple_wallet::lns_print_owners_to_names(const std::vector<std::string>& ar
return false;
boost::optional<std::string> failed;
std::vector<std::vector<cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::response_entry>> rpc_results;
std::vector<cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::request> requests(1);
std::vector<std::vector<cryptonote::rpc::LNS_OWNERS_TO_NAMES::response_entry>> rpc_results;
std::vector<cryptonote::rpc::LNS_OWNERS_TO_NAMES::request> requests(1);
if (args.size() == 0)
{
for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
{
if (requests.back().entries.size() >= cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES)
if (requests.back().entries.size() >= cryptonote::rpc::LNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES)
requests.emplace_back();
requests.back().entries.push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account, index}));
}
@ -6525,7 +6512,7 @@ bool simple_wallet::lns_print_owners_to_names(const std::vector<std::string>& ar
return false;
}
if (requests.back().entries.size() >= cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES)
if (requests.back().entries.size() >= cryptonote::rpc::LNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES)
requests.emplace_back();
requests.back().entries.push_back(arg);
}
@ -6534,7 +6521,7 @@ bool simple_wallet::lns_print_owners_to_names(const std::vector<std::string>& ar
rpc_results.reserve(requests.size());
for (auto const &request : requests)
{
std::vector<cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::response_entry> result = m_wallet->lns_owners_to_names(request, failed);
std::vector<cryptonote::rpc::LNS_OWNERS_TO_NAMES::response_entry> result = m_wallet->lns_owners_to_names(request, failed);
if (failed)
{
fail_msg_writer() << *failed;
@ -8984,7 +8971,7 @@ bool simple_wallet::get_description(const std::vector<std::string> &args)
bool simple_wallet::status(const std::vector<std::string> &args)
{
uint64_t local_height = m_wallet->get_blockchain_current_height();
uint32_t version = 0;
rpc::version_t version;
bool ssl = false;
if (!m_wallet->check_connection(&version, &ssl))
{
@ -8998,7 +8985,7 @@ bool simple_wallet::status(const std::vector<std::string> &args)
{
bool synced = local_height == bc_height;
success_msg_writer() << "Refreshed " << local_height << "/" << bc_height << ", " << (synced ? "synced" : "syncing")
<< ", daemon RPC v" << get_version_string(version) << ", " << (ssl ? "SSL" : "no SSL");
<< ", daemon RPC v" << version.first << '.' << version.second << ", " << (ssl ? "SSL" : "no SSL");
}
else
{

View File

@ -279,7 +279,7 @@ namespace cryptonote
bool cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> const &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func);
uint64_t get_daemon_blockchain_height(std::string& err);
bool try_connect_to_daemon(bool silent = false, uint32_t* version = nullptr);
bool try_connect_to_daemon(bool silent = false, rpc::version_t* version = nullptr);
bool ask_wallet_create_if_needed();
bool accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message = std::string());
bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs);

View File

@ -39,6 +39,8 @@ add_library(wallet
target_link_libraries(wallet
PUBLIC
multisig
rpc_commands
rpc_args
cryptonote_core
mnemonics
device_trezor
@ -59,7 +61,6 @@ loki_add_executable(wallet_rpc_server "loki-wallet-rpc"
target_link_libraries(wallet_rpc_server
PRIVATE
wallet
rpc_base
daemonizer
epee_readline
Boost::program_options

View File

@ -982,7 +982,7 @@ bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_
{
try
{
tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response response;
tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response response{};
if(!m_wallet->light_wallet_import_wallet_request(response)){
setStatusError(tr("Failed to send import wallet request"));
return false;
@ -2042,12 +2042,12 @@ bool WalletImpl::connectToDaemon()
Wallet::ConnectionStatus WalletImpl::connected() const
{
uint32_t version = 0;
rpc::version_t version;
m_is_connected = m_wallet->check_connection(&version, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS);
if (!m_is_connected)
return Wallet::ConnectionStatus_Disconnected;
// Version check is not implemented in light wallets nodes/wallets
if (!m_wallet->light_wallet() && (version >> 16) != CORE_RPC_VERSION_MAJOR)
if (!m_wallet->light_wallet() && version.first != rpc::VERSION.first)
return Wallet::ConnectionStatus_WrongVersion;
return Wallet::ConnectionStatus_Connected;
}

View File

@ -233,8 +233,8 @@ void WalletManagerImpl::setDaemonAddress(const std::string &address)
bool WalletManagerImpl::connected(uint32_t *version)
{
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_VERSION::request> req_t{};
epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_VERSION::response, std::string> resp_t{};
epee::json_rpc::request<cryptonote::rpc::GET_VERSION::request> req_t{};
epee::json_rpc::response<cryptonote::rpc::GET_VERSION::response, std::string> resp_t{};
req_t.jsonrpc = "2.0";
req_t.id = epee::serialization::storage_entry(0);
req_t.method = "get_version";
@ -248,8 +248,8 @@ bool WalletManagerImpl::connected(uint32_t *version)
uint64_t WalletManagerImpl::blockchainHeight()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
cryptonote::rpc::GET_INFO::request ireq{};
cryptonote::rpc::GET_INFO::response ires{};
if (!epee::net_utils::invoke_http_json("/getinfo", ireq, ires, m_http_client))
return 0;
@ -258,8 +258,8 @@ uint64_t WalletManagerImpl::blockchainHeight()
uint64_t WalletManagerImpl::blockchainTargetHeight()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
cryptonote::rpc::GET_INFO::request ireq{};
cryptonote::rpc::GET_INFO::response ires{};
if (!epee::net_utils::invoke_http_json("/getinfo", ireq, ires, m_http_client))
return 0;
@ -268,8 +268,8 @@ uint64_t WalletManagerImpl::blockchainTargetHeight()
uint64_t WalletManagerImpl::networkDifficulty()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
cryptonote::rpc::GET_INFO::request ireq{};
cryptonote::rpc::GET_INFO::response ires{};
if (!epee::net_utils::invoke_http_json("/getinfo", ireq, ires, m_http_client))
return 0;
@ -278,8 +278,8 @@ uint64_t WalletManagerImpl::networkDifficulty()
double WalletManagerImpl::miningHashRate()
{
cryptonote::COMMAND_RPC_MINING_STATUS::request mreq;
cryptonote::COMMAND_RPC_MINING_STATUS::response mres;
cryptonote::rpc::MINING_STATUS::request mreq{};
cryptonote::rpc::MINING_STATUS::response mres{};
if (!epee::net_utils::invoke_http_json("/mining_status", mreq, mres, m_http_client))
return 0.0;
@ -290,8 +290,8 @@ double WalletManagerImpl::miningHashRate()
uint64_t WalletManagerImpl::blockTarget()
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
cryptonote::rpc::GET_INFO::request ireq{};
cryptonote::rpc::GET_INFO::response ires{};
if (!epee::net_utils::invoke_http_json("/getinfo", ireq, ires, m_http_client))
return 0;
@ -300,8 +300,8 @@ uint64_t WalletManagerImpl::blockTarget()
bool WalletManagerImpl::isMining()
{
cryptonote::COMMAND_RPC_MINING_STATUS::request mreq;
cryptonote::COMMAND_RPC_MINING_STATUS::response mres;
cryptonote::rpc::MINING_STATUS::request mreq{};
cryptonote::rpc::MINING_STATUS::response mres{};
if (!epee::net_utils::invoke_http_json("/mining_status", mreq, mres, m_http_client))
return false;
@ -310,25 +310,25 @@ bool WalletManagerImpl::isMining()
bool WalletManagerImpl::startMining(const std::string &address, uint32_t threads)
{
cryptonote::COMMAND_RPC_START_MINING::request mreq;
cryptonote::COMMAND_RPC_START_MINING::response mres;
cryptonote::rpc::START_MINING::request mreq{};
cryptonote::rpc::START_MINING::response mres{};
mreq.miner_address = address;
mreq.threads_count = threads;
if (!epee::net_utils::invoke_http_json("/start_mining", mreq, mres, m_http_client))
return false;
return mres.status == CORE_RPC_STATUS_OK;
return mres.status == cryptonote::rpc::STATUS_OK;
}
bool WalletManagerImpl::stopMining()
{
cryptonote::COMMAND_RPC_STOP_MINING::request mreq;
cryptonote::COMMAND_RPC_STOP_MINING::response mres;
cryptonote::rpc::STOP_MINING::request mreq{};
cryptonote::rpc::STOP_MINING::response mres{};
if (!epee::net_utils::invoke_http_json("/stop_mining", mreq, mres, m_http_client))
return false;
return mres.status == CORE_RPC_STATUS_OK;
return mres.status == cryptonote::rpc::STATUS_OK;
}
std::string WalletManagerImpl::resolveOpenAlias(const std::string &address, bool &dnssec_valid) const

View File

@ -1204,7 +1204,7 @@ void message_store::send_message(const multisig_wallet_state &state, uint32_t id
message &m = get_message_ref_by_id(id);
const authorized_signer &me = m_signers[0];
const authorized_signer &receiver = m_signers[m.signer_index];
transport_message dm;
transport_message dm{};
crypto::public_key public_key;
dm.timestamp = (uint64_t)time(NULL);

View File

@ -44,7 +44,7 @@ namespace mms
namespace bitmessage_rpc
{
struct message_info_t
struct message_info
{
uint32_t encodingType;
std::string toAddress;
@ -66,9 +66,8 @@ namespace bitmessage_rpc
KV_SERIALIZE(subject)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<message_info_t> message_info;
struct inbox_messages_response_t
struct inbox_messages_response
{
std::vector<message_info> inboxMessages;
@ -76,7 +75,6 @@ namespace bitmessage_rpc
KV_SERIALIZE(inboxMessages)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<inbox_messages_response_t> inbox_messages_response;
}
@ -136,7 +134,7 @@ bool message_transporter::receive_messages(const std::vector<std::string> &desti
const bitmessage_rpc::message_info &message_info = bitmessage_res.inboxMessages[i];
if (std::find(destination_transport_addresses.begin(), destination_transport_addresses.end(), message_info.toAddress) != destination_transport_addresses.end())
{
transport_message message;
transport_message message{};
bool is_mms_message = false;
try
{

View File

@ -42,7 +42,7 @@
namespace mms
{
struct transport_message_t
struct transport_message
{
cryptonote::account_public_address source_monero_address;
std::string source_transport_address;
@ -78,7 +78,6 @@ struct transport_message_t
KV_SERIALIZE(transport_id)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<transport_message_t> transport_message;
class message_transporter
{

View File

@ -30,6 +30,8 @@
#include "storages/http_abstract_invoke.h"
#include <boost/thread.hpp>
namespace rpc = cryptonote::rpc;
namespace tools
{
@ -63,27 +65,27 @@ void NodeRPCProxy::invalidate()
m_dynamic_base_fee_estimate_cached_height = 0;
m_dynamic_base_fee_estimate_grace_blocks = 0;
m_fee_quantization_mask = 1;
m_rpc_version = 0;
m_rpc_version = {0, 0};
m_target_height = 0;
m_block_weight_limit = 0;
m_get_info_time = 0;
}
boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const
boost::optional<std::string> NodeRPCProxy::get_rpc_version(rpc::version_t &rpc_version) const
{
if (m_offline)
return boost::optional<std::string>("offline");
if (m_rpc_version == 0)
if (m_rpc_version == rpc::version_t{0, 0})
{
cryptonote::COMMAND_RPC_GET_VERSION::request req_t{};
cryptonote::COMMAND_RPC_GET_VERSION::response resp_t{};
cryptonote::rpc::GET_VERSION::request req_t{};
cryptonote::rpc::GET_VERSION::response resp_t{};
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get daemon RPC version");
m_rpc_version = resp_t.version;
CHECK_AND_ASSERT_MES(resp_t.status != rpc::STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == rpc::STATUS_OK, resp_t.status, "Failed to get daemon RPC version");
m_rpc_version = rpc::make_version(resp_t.version);
}
rpc_version = m_rpc_version;
return boost::optional<std::string>();
@ -103,16 +105,16 @@ boost::optional<std::string> NodeRPCProxy::get_info() const
const time_t now = time(NULL);
if (now >= m_get_info_time + 30) // re-cache every 30 seconds
{
cryptonote::COMMAND_RPC_GET_INFO::request req_t{};
cryptonote::COMMAND_RPC_GET_INFO::response resp_t{};
cryptonote::rpc::GET_INFO::request req_t{};
cryptonote::rpc::GET_INFO::response resp_t{};
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height");
CHECK_AND_ASSERT_MES(resp_t.status != rpc::STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == rpc::STATUS_OK, resp_t.status, "Failed to get target blockchain height");
m_height = resp_t.height;
m_target_height = resp_t.target_height;
m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit;
@ -164,16 +166,16 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
return boost::optional<std::string>("offline");
if (m_earliest_height[version] == 0)
{
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req_t{};
cryptonote::COMMAND_RPC_HARD_FORK_INFO::response resp_t{};
cryptonote::rpc::HARD_FORK_INFO::request req_t{};
cryptonote::rpc::HARD_FORK_INFO::response resp_t{};
m_daemon_rpc_mutex.lock();
req_t.version = version;
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get hard fork status");
CHECK_AND_ASSERT_MES(resp_t.status != rpc::STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == rpc::STATUS_OK, resp_t.status, "Failed to get hard fork status");
m_earliest_height[version] = resp_t.earliest_height;
}
@ -186,15 +188,15 @@ boost::optional<uint8_t> NodeRPCProxy::get_hardfork_version() const
if (m_offline)
return boost::none;
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req{};
cryptonote::COMMAND_RPC_HARD_FORK_INFO::response resp{};
cryptonote::rpc::HARD_FORK_INFO::request req{};
cryptonote::rpc::HARD_FORK_INFO::response resp{};
m_daemon_rpc_mutex.lock();
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req, resp, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, {}, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp.status != CORE_RPC_STATUS_BUSY, {}, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp.status == CORE_RPC_STATUS_OK, {}, "Failed to get hard fork status");
CHECK_AND_ASSERT_MES(resp.status != rpc::STATUS_BUSY, {}, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp.status == rpc::STATUS_OK, {}, "Failed to get hard fork status");
return resp.version;
}
@ -211,16 +213,16 @@ boost::optional<std::string> NodeRPCProxy::refresh_dynamic_base_fee_cache(uint64
if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks)
{
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t{};
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t{};
cryptonote::rpc::GET_BASE_FEE_ESTIMATE::request req_t{};
cryptonote::rpc::GET_BASE_FEE_ESTIMATE::response resp_t{};
m_daemon_rpc_mutex.lock();
req_t.grace_blocks = grace_blocks;
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
CHECK_AND_ASSERT_MES(resp_t.status != rpc::STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == rpc::STATUS_OK, resp_t.status, "Failed to get fee estimate");
m_dynamic_base_fee_estimate = {resp_t.fee_per_byte, resp_t.fee_per_output};
m_dynamic_base_fee_estimate_cached_height = height;
m_dynamic_base_fee_estimate_grace_blocks = grace_blocks;
@ -260,7 +262,7 @@ static bool check_invoke(bool r, T &response, boost::optional<std::string> &fail
return false;
}
if (response.status != CORE_RPC_STATUS_OK)
if (response.status != rpc::STATUS_OK)
{
failed = response.status;
return false;
@ -269,17 +271,17 @@ static bool check_invoke(bool r, T &response, boost::optional<std::string> &fail
return true;
}
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> NodeRPCProxy::get_service_nodes(std::vector<std::string> const &pubkeys, boost::optional<std::string> &failed) const
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> NodeRPCProxy::get_service_nodes(std::vector<std::string> const &pubkeys, boost::optional<std::string> &failed) const
{
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> result;
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> result;
if (m_offline)
{
failed = std::string("offline");
return result;
}
cryptonote::COMMAND_RPC_GET_SERVICE_NODES::request req = {};
cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response res = {};
cryptonote::rpc::GET_SERVICE_NODES::request req = {};
cryptonote::rpc::GET_SERVICE_NODES::response res = {};
req.service_node_pubkeys = pubkeys;
m_daemon_rpc_mutex.lock();
@ -300,8 +302,8 @@ bool NodeRPCProxy::update_all_service_nodes_cache(uint64_t height, boost::option
return false;
}
cryptonote::COMMAND_RPC_GET_SERVICE_NODES::request req = {};
cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response res = {};
cryptonote::rpc::GET_SERVICE_NODES::request req = {};
cryptonote::rpc::GET_SERVICE_NODES::response res = {};
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "get_all_service_nodes", req, res, m_http_client, rpc_timeout);
if (!check_invoke(r, res, failed))
@ -313,9 +315,9 @@ bool NodeRPCProxy::update_all_service_nodes_cache(uint64_t height, boost::option
}
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> NodeRPCProxy::get_all_service_nodes(boost::optional<std::string> &failed) const
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> NodeRPCProxy::get_all_service_nodes(boost::optional<std::string> &failed) const
{
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> result;
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> result{};
uint64_t height;
failed = get_height(height);
@ -335,9 +337,9 @@ std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> NodeRPCP
// Filtered version of the above that caches the filtered result as long as used on the same
// contributor at the same height (which is very common, for example, for wallet balance lookups).
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> NodeRPCProxy::get_contributed_service_nodes(const std::string &contributor, boost::optional<std::string> &failed) const
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> NodeRPCProxy::get_contributed_service_nodes(const std::string &contributor, boost::optional<std::string> &failed) const
{
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> result;
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> result{};
uint64_t height;
failed = get_height(height);
@ -353,10 +355,10 @@ std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> NodeRPCP
m_contributed_service_nodes.clear();
std::copy_if(m_all_service_nodes.begin(), m_all_service_nodes.end(), std::back_inserter(m_contributed_service_nodes),
[&contributor](const cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry &e)
[&contributor](const cryptonote::rpc::GET_SERVICE_NODES::response::entry &e)
{
return std::any_of(e.contributors.begin(), e.contributors.end(),
[&contributor](const cryptonote::service_node_contributor &c) { return contributor == c.address; });
[&contributor](const cryptonote::rpc::service_node_contributor &c) { return contributor == c.address; });
}
);
m_contributed_service_nodes_cached_height = height;
@ -369,9 +371,9 @@ std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> NodeRPCP
return result;
}
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> NodeRPCProxy::get_service_node_blacklisted_key_images(boost::optional<std::string> &failed) const
std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> NodeRPCProxy::get_service_node_blacklisted_key_images(boost::optional<std::string> &failed) const
{
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> result;
std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> result{};
if (m_offline)
{
failed = std::string("offline");
@ -387,8 +389,8 @@ std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::ent
std::lock_guard<std::recursive_mutex> lock(m_daemon_rpc_mutex);
if (m_service_node_blacklisted_key_images_cached_height != height)
{
cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::request req = {};
cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::response res = {};
cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::request req = {};
cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::response res = {};
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "get_service_node_blacklisted_key_images", req, res, m_http_client, rpc_timeout);
if (!check_invoke(r, res, failed))
@ -404,7 +406,7 @@ std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::ent
return result;
}
std::vector<cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::response_entry> NodeRPCProxy::lns_owners_to_names(cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::request const &request, boost::optional<std::string> &failed) const
std::vector<cryptonote::rpc::LNS_OWNERS_TO_NAMES::response_entry> NodeRPCProxy::lns_owners_to_names(cryptonote::rpc::LNS_OWNERS_TO_NAMES::request const &request, boost::optional<std::string> &failed) const
{
if (m_offline)
{
@ -412,7 +414,7 @@ std::vector<cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::response_entry> NodeRPC
return {};
}
cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::response res = {};
cryptonote::rpc::LNS_OWNERS_TO_NAMES::response res = {};
{
std::lock_guard<std::recursive_mutex> lock(m_daemon_rpc_mutex);
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "lns_owners_to_names", request, res, m_http_client, rpc_timeout);
@ -423,7 +425,7 @@ std::vector<cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::response_entry> NodeRPC
return res.entries;
}
std::vector<cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::response_entry> NodeRPCProxy::lns_names_to_owners(cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::request const &request, boost::optional<std::string> &failed) const
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> NodeRPCProxy::lns_names_to_owners(cryptonote::rpc::LNS_NAMES_TO_OWNERS::request const &request, boost::optional<std::string> &failed) const
{
if (m_offline)
{
@ -431,7 +433,7 @@ std::vector<cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::response_entry> NodeRPC
return {};
}
cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::response res = {};
cryptonote::rpc::LNS_NAMES_TO_OWNERS::response res = {};
{
std::lock_guard<std::recursive_mutex> lock(m_daemon_rpc_mutex);
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "lns_names_to_owners", request, res, m_http_client, rpc_timeout);

View File

@ -45,7 +45,7 @@ public:
void invalidate();
void set_offline(bool offline) { m_offline = offline; }
boost::optional<std::string> get_rpc_version(uint32_t &version) const;
boost::optional<std::string> get_rpc_version(cryptonote::rpc::version_t &version) const;
boost::optional<std::string> get_height(uint64_t &height) const;
void set_height(uint64_t h);
boost::optional<std::string> get_target_height(uint64_t &height) const;
@ -56,12 +56,12 @@ public:
boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask) const;
boost::optional<uint8_t> get_hardfork_version() const;
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> get_service_nodes(std::vector<std::string> const &pubkeys, boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> get_all_service_nodes(boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> get_contributed_service_nodes(const std::string &contributor, boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> get_service_node_blacklisted_key_images(boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::response_entry> lns_owners_to_names(cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::request const &request, boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::response_entry> lns_names_to_owners(cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::request const &request, boost::optional<std::string> &failed) const;
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> get_service_nodes(std::vector<std::string> const &pubkeys, boost::optional<std::string> &failed) const;
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> get_all_service_nodes(boost::optional<std::string> &failed) const;
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> get_contributed_service_nodes(const std::string &contributor, boost::optional<std::string> &failed) const;
std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> get_service_node_blacklisted_key_images(boost::optional<std::string> &failed) const;
std::vector<cryptonote::rpc::LNS_OWNERS_TO_NAMES::response_entry> lns_owners_to_names(cryptonote::rpc::LNS_OWNERS_TO_NAMES::request const &request, boost::optional<std::string> &failed) const;
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> lns_names_to_owners(cryptonote::rpc::LNS_NAMES_TO_OWNERS::request const &request, boost::optional<std::string> &failed) const;
private:
boost::optional<std::string> get_info() const;
@ -71,16 +71,16 @@ private:
bool m_offline;
mutable uint64_t m_service_node_blacklisted_key_images_cached_height;
mutable std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> m_service_node_blacklisted_key_images;
mutable std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> m_service_node_blacklisted_key_images;
bool update_all_service_nodes_cache(uint64_t height, boost::optional<std::string> &failed) const;
mutable uint64_t m_all_service_nodes_cached_height;
mutable std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> m_all_service_nodes;
mutable std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> m_all_service_nodes;
mutable uint64_t m_contributed_service_nodes_cached_height;
mutable std::string m_contributed_service_nodes_cached_address;
mutable std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> m_contributed_service_nodes;
mutable std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> m_contributed_service_nodes;
mutable uint64_t m_height;
mutable uint64_t m_immutable_height;
@ -90,7 +90,7 @@ private:
mutable uint64_t m_dynamic_base_fee_estimate_grace_blocks;
mutable uint64_t m_fee_quantization_mask;
boost::optional<std::string> refresh_dynamic_base_fee_cache(uint64_t grace_blocks) const;
mutable uint32_t m_rpc_version;
mutable cryptonote::rpc::version_t m_rpc_version;
mutable uint64_t m_target_height;
mutable uint64_t m_block_weight_limit;
mutable time_t m_get_info_time;

View File

@ -219,7 +219,7 @@ ringdb::ringdb(std::string filename, const std::string &genesis):
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
dbr = mdb_dbi_open(txn, ("rings-" + genesis).c_str(), MDB_CREATE, &dbi_rings);
@ -261,7 +261,7 @@ bool ringdb::add_rings(const crypto::chacha_key &chacha_key, const cryptonote::t
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size");
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
for (const auto &in: tx.vin)
@ -292,7 +292,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vecto
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size");
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
for (const crypto::key_image &key_image: key_images)
@ -346,7 +346,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
MDB_val key, data;
@ -382,7 +382,7 @@ bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
store_relative_ring(txn, dbi_rings, key_image, relative ? outs : cryptonote::absolute_output_offsets_to_relative(outs), chacha_key);
@ -407,7 +407,7 @@ bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
LOKI_DEFER { if (tx_active) mdb_txn_abort(txn); };
tx_active = true;
dbr = mdb_cursor_open(txn, dbi_blackballs, &cursor);

File diff suppressed because it is too large Load Diff

View File

@ -695,7 +695,7 @@ private:
crypto::hash hash;
cryptonote::block block;
std::vector<cryptonote::transaction> txes;
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices o_indices;
cryptonote::rpc::GET_BLOCKS_FAST::block_output_indices o_indices;
bool error;
};
@ -999,7 +999,7 @@ private:
std::vector<pending_tx> create_unmixable_sweep_transactions();
void discard_unmixable_outputs();
bool is_connected() const;
bool check_connection(uint32_t *version = NULL, bool *ssl = NULL, uint32_t timeout = 200000);
bool check_connection(cryptonote::rpc::version_t *version = NULL, bool *ssl = NULL, uint32_t timeout = 200000);
transfer_view make_transfer_view(const crypto::hash &txid, const crypto::hash &payment_id, const wallet2::payment_details &pd) const;
transfer_view make_transfer_view(const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd) const;
transfer_view make_transfer_view(const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd) const;
@ -1032,11 +1032,11 @@ private:
void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
// NOTE(loki): get_all_service_node caches the result, get_service_nodes doesn't
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> get_all_service_nodes(boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_all_service_nodes(failed); }
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> get_service_nodes (std::vector<std::string> const &pubkeys, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_service_nodes(pubkeys, failed); }
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> get_service_node_blacklisted_key_images(boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_service_node_blacklisted_key_images(failed); }
std::vector<cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::response_entry> lns_owners_to_names(cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::request const &request, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.lns_owners_to_names(request, failed); }
std::vector<cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::response_entry> lns_names_to_owners(cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::request const &request, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.lns_names_to_owners(request, failed); }
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> get_all_service_nodes(boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_all_service_nodes(failed); }
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> get_service_nodes (std::vector<std::string> const &pubkeys, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_service_nodes(pubkeys, failed); }
std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> get_service_node_blacklisted_key_images(boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_service_node_blacklisted_key_images(failed); }
std::vector<cryptonote::rpc::LNS_OWNERS_TO_NAMES::response_entry> lns_owners_to_names(cryptonote::rpc::LNS_OWNERS_TO_NAMES::request const &request, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.lns_owners_to_names(request, failed); }
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> lns_names_to_owners(cryptonote::rpc::LNS_NAMES_TO_OWNERS::request const &request, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.lns_names_to_owners(request, failed); }
uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); }
void rescan_spent();
@ -1553,8 +1553,8 @@ private:
// signature: (Optional) If set, use the signature given, otherwise by default derive the signature from the wallet spend key as an ed25519 key.
// The signature is derived from the hash of the previous txid blob and previous value blob of the mapping. By default this is signed using the wallet's spend key as an ed25519 keypair.
std::vector<wallet2::pending_tx> lns_create_update_mapping_tx(lns::mapping_type type, std::string name, std::string const *value, std::string const *owner, std::string const *backup_owner, std::string const *signature, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {}, std::vector<cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::response_entry> *response = {});
std::vector<wallet2::pending_tx> lns_create_update_mapping_tx(std::string const &type, std::string const &name, std::string const *value, std::string const *owner, std::string const *backup_owner, std::string const *signature, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {}, std::vector<cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::response_entry> *response = {});
std::vector<wallet2::pending_tx> lns_create_update_mapping_tx(lns::mapping_type type, std::string name, std::string const *value, std::string const *owner, std::string const *backup_owner, std::string const *signature, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {}, std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> *response = {});
std::vector<wallet2::pending_tx> lns_create_update_mapping_tx(std::string const &type, std::string const &name, std::string const *value, std::string const *owner, std::string const *backup_owner, std::string const *signature, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {}, std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> *response = {});
// Generate just the signature required for putting into lns_update_mapping command in the wallet
bool lns_make_update_mapping_signature(lns::mapping_type type, std::string name, std::string const *value, std::string const *owner, std::string const *backup_owner, lns::generic_signature &signature, uint32_t account_index = 0, std::string *reason = nullptr);
@ -1613,7 +1613,7 @@ private:
void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
bool clear();
void clear_soft(bool keep_key_images=false);
void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices);
void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::rpc::GET_BLOCKS_FAST::block_output_indices> &o_indices);
void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes);
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false);
void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error);

View File

@ -39,7 +39,7 @@ namespace tools
// TODO: Undocumented light wallet RPC call
struct COMMAND_RPC_GET_ADDRESS_TXS
{
struct request_t
struct request
{
std::string address; // Address of wallet to receive tx information.
std::string view_key; // View key of Address.
@ -49,7 +49,6 @@ namespace tools
KV_SERIALIZE(view_key)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct spent_output
{
@ -102,7 +101,7 @@ namespace tools
};
struct response_t
struct response
{
//std::list<std::string> txs_as_json;
uint64_t total_received; // Total Loki received in atomic units.
@ -123,7 +122,6 @@ namespace tools
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
@ -131,7 +129,7 @@ namespace tools
// TODO: Undocumented light wallet RPC call
struct COMMAND_RPC_GET_ADDRESS_INFO
{
struct request_t
struct request
{
std::string address;
std::string view_key;
@ -141,7 +139,6 @@ namespace tools
KV_SERIALIZE(view_key)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct spent_output
{
@ -160,7 +157,7 @@ namespace tools
END_KV_SERIALIZE_MAP()
};
struct response_t
struct response
{
uint64_t locked_funds;
uint64_t total_received;
@ -184,7 +181,6 @@ namespace tools
KV_SERIALIZE(spent_outputs)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
@ -192,7 +188,7 @@ namespace tools
// TODO: Undocumented light wallet RPC call
struct COMMAND_RPC_GET_UNSPENT_OUTS
{
struct request_t
struct request
{
std::string amount;
std::string address;
@ -211,7 +207,6 @@ namespace tools
KV_SERIALIZE(dust_threshold)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct output {
@ -242,7 +237,7 @@ namespace tools
END_KV_SERIALIZE_MAP()
};
struct response_t
struct response
{
uint64_t amount;
std::list<output> outputs;
@ -258,7 +253,6 @@ namespace tools
KV_SERIALIZE(reason)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
@ -266,7 +260,7 @@ namespace tools
LOKI_RPC_DOC_INTROSPECT
struct COMMAND_RPC_LOGIN
{
struct request_t
struct request
{
std::string address;
std::string view_key;
@ -278,9 +272,8 @@ namespace tools
KV_SERIALIZE(create_account)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
struct response
{
std::string status;
std::string reason;
@ -292,13 +285,12 @@ namespace tools
KV_SERIALIZE(new_address)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
LOKI_RPC_DOC_INTROSPECT
struct COMMAND_RPC_IMPORT_WALLET_REQUEST
{
struct request_t
struct request
{
std::string address;
std::string view_key;
@ -308,9 +300,8 @@ namespace tools
KV_SERIALIZE(view_key)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
struct response
{
std::string payment_id;
uint64_t import_fee;
@ -328,7 +319,6 @@ namespace tools
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
}

View File

@ -61,6 +61,8 @@
#define DEFAULT_AUTO_REFRESH_PERIOD 20 // seconds
namespace rpc = cryptonote::rpc;
namespace
{
const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
@ -2816,13 +2818,13 @@ namespace tools
return false;
}
cryptonote::COMMAND_RPC_START_MINING::request daemon_req{};
rpc::START_MINING::request daemon_req{};
daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
daemon_req.threads_count = req.threads_count;
cryptonote::COMMAND_RPC_START_MINING::response daemon_res;
rpc::START_MINING::response daemon_res{};
bool r = m_wallet->invoke_http_json("/start_mining", daemon_req, daemon_res);
if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
if (!r || daemon_res.status != rpc::STATUS_OK)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Couldn't start mining due to unknown error.";
@ -2834,10 +2836,10 @@ namespace tools
bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req;
cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res;
rpc::STOP_MINING::request daemon_req{};
rpc::STOP_MINING::response daemon_res{};
bool r = m_wallet->invoke_http_json("/stop_mining", daemon_req, daemon_res);
if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
if (!r || daemon_res.status != rpc::STATUS_OK)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Couldn't stop mining due to unknown error.";
@ -2917,8 +2919,8 @@ namespace tools
return false;
}
wal->set_seed_language(req.language);
cryptonote::COMMAND_RPC_GET_HEIGHT::request hreq;
cryptonote::COMMAND_RPC_GET_HEIGHT::response hres;
rpc::GET_HEIGHT::request hreq{};
rpc::GET_HEIGHT::response hres{};
hres.height = 0;
bool r = wal->invoke_http_json("/getheight", hreq, hres);
if (r)

File diff suppressed because it is too large Load Diff

View File

@ -68,10 +68,10 @@ TEST(Transfers, Transfers)
receiver.store("receiver.b2wallet");
{
COMMAND_RPC_START_MINE::request req;
rpc::START_MINE::request req;
req.miner_address = miner.get_account().get_public_address_str(false);
req.threads_count = 1;
COMMAND_RPC_START_MINE::response res;
rpc::START_MINE::response res;
bool r = net_utils::http::invoke_http_json_remote_command(daemon_address + "/start_mine", req, res, http_client);
ASSERT_TRUE(r);
}

View File

@ -166,18 +166,18 @@ bool transactions_flow_test(std::string& working_folder,
//lets do some money
epee::net_utils::http::http_simple_client http_client;
COMMAND_RPC_STOP_MINING::request daemon1_req{};
COMMAND_RPC_STOP_MINING::response daemon1_rsp{};
rpc::STOP_MINING::request daemon1_req{};
rpc::STOP_MINING::response daemon1_rsp{};
bool r = http_client.set_server(daemon_addr_a, boost::none) && net_utils::invoke_http_json("/stop_mine", daemon1_req, daemon1_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to stop mining");
COMMAND_RPC_START_MINING::request daemon_req{};
COMMAND_RPC_START_MINING::response daemon_rsp{};
rpc::START_MINING::request daemon_req{};
rpc::START_MINING::response daemon_rsp{};
daemon_req.miner_address = w1.get_account().get_public_address_str(MAINNET);
daemon_req.threads_count = 9;
r = net_utils::invoke_http_json("/start_mining", daemon_req, daemon_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
CHECK_AND_ASSERT_MES(daemon_rsp.status == rpc::STATUS_OK, false, "failed to start mining");
//wait for money, until balance will have enough money
w1.refresh(true, blocks_fetched, received_money, ok);

View File

@ -135,12 +135,12 @@ bool make_tx(blockchain_storage& bch)
std::cout << "transaction construction failed" << std::endl;
}
COMMAND_RPC_SEND_RAW_TX::request req;
rpc::SEND_RAW_TX::request req;
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(tx));
COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
rpc::SEND_RAW_TX::response daemon_send_resp;
r = net_utils::http::invoke_http_json_remote_command(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client);
CHECK_AND_ASSERT_MES(r, false, "failed to send transaction");
if(daemon_send_resp.status != CORE_RPC_STATUS_OK)
if(daemon_send_resp.status != rpc::STATUS_OK)
{
std::cout << "daemon failed to accept generated transaction" << std::endl;
return false;

View File

@ -33,9 +33,9 @@ using namespace std;
using namespace daemonize;
namespace po = boost::program_options;
bool mock_rpc_daemon::on_send_raw_tx_2(const cryptonote::COMMAND_RPC_SEND_RAW_TX::request& req, cryptonote::COMMAND_RPC_SEND_RAW_TX::response& res, const cryptonote::core_rpc_server::connection_context *ctx)
bool mock_rpc_daemon::on_send_raw_tx_2(const cryptonote::rpc::SEND_RAW_TX::request& req, cryptonote::rpc::SEND_RAW_TX::response& res, const cryptonote::core_rpc_server::connection_context *ctx)
{
cryptonote::COMMAND_RPC_SEND_RAW_TX::request req2(req);
cryptonote::rpc::SEND_RAW_TX::request req2(req);
req2.do_not_relay = true; // Do not relay in test setup, only one daemon running.
return cryptonote::core_rpc_server::on_send_raw_tx(req2, res, ctx);
}
@ -345,27 +345,27 @@ constexpr const std::chrono::seconds mock_daemon::rpc_timeout;
void mock_daemon::start_mining(const std::string &miner_address, uint64_t threads_count, bool do_background_mining, bool ignore_battery)
{
cryptonote::COMMAND_RPC_START_MINING::request req;
cryptonote::rpc::START_MINING::request req;
req.miner_address = miner_address;
req.threads_count = threads_count;
req.do_background_mining = do_background_mining;
req.ignore_battery = ignore_battery;
cryptonote::COMMAND_RPC_START_MINING::response resp;
cryptonote::rpc::START_MINING::response resp;
bool r = epee::net_utils::invoke_http_json("/start_mining", req, resp, m_http_client, rpc_timeout);
CHECK_AND_ASSERT_THROW_MES(r, "RPC error - start mining");
CHECK_AND_ASSERT_THROW_MES(resp.status != CORE_RPC_STATUS_BUSY, "Daemon busy");
CHECK_AND_ASSERT_THROW_MES(resp.status == CORE_RPC_STATUS_OK, "Daemon response invalid: " << resp.status);
CHECK_AND_ASSERT_THROW_MES(resp.status != rpc::STATUS_BUSY, "Daemon busy");
CHECK_AND_ASSERT_THROW_MES(resp.status == rpc::STATUS_OK, "Daemon response invalid: " << resp.status);
}
void mock_daemon::stop_mining()
{
cryptonote::COMMAND_RPC_STOP_MINING::request req;
cryptonote::COMMAND_RPC_STOP_MINING::response resp;
cryptonote::rpc::STOP_MINING::request req;
cryptonote::rpc::STOP_MINING::response resp;
bool r = epee::net_utils::invoke_http_json("/stop_mining", req, resp, m_http_client, rpc_timeout);
CHECK_AND_ASSERT_THROW_MES(r, "RPC error - stop mining");
CHECK_AND_ASSERT_THROW_MES(resp.status != CORE_RPC_STATUS_BUSY, "Daemon busy");
CHECK_AND_ASSERT_THROW_MES(resp.status == CORE_RPC_STATUS_OK, "Daemon response invalid: " << resp.status);
CHECK_AND_ASSERT_THROW_MES(resp.status != rpc::STATUS_BUSY, "Daemon busy");
CHECK_AND_ASSERT_THROW_MES(resp.status == rpc::STATUS_OK, "Daemon response invalid: " << resp.status);
}
uint64_t mock_daemon::get_height()

View File

@ -58,14 +58,14 @@ public:
CHAIN_HTTP_TO_MAP2(cryptonote::core_rpc_server::connection_context); //forward http requests to uri map
BEGIN_URI_MAP2()
MAP_URI_AUTO_JON2("/send_raw_transaction", on_send_raw_tx_2, cryptonote::COMMAND_RPC_SEND_RAW_TX)
MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx_2, cryptonote::COMMAND_RPC_SEND_RAW_TX)
MAP_URI_AUTO_JON2("/send_raw_transaction", on_send_raw_tx_2, cryptonote::rpc::SEND_RAW_TX)
MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx_2, cryptonote::rpc::SEND_RAW_TX)
else { // Default to parent for non-overriden callbacks
return cryptonote::core_rpc_server::handle_http_request_map(query_info, response_info, m_conn_context);
}
END_URI_MAP2()
bool on_send_raw_tx_2(const cryptonote::COMMAND_RPC_SEND_RAW_TX::request& req, cryptonote::COMMAND_RPC_SEND_RAW_TX::response& res, const cryptonote::core_rpc_server::connection_context *ctx);
bool on_send_raw_tx_2(const cryptonote::rpc::SEND_RAW_TX::request& req, cryptonote::rpc::SEND_RAW_TX::response& res, const cryptonote::core_rpc_server::connection_context *ctx);
protected:
cryptonote::network_type m_network_type;

71
utils/lmq-rpc.py Executable file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env python3
import nacl.bindings as sodium
from nacl.public import PrivateKey
from nacl.signing import SigningKey, VerifyKey
import nacl.encoding
import requests
import zmq
import sys
import re
import time
import random
import shutil
context = zmq.Context()
socket = context.socket(zmq.DEALER)
socket.setsockopt(zmq.CONNECT_TIMEOUT, 5000)
socket.setsockopt(zmq.HANDSHAKE_IVL, 5000)
#socket.setsockopt(zmq.IMMEDIATE, 1)
if len(sys.argv) > 1 and any(sys.argv[1].startswith(x) for x in ("ipc://", "tcp://")):
remote = sys.argv[1]
del sys.argv[1]
else:
remote = "ipc://./loki.sock"
curve_pubkey = b''
my_privkey, my_pubkey = b'', b''
if len(sys.argv) > 1 and len(sys.argv[1]) == 64 and all(x in "0123456789abcdefABCDEF" for x in sys.argv[1]):
curve_pubkey = bytes.fromhex(sys.argv[1])
del sys.argv[1]
socket.setsockopt(zmq.CURVE_SERVERKEY, curve_pubkey)
if len(sys.argv) > 1 and len(sys.argv[1]) == 64 and all(x in "0123456789abcdefABCDEF" for x in sys.argv[1]):
my_privkey = bytes.fromhex(sys.argv[1])
del sys.argv[1]
my_pubkey = zmq.utils.z85.decode(zmq.curve_public(zmq.utils.z85.encode(my_privkey)))
socket.setsockopt(zmq.CURVE_PUBLICKEY, my_pubkey)
socket.setsockopt(zmq.CURVE_SECRETKEY, my_privkey)
if not 2 <= len(sys.argv) <= 3 or any(x in y for x in ("--help", "-h") for y in sys.argv[1:]):
print("Usage: {} [ipc:///path/to/sock|tcp://1.2.3.4:5678] [SERVER_CURVE_PUBKEY [LOCAL_CURVE_PRIVKEY]] COMMAND ['JSON']".format(
sys.argv[0]), file=sys.stderr)
sys.exit(1)
beginning_of_time = time.clock_gettime(time.CLOCK_MONOTONIC)
print("Connecting to {}".format(remote), file=sys.stderr)
socket.connect(remote)
to_send = [sys.argv[1].encode(), b'tagxyz123']
#to_send += (x.encode() for x in sys.argv[2:])
print("Sending {}".format(to_send[0]), file=sys.stderr)
socket.send_multipart(to_send)
if socket.poll(timeout=5000):
m = socket.recv_multipart()
recv_time = time.clock_gettime(time.CLOCK_MONOTONIC)
if len(m) < 3 or m[0:2] != [b'REPLY', b'tagxyz123']:
print("Received unexpected {}-part reply:".format(len(m)), file=sys.stderr)
for x in m:
print("- {}".format(x))
else:
print("Received {} reply in {:.6f}s:".format(m[2].decode(), recv_time - beginning_of_time), file=sys.stderr)
if len(m) < 4:
print("(empty reply data)", file=sys.stderr)
else:
for x in m[3:]:
print(x.decode(), end="\n\n")
else:
print("Request timed out", file=sys.stderr)
socket.close(linger=0)
sys.exit(1)

Some files were not shown because too many files have changed in this diff Show More