mirror of https://github.com/oxen-io/oxen-core.git
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:
parent
d3a8baf9e7
commit
0e3f173c7f
|
@ -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)
|
||||
|
|
|
@ -153,10 +153,5 @@ namespace misc_utils
|
|||
return slc;
|
||||
}
|
||||
|
||||
template<typename T> struct struct_init: T
|
||||
{
|
||||
struct_init(): T{} {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#pragma once
|
||||
|
|
@ -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_
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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) << "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 << '}';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -50,4 +50,5 @@ target_link_libraries(daemon
|
|||
Boost::filesystem
|
||||
Boost::program_options
|
||||
Boost::system
|
||||
systemd
|
||||
extra)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
}}
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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;
|
||||
|
||||
}}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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"); }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -34,7 +34,7 @@ target_link_libraries(simplewallet
|
|||
PRIVATE
|
||||
wallet
|
||||
daemonizer
|
||||
rpc_base
|
||||
rpc_commands
|
||||
cryptonote_core
|
||||
mnemonics
|
||||
epee_readline
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
//-----------------------------------------------
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue