aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/daemon/rpc_command_executor.cpp25
-rw-r--r--src/rpc/core_rpc_server.cpp272
-rw-r--r--src/rpc/core_rpc_server.h14
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h64
4 files changed, 370 insertions, 5 deletions
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 0ae0fd2cc..5ef799d40 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -353,15 +353,18 @@ static std::string get_fork_extra_info(uint64_t t, uint64_t now, uint64_t block_
return "";
}
-static float get_sync_percentage(const cryptonote::COMMAND_RPC_GET_INFO::response &ires)
+static float get_sync_percentage(uint64_t height, uint64_t target_height)
{
- uint64_t height = ires.height;
- uint64_t target_height = ires.target_height ? ires.target_height < ires.height ? ires.height : ires.target_height : ires.height;
+ target_height = target_height ? target_height < height ? height : target_height : height;
float pc = 100.0f * height / target_height;
if (height < target_height && pc > 99.9f)
return 99.9f; // to avoid 100% when not fully synced
return pc;
}
+static float get_sync_percentage(const cryptonote::COMMAND_RPC_GET_INFO::response &ires)
+{
+ return get_sync_percentage(ires.height, ires.target_height);
+}
bool t_rpc_command_executor::show_status() {
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
@@ -421,12 +424,26 @@ bool t_rpc_command_executor::show_status() {
std::time_t uptime = std::time(nullptr) - ires.start_time;
uint64_t net_height = ires.target_height > ires.height ? ires.target_height : ires.height;
+ std::string bootstrap_msg;
+ if (ires.was_bootstrap_ever_used)
+ {
+ bootstrap_msg = ", bootstrapping from " + ires.bootstrap_daemon_address;
+ if (ires.untrusted)
+ {
+ bootstrap_msg += (boost::format(", local height: %llu (%.1f%%)") % ires.height_without_bootstrap % get_sync_percentage(ires.height_without_bootstrap, net_height)).str();
+ }
+ else
+ {
+ bootstrap_msg += " was used before";
+ }
+ }
- tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us")
+ tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us")
% (unsigned long long)ires.height
% (unsigned long long)net_height
% get_sync_percentage(ires)
% (ires.testnet ? "testnet" : "mainnet")
+ % bootstrap_msg
% (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining")
% get_mining_speed(ires.difficulty / ires.target)
% (unsigned)hfres.version
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 80d9823d4..da2e79dfa 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -42,6 +42,7 @@ using namespace epee;
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "misc_language.h"
+#include "storages/http_abstract_invoke.h"
#include "crypto/hash.h"
#include "rpc/rpc_args.h"
#include "core_rpc_server_error_codes.h"
@@ -75,6 +76,8 @@ namespace cryptonote
command_line::add_arg(desc, arg_testnet_rpc_bind_port);
command_line::add_arg(desc, arg_testnet_rpc_restricted_bind_port);
command_line::add_arg(desc, arg_restricted_rpc);
+ command_line::add_arg(desc, arg_bootstrap_daemon_address);
+ command_line::add_arg(desc, arg_bootstrap_daemon_login);
cryptonote::rpc_args::init_options(desc);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -101,6 +104,30 @@ namespace cryptonote
if (!rpc_config)
return false;
+ m_bootstrap_daemon_address = command_line::get_arg(vm, arg_bootstrap_daemon_address);
+ if (!m_bootstrap_daemon_address.empty())
+ {
+ const std::string &bootstrap_daemon_login = command_line::get_arg(vm, arg_bootstrap_daemon_login);
+ const auto loc = bootstrap_daemon_login.find(':');
+ if (!bootstrap_daemon_login.empty() && loc != std::string::npos)
+ {
+ epee::net_utils::http::login login;
+ login.username = bootstrap_daemon_login.substr(0, loc);
+ login.password = bootstrap_daemon_login.substr(loc + 1);
+ m_http_client.set_server(m_bootstrap_daemon_address, login, false);
+ }
+ else
+ {
+ m_http_client.set_server(m_bootstrap_daemon_address, boost::none, false);
+ }
+ m_should_use_bootstrap_daemon = true;
+ }
+ else
+ {
+ m_should_use_bootstrap_daemon = false;
+ }
+ m_was_bootstrap_ever_used = false;
+
boost::optional<epee::net_utils::http::login> http_login{};
if (rpc_config->login)
@@ -126,6 +153,10 @@ namespace cryptonote
bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res)
{
PERF_TIMER(on_get_height);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HEIGHT>(invoke_http_mode::JON, "/getheight", req, res, r))
+ return r;
+
res.height = m_core.get_current_blockchain_height();
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -134,6 +165,17 @@ namespace cryptonote
bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res)
{
PERF_TIMER(on_get_info);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON, "/getinfo", req, res, r))
+ {
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ crypto::hash top_hash;
+ m_core.get_blockchain_top(res.height_without_bootstrap, top_hash);
+ ++res.height_without_bootstrap; // turn top block height into blockchain height
+ res.was_bootstrap_ever_used = true;
+ return r;
+ }
+
crypto::hash top_hash;
m_core.get_blockchain_top(res.height, top_hash);
++res.height; // turn top block height into blockchain height
@@ -158,6 +200,12 @@ namespace cryptonote
res.start_time = (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
res.offline = m_core.offline();
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ res.height_without_bootstrap = res.height;
+ {
+ boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ res.was_bootstrap_ever_used = m_was_bootstrap_ever_used;
+ }
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -181,6 +229,10 @@ namespace cryptonote
bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res)
{
PERF_TIMER(on_get_blocks);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_FAST>(invoke_http_mode::BIN, "/getblocks.bin", req, res, r))
+ return r;
+
std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > > bs;
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
@@ -240,6 +292,10 @@ namespace cryptonote
bool core_rpc_server::on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res)
{
PERF_TIMER(on_get_alt_blocks_hashes);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_ALT_BLOCKS_HASHES>(invoke_http_mode::JON, "/get_alt_blocks_hashes", req, res, r))
+ return r;
+
std::list<block> blks;
if(!m_core.get_alternative_blocks(blks))
@@ -263,6 +319,10 @@ namespace cryptonote
bool core_rpc_server::on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res)
{
PERF_TIMER(on_get_blocks_by_height);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_BY_HEIGHT>(invoke_http_mode::BIN, "/getblocks_by_height.bin", req, res, r))
+ return r;
+
res.status = "Failed";
res.blocks.clear();
res.blocks.reserve(req.heights.size());
@@ -293,6 +353,10 @@ namespace cryptonote
bool core_rpc_server::on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res)
{
PERF_TIMER(on_get_hashes);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HASHES_FAST>(invoke_http_mode::BIN, "/gethashes.bin", req, res, r))
+ return r;
+
NOTIFY_RESPONSE_CHAIN_ENTRY::request resp;
resp.start_height = req.start_height;
@@ -312,6 +376,10 @@ namespace cryptonote
bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)
{
PERF_TIMER(on_get_random_outs);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS>(invoke_http_mode::BIN, "/getrandom_outs.bin", req, res, r))
+ return r;
+
res.status = "Failed";
if (m_restricted)
@@ -351,6 +419,10 @@ namespace cryptonote
bool core_rpc_server::on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res)
{
PERF_TIMER(on_get_outs_bin);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUTS_BIN>(invoke_http_mode::BIN, "/get_outs.bin", req, res, r))
+ return r;
+
res.status = "Failed";
if (m_restricted)
@@ -374,6 +446,10 @@ namespace cryptonote
bool core_rpc_server::on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res)
{
PERF_TIMER(on_get_outs);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUTS>(invoke_http_mode::JON, "/get_outs", req, res, r))
+ return r;
+
res.status = "Failed";
if (m_restricted)
@@ -412,6 +488,10 @@ namespace cryptonote
bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res)
{
PERF_TIMER(on_get_random_rct_outs);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS>(invoke_http_mode::BIN, "/getrandom_rctouts.bin", req, res, r))
+ return r;
+
res.status = "Failed";
if(!m_core.get_random_rct_outs(req, res))
{
@@ -436,6 +516,10 @@ namespace cryptonote
bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res)
{
PERF_TIMER(on_get_indexes);
+ bool ok;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES>(invoke_http_mode::BIN, "/get_o_indexes.bin", req, res, ok))
+ return ok;
+
bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes);
if(!r)
{
@@ -450,6 +534,10 @@ namespace cryptonote
bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res)
{
PERF_TIMER(on_get_transactions);
+ bool ok;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTIONS>(invoke_http_mode::JON, "/gettransactions", req, res, ok))
+ return ok;
+
std::vector<crypto::hash> vh;
for(const auto& tx_hex_str: req.txs_hashes)
{
@@ -600,6 +688,10 @@ namespace cryptonote
bool core_rpc_server::on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res, bool request_has_rpc_origin)
{
PERF_TIMER(on_is_key_image_spent);
+ bool ok;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_IS_KEY_IMAGE_SPENT>(invoke_http_mode::JON, "/is_key_image_spent", req, res, ok))
+ return ok;
+
std::vector<crypto::key_image> key_images;
for(const auto& ki_hex_str: req.key_images)
{
@@ -663,6 +755,10 @@ namespace cryptonote
bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res)
{
PERF_TIMER(on_send_raw_tx);
+ bool ok;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_SEND_RAW_TX>(invoke_http_mode::JON, "/sendrawtransaction", req, res, ok))
+ return ok;
+
CHECK_CORE_READY();
std::string tx_blob;
@@ -886,6 +982,10 @@ namespace cryptonote
bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, bool request_has_rpc_origin)
{
PERF_TIMER(on_get_transaction_pool);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL>(invoke_http_mode::JON, "/get_transaction_pool", req, res, r))
+ return r;
+
m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted);
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -894,6 +994,10 @@ namespace cryptonote
bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, bool request_has_rpc_origin)
{
PERF_TIMER(on_get_transaction_pool_hashes);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_HASHES>(invoke_http_mode::JON, "/get_transaction_pool_hashes.bin", req, res, r))
+ return r;
+
m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !m_restricted);
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -902,6 +1006,10 @@ namespace cryptonote
bool core_rpc_server::on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, bool request_has_rpc_origin)
{
PERF_TIMER(on_get_transaction_pool_stats);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_STATS>(invoke_http_mode::JON, "/get_transaction_pool_stats", req, res, r))
+ return r;
+
m_core.get_pool_transaction_stats(res.pool_stats, !request_has_rpc_origin || !m_restricted);
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -920,6 +1028,14 @@ namespace cryptonote
bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res)
{
PERF_TIMER(on_getblockcount);
+ {
+ boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ if (m_should_use_bootstrap_daemon)
+ {
+ res.status = "This command is unsupported for bootstrap daemon";
+ return false;
+ }
+ }
res.count = m_core.get_current_blockchain_height();
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -928,6 +1044,14 @@ namespace cryptonote
bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp)
{
PERF_TIMER(on_getblockhash);
+ {
+ boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ if (m_should_use_bootstrap_daemon)
+ {
+ res = "This command is unsupported for bootstrap daemon";
+ return false;
+ }
+ }
if(req.size() != 1)
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
@@ -964,6 +1088,10 @@ namespace cryptonote
bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp)
{
PERF_TIMER(on_getblocktemplate);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GETBLOCKTEMPLATE>(invoke_http_mode::JON_RPC, "getblocktemplate", req, res, r))
+ return r;
+
if(!check_core_ready())
{
error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY;
@@ -1039,6 +1167,14 @@ namespace cryptonote
bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp)
{
PERF_TIMER(on_submitblock);
+ {
+ boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ if (m_should_use_bootstrap_daemon)
+ {
+ res.status = "This command is unsupported for bootstrap daemon";
+ return false;
+ }
+ }
CHECK_CORE_READY();
if(req.size()!=1)
{
@@ -1112,9 +1248,80 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ template <typename COMMAND_TYPE>
+ bool core_rpc_server::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)
+ {
+ res.untrusted = false;
+ if (m_bootstrap_daemon_address.empty())
+ return false;
+
+ boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ if (!m_should_use_bootstrap_daemon)
+ {
+ MINFO("The local daemon is fully synced. Not switching back to the bootstrap daemon");
+ return false;
+ }
+
+ auto current_time = std::chrono::system_clock::now();
+ if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s
+ {
+ m_bootstrap_height_check_time = current_time;
+
+ uint64_t top_height;
+ crypto::hash top_hash;
+ m_core.get_blockchain_top(top_height, top_hash);
+ ++top_height; // turn top block height into blockchain height
+
+ // query bootstrap daemon's height
+ cryptonote::COMMAND_RPC_GET_HEIGHT::request getheight_req;
+ cryptonote::COMMAND_RPC_GET_HEIGHT::response getheight_res;
+ bool ok = epee::net_utils::invoke_http_json("/getheight", getheight_req, getheight_res, m_http_client);
+ ok = ok && getheight_res.status == CORE_RPC_STATUS_OK;
+
+ m_should_use_bootstrap_daemon = ok && top_height + 10 < getheight_res.height;
+ MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << getheight_res.height << ")");
+ }
+ if (!m_should_use_bootstrap_daemon)
+ return false;
+
+ if (mode == invoke_http_mode::JON)
+ {
+ r = epee::net_utils::invoke_http_json(command_name, req, res, m_http_client);
+ }
+ else if (mode == invoke_http_mode::BIN)
+ {
+ r = epee::net_utils::invoke_http_bin(command_name, req, res, m_http_client);
+ }
+ else if (mode == invoke_http_mode::JON_RPC)
+ {
+ epee::json_rpc::request<typename COMMAND_TYPE::request> json_req = AUTO_VAL_INIT(json_req);
+ epee::json_rpc::response<typename COMMAND_TYPE::response, std::string> json_resp = AUTO_VAL_INIT(json_resp);
+ json_req.jsonrpc = "2.0";
+ json_req.id = epee::serialization::storage_entry(0);
+ json_req.method = command_name;
+ json_req.params = req;
+ r = net_utils::invoke_http_json("/json_rpc", json_req, json_resp, m_http_client);
+ if (r)
+ res = json_resp.result;
+ }
+ else
+ {
+ MERROR("Unknown invoke_http_mode: " << mode);
+ return false;
+ }
+ m_was_bootstrap_ever_used = true;
+ r = r && res.status == CORE_RPC_STATUS_OK;
+ res.untrusted = true;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::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)
{
PERF_TIMER(on_get_last_block_header);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_LAST_BLOCK_HEADER>(invoke_http_mode::JON_RPC, "getlastblockheader", req, res, r))
+ return r;
+
CHECK_CORE_READY();
uint64_t last_block_height;
crypto::hash last_block_hash;
@@ -1140,6 +1347,10 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::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){
PERF_TIMER(on_get_block_header_by_hash);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH>(invoke_http_mode::JON_RPC, "getblockheaderbyhash", req, res, r))
+ return r;
+
crypto::hash block_hash;
bool hash_parsed = parse_hash256(req.hash, block_hash);
if(!hash_parsed)
@@ -1177,6 +1388,10 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::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){
PERF_TIMER(on_get_block_headers_range);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADERS_RANGE>(invoke_http_mode::JON_RPC, "getblockheadersrange", req, res, r))
+ return r;
+
const uint64_t bc_height = m_core.get_current_blockchain_height();
if (req.start_height >= bc_height || req.end_height >= bc_height || req.start_height > req.end_height)
{
@@ -1223,6 +1438,10 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::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){
PERF_TIMER(on_get_block_header_by_height);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT>(invoke_http_mode::JON_RPC, "getblockheaderbyheight", req, res, r))
+ return r;
+
if(m_core.get_current_blockchain_height() <= req.height)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
@@ -1251,6 +1470,10 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_block(const COMMAND_RPC_GET_BLOCK::request& req, COMMAND_RPC_GET_BLOCK::response& res, epee::json_rpc::error& error_resp){
PERF_TIMER(on_get_block);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK>(invoke_http_mode::JON_RPC, "getblock", req, res, r))
+ return r;
+
crypto::hash block_hash;
if (!req.hash.empty())
{
@@ -1320,6 +1543,16 @@ namespace cryptonote
bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp)
{
PERF_TIMER(on_get_info_json);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON_RPC, "get_info", req, res, r))
+ {
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ crypto::hash top_hash;
+ m_core.get_blockchain_top(res.height_without_bootstrap, top_hash);
+ ++res.height_without_bootstrap; // turn top block height into blockchain height
+ res.was_bootstrap_ever_used = true;
+ return r;
+ }
crypto::hash top_hash;
m_core.get_blockchain_top(res.height, top_hash);
@@ -1345,12 +1578,21 @@ namespace cryptonote
res.start_time = (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
res.offline = m_core.offline();
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ res.height_without_bootstrap = res.height;
+ {
+ boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ res.was_bootstrap_ever_used = m_was_bootstrap_ever_used;
+ }
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::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)
{
PERF_TIMER(on_hard_fork_info);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_HARD_FORK_INFO>(invoke_http_mode::JON_RPC, "hard_fork_info", req, res, r))
+ return r;
const Blockchain &blockchain = m_core.get_blockchain_storage();
uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version();
@@ -1473,6 +1715,9 @@ namespace cryptonote
bool core_rpc_server::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)
{
PERF_TIMER(on_get_output_histogram);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_HISTOGRAM>(invoke_http_mode::JON_RPC, "get_output_histogram", req, res, r))
+ return r;
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> histogram;
try
@@ -1500,6 +1745,10 @@ namespace cryptonote
bool core_rpc_server::on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp)
{
PERF_TIMER(on_get_version);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_VERSION>(invoke_http_mode::JON_RPC, "get_version", req, res, r))
+ return r;
+
res.version = CORE_RPC_VERSION;
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -1518,6 +1767,10 @@ namespace cryptonote
bool core_rpc_server::on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp)
{
PERF_TIMER(on_get_per_kb_fee_estimate);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE>(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r))
+ return r;
+
res.fee = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.grace_blocks);
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -1545,6 +1798,10 @@ namespace cryptonote
bool core_rpc_server::on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res)
{
PERF_TIMER(on_get_limit);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_LIMIT>(invoke_http_mode::JON, "/get_limit", req, res, r))
+ return r;
+
res.limit_down = epee::net_utils::connection_basic::get_rate_down_limit();
res.limit_up = epee::net_utils::connection_basic::get_rate_up_limit();
res.status = CORE_RPC_STATUS_OK;
@@ -1802,6 +2059,9 @@ namespace cryptonote
bool core_rpc_server::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)
{
PERF_TIMER(on_get_txpool_backlog);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG>(invoke_http_mode::JON_RPC, "get_txpool_backlog", req, res, r))
+ return r;
if (!m_core.get_txpool_backlog(res.backlog))
{
@@ -1844,4 +2104,16 @@ namespace cryptonote
, "Restrict RPC to view only commands and do not return privacy sensitive data in RPC calls"
, false
};
+
+ const command_line::arg_descriptor<std::string> core_rpc_server::arg_bootstrap_daemon_address = {
+ "bootstrap-daemon-address"
+ , "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced"
+ , ""
+ };
+
+ const command_line::arg_descriptor<std::string> core_rpc_server::arg_bootstrap_daemon_login = {
+ "bootstrap-daemon-login"
+ , "Specify username:password for the bootstrap daemon login"
+ , ""
+ };
} // namespace cryptonote
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 2b0d54bef..650e738bd 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -34,6 +34,7 @@
#include <boost/program_options/variables_map.hpp>
#include "net/http_server_impl_base.h"
+#include "net/http_client.h"
#include "core_rpc_server_commands_defs.h"
#include "cryptonote_core/cryptonote_core.h"
#include "p2p/net_node.h"
@@ -57,6 +58,8 @@ namespace cryptonote
static const command_line::arg_descriptor<std::string> arg_testnet_rpc_bind_port;
static const command_line::arg_descriptor<std::string> arg_testnet_rpc_restricted_bind_port;
static const command_line::arg_descriptor<bool> arg_restricted_rpc;
+ static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_address;
+ static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_login;
typedef epee::net_utils::connection_context_base connection_context;
@@ -171,7 +174,7 @@ namespace cryptonote
bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res);
bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res);
bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res);
- bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res);
+ bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res);
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res);
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res);
bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res);
@@ -222,9 +225,18 @@ private:
//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);
+ enum invoke_http_mode { JON, BIN, JON_RPC };
+ 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);
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::chrono::system_clock::time_point m_bootstrap_height_check_time;
+ bool m_was_bootstrap_ever_used;
bool m_testnet;
bool m_restricted;
};
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 3a878c4cc..64a97f8a3 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -65,10 +65,12 @@ namespace cryptonote
{
uint64_t height;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(height)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -113,6 +115,7 @@ namespace cryptonote
uint64_t current_height;
std::string status;
std::vector<block_output_indices> output_indices;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(blocks)
@@ -120,6 +123,7 @@ namespace cryptonote
KV_SERIALIZE(current_height)
KV_SERIALIZE(status)
KV_SERIALIZE(output_indices)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -138,10 +142,12 @@ namespace cryptonote
{
std::vector<block_complete_entry> blocks;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(blocks)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -158,10 +164,12 @@ namespace cryptonote
{
std::vector<std::string> blks_hashes;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(blks_hashes)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -184,12 +192,14 @@ namespace cryptonote
uint64_t start_height;
uint64_t current_height;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids)
KV_SERIALIZE(start_height)
KV_SERIALIZE(current_height)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -595,6 +605,7 @@ namespace cryptonote
// new style
std::vector<entry> txs;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txs_as_hex)
@@ -602,6 +613,7 @@ namespace cryptonote
KV_SERIALIZE(txs)
KV_SERIALIZE(missed_tx)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -629,10 +641,12 @@ namespace cryptonote
{
std::vector<int> spent_status;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(spent_status)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -653,9 +667,11 @@ namespace cryptonote
{
std::vector<uint64_t> o_indexes;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(o_indexes)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -695,9 +711,11 @@ namespace cryptonote
{
std::vector<outs_for_amount> outs;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outs)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -745,10 +763,12 @@ namespace cryptonote
{
std::vector<outkey> outs;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outs)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -785,10 +805,12 @@ namespace cryptonote
{
std::vector<outkey> outs;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outs)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -817,9 +839,11 @@ namespace cryptonote
{
std::list<out_entry> outs;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -854,6 +878,7 @@ namespace cryptonote
bool overspend;
bool fee_too_low;
bool not_rct;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
@@ -867,6 +892,7 @@ namespace cryptonote
KV_SERIALIZE(overspend)
KV_SERIALIZE(fee_too_low)
KV_SERIALIZE(not_rct)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -930,6 +956,10 @@ namespace cryptonote
uint64_t start_time;
uint64_t free_space;
bool offline;
+ bool untrusted;
+ std::string bootstrap_daemon_address;
+ uint64_t height_without_bootstrap;
+ bool was_bootstrap_ever_used;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
@@ -953,6 +983,10 @@ namespace cryptonote
KV_SERIALIZE(start_time)
KV_SERIALIZE(free_space)
KV_SERIALIZE(offline)
+ KV_SERIALIZE(untrusted)
+ KV_SERIALIZE(bootstrap_daemon_address)
+ KV_SERIALIZE(height_without_bootstrap)
+ KV_SERIALIZE(was_bootstrap_ever_used)
END_KV_SERIALIZE_MAP()
};
};
@@ -1080,6 +1114,7 @@ namespace cryptonote
blobdata blocktemplate_blob;
blobdata blockhashing_blob;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(difficulty)
@@ -1090,6 +1125,7 @@ namespace cryptonote
KV_SERIALIZE(blocktemplate_blob)
KV_SERIALIZE(blockhashing_blob)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1153,10 +1189,12 @@ namespace cryptonote
{
std::string status;
block_header_response block_header;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block_header)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
@@ -1177,10 +1215,12 @@ namespace cryptonote
{
std::string status;
block_header_response block_header;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block_header)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
@@ -1201,10 +1241,12 @@ namespace cryptonote
{
std::string status;
block_header_response block_header;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block_header)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
@@ -1231,6 +1273,7 @@ namespace cryptonote
std::vector<std::string> tx_hashes;
std::string blob;
std::string json;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block_header)
@@ -1239,6 +1282,7 @@ namespace cryptonote
KV_SERIALIZE(status)
KV_SERIALIZE(blob)
KV_SERIALIZE(json)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
@@ -1415,11 +1459,13 @@ namespace cryptonote
std::string status;
std::vector<tx_info> transactions;
std::vector<spent_key_image_info> spent_key_images;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(transactions)
KV_SERIALIZE(spent_key_images)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1436,10 +1482,12 @@ namespace cryptonote
{
std::string status;
std::vector<crypto::hash> tx_hashes;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1463,10 +1511,12 @@ namespace cryptonote
{
std::string status;
std::vector<tx_backlog_entry> backlog;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(backlog)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1527,10 +1577,12 @@ namespace cryptonote
{
std::string status;
txpool_stats pool_stats;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(pool_stats)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1573,10 +1625,12 @@ namespace cryptonote
{
std::string status;
std::vector<block_header_response> headers;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(headers)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1630,11 +1684,13 @@ namespace cryptonote
std::string status;
uint64_t limit_up;
uint64_t limit_down;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(limit_up)
KV_SERIALIZE(limit_down)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1764,6 +1820,7 @@ namespace cryptonote
uint32_t state;
uint64_t earliest_height;
std::string status;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(version)
@@ -1775,6 +1832,7 @@ namespace cryptonote
KV_SERIALIZE(state)
KV_SERIALIZE(earliest_height)
KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1911,10 +1969,12 @@ namespace cryptonote
{
std::string status;
std::vector<entry> histogram;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(histogram)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1931,10 +1991,12 @@ namespace cryptonote
{
std::string status;
uint32_t version;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(version)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};
@@ -1981,10 +2043,12 @@ namespace cryptonote
{
std::string status;
uint64_t fee;
+ bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(fee)
+ KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
};