amend wallet2 and simple wallet to use bt-rpc definitions

This goes through the remaining code in wallet2 that needed updating to
utilise our newly defined json_rpc methods rather than epee
serialisation to call rpc methods.
This commit is contained in:
Sean Darcy 2022-03-17 16:42:52 +11:00
parent 8a66a526bc
commit bb82585f71
10 changed files with 636 additions and 531 deletions

View File

@ -1095,6 +1095,7 @@ namespace cryptonote
return buf;
}
//---------------------------------------------------------------
std::unordered_set<std::string> tx_verification_failure_codes(const tx_verification_context& tvc) {
std::unordered_set<std::string> reasons;

View File

@ -2,6 +2,16 @@
namespace cryptonote::rpc {
void to_json(nlohmann::json& j, const GET_BLOCKS_BIN::tx_output_indices& toi)
{
j = nlohmann::json{{"indices", toi.indices}};
}
void to_json(nlohmann::json& j, const GET_BLOCKS_BIN::block_output_indices& boi)
{
j = nlohmann::json{{"indices", boi.indices}};
}
KV_SERIALIZE_MAP_CODE_BEGIN(GET_BLOCKS_BIN::request)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
KV_SERIALIZE(start_height)

View File

@ -63,6 +63,7 @@ namespace cryptonote::rpc {
struct tx_output_indices
{
std::vector<uint64_t> indices; // Array of unsigned int.
KV_MAP_SERIALIZABLE
};
@ -70,6 +71,7 @@ namespace cryptonote::rpc {
struct block_output_indices
{
std::vector<tx_output_indices> indices; // Array of TX output indices:
KV_MAP_SERIALIZABLE
};
@ -87,6 +89,9 @@ namespace cryptonote::rpc {
};
};
void to_json(nlohmann::json& j, const GET_BLOCKS_BIN::tx_output_indices& toi);
void to_json(nlohmann::json& j, const GET_BLOCKS_BIN::block_output_indices& boi);
OXEN_RPC_DOC_INTROSPECT
// Get blocks by height. Binary request.
struct GET_BLOCKS_BY_HEIGHT_BIN : PUBLIC, BINARY

View File

@ -84,7 +84,7 @@ namespace cryptonote::rpc {
if (m_closing) res.writeHeader("Connection", "close");
res.end(nlohmann::json{
{"jsonrpc", "2.0"},
{"id", std::move(id)},
{"id", id.get<std::string>()},
{"error", nlohmann::json{
{"code", code},
{"message", std::move(message)}

View File

@ -4076,11 +4076,14 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
message_writer(epee::console_color_yellow, true) << tr("If you or someone you trust are operating this daemon, you can use --trusted-daemon");
message_writer();
cryptonote::rpc::GET_INFO::request req;
cryptonote::rpc::GET_INFO::response res;
bool r = m_wallet->invoke_http<rpc::GET_INFO>(req, res);
std::string err = interpret_rpc_response(r, res.status);
if (r && err.empty() && res.untrusted)
nlohmann::json res;
try {
res = m_wallet->json_rpc("get_info", {});
} catch (const std::exception& e) {
fail_msg_writer() << tr("wallet failed to connect to daemon when calling get_info at ") << m_wallet->get_daemon_address() << ": " << e.what() << ".\n";
}
std::string err = interpret_rpc_response(true, res["status"]);
if (err.empty() && res["untrusted"].get<bool>())
message_writer(epee::console_color_yellow, true) << tr("Moreover, a daemon is also less secure when running in bootstrap mode");
}
@ -4685,21 +4688,19 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
fail_msg_writer() << tr("wallet is null");
return true;
}
rpc::START_MINING::request req{};
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
bool ok = true;
size_t arg_size = args.size();
nlohmann::json req_params{
{"miner_address", m_wallet->get_account().get_public_address_str(m_wallet->nettype())},
{"threads_count", 1},
};
if(arg_size >= 1)
{
uint16_t num = 1;
ok = string_tools::get_xtype_from_string(num, args[0]);
ok = ok && 1 <= num;
req.threads_count = num;
}
else
{
req.threads_count = 1;
req_params["threads_count"] = num;
}
if (!ok)
@ -4708,9 +4709,13 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
return true;
}
rpc::START_MINING::response res{};
bool r = m_wallet->invoke_http<rpc::START_MINING>(req, res);
std::string err = interpret_rpc_response(r, res.status);
nlohmann::json res;
try {
res = m_wallet->json_rpc("start_mining", req_params);
} catch (const std::exception& e) {
fail_msg_writer() << tr("wallet failed to communicate with daemon when calling start_mining at ") << m_wallet->get_daemon_address() << ": " << e.what() << ".\n";
}
std::string err = interpret_rpc_response(true, res["status"].get<std::string>());
if (err.empty())
success_msg_writer() << tr("Mining started in daemon");
else
@ -4729,9 +4734,13 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
return true;
}
rpc::STOP_MINING::response res{};
bool r = m_wallet->invoke_http<rpc::STOP_MINING>({}, res);
std::string err = interpret_rpc_response(r, res.status);
nlohmann::json res;
try {
res = m_wallet->json_rpc("stop_mining", {});
} catch (const std::exception& e) {
fail_msg_writer() << tr("wallet failed to communicate with daemon when calling stop_mining at ") << m_wallet->get_daemon_address() << ": " << e.what() << ".\n";
}
std::string err = interpret_rpc_response(true, res["status"].get<std::string>());
if (err.empty())
success_msg_writer() << tr("Mining stopped in daemon");
else
@ -4797,10 +4806,13 @@ bool simple_wallet::save_bc(const std::vector<std::string>& args)
fail_msg_writer() << tr("wallet is null");
return true;
}
rpc::SAVE_BC::request req{};
rpc::SAVE_BC::response res{};
bool r = m_wallet->invoke_http<rpc::SAVE_BC>(req, res);
std::string err = interpret_rpc_response(r, res.status);
nlohmann::json res;
try {
res = m_wallet->json_rpc("save_bc", {});
} catch (const std::exception& e) {
fail_msg_writer() << tr("wallet failed to connect to daemon when calling save_bc at ") << m_wallet->get_daemon_address() << ": " << e.what() << ".\n";
}
std::string err = interpret_rpc_response(true, res["status"]);
if (err.empty())
success_msg_writer() << tr("Blockchain saved");
else
@ -6321,14 +6333,14 @@ bool simple_wallet::query_locked_stakes(bool print_result)
using namespace cryptonote;
auto response = m_wallet->list_current_stakes();
for (rpc::GET_SERVICE_NODES::response::entry const &node_info : response)
for (const auto& node_info : response)
{
bool only_once = true;
for (const auto& contributor : node_info.contributors)
for (const auto& contributor : node_info["contributors"])
{
for (size_t i = 0; i < contributor.locked_contributions.size(); ++i)
for (size_t i = 0; i < contributor["locked_contributions"].size(); ++i)
{
const auto& contribution = contributor.locked_contributions[i];
const auto& contribution = contributor["locked_contributions"][i];
has_locked_stakes = true;
if (!print_result)
@ -6339,29 +6351,29 @@ bool simple_wallet::query_locked_stakes(bool print_result)
{
only_once = false;
msg_buf.append("Service Node: ");
msg_buf.append(node_info.service_node_pubkey);
msg_buf.append(node_info["service_node_pubkey"]);
msg_buf.append("\n");
msg_buf.append("Unlock Height: ");
if (node_info.requested_unlock_height == service_nodes::KEY_IMAGE_AWAITING_UNLOCK_HEIGHT)
if (node_info["requested_unlock_height"].get<uint64_t>() == service_nodes::KEY_IMAGE_AWAITING_UNLOCK_HEIGHT)
msg_buf.append("Unlock not requested yet");
else
msg_buf.append(std::to_string(node_info.requested_unlock_height));
msg_buf.append(node_info["requested_unlock_height"]);
msg_buf.append("\n");
msg_buf.append("Total Locked: ");
msg_buf.append(cryptonote::print_money(contributor.amount));
msg_buf.append(cryptonote::print_money(contributor["amount"].get<uint64_t>()));
msg_buf.append("\n");
msg_buf.append("Amount/Key Image: ");
}
msg_buf.append(cryptonote::print_money(contribution.amount));
msg_buf.append(cryptonote::print_money(contribution["amount"].get<uint64_t>()));
msg_buf.append("/");
msg_buf.append(contribution.key_image);
msg_buf.append(contribution["key_image"].get<std::string>());
msg_buf.append("\n");
if (i < (contributor.locked_contributions.size() - 1))
if (i < (contributor["locked_contributions"].size() - 1))
{
msg_buf.append(" ");
}
@ -6387,11 +6399,11 @@ 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)
{
rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry const &entry = response[i];
const auto& 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))
if(!epee::string_tools::parse_hexstr_to_binbuff(entry["key_image"].get<std::string>(), binary_buf) || binary_buf.size() != sizeof(crypto::key_image))
{
fail_msg_writer() << tr("Failed to parse hex representation of key image: ") << entry.key_image;
fail_msg_writer() << tr("Failed to parse hex representation of key image: ") << entry["key_image"];
continue;
}
@ -6410,14 +6422,14 @@ bool simple_wallet::query_locked_stakes(bool print_result)
}
msg_buf.append(" Unlock Height/Key Image: ");
msg_buf.append(std::to_string(entry.unlock_height));
msg_buf.append(entry["unlock_height"].get<std::string>());
msg_buf.append("/");
msg_buf.append(entry.key_image);
msg_buf.append(entry["key_image"].get<std::string>());
msg_buf.append("\n");
if (entry.amount > 0) {
if (entry["amount"].get<uint64_t>() > 0) {
// version >= service_nodes::key_image_blacklist_entry::version_1_serialize_amount
msg_buf.append(" Total Locked: ");
msg_buf.append(cryptonote::print_money(entry.amount));
msg_buf.append(cryptonote::print_money(entry["amount"].get<uint64_t>()));
msg_buf.append("\n");
}
@ -6611,7 +6623,7 @@ bool simple_wallet::ons_renew_mapping(std::vector<std::string> args)
SCOPED_WALLET_UNLOCK();
std::string reason;
std::vector<tools::wallet2::pending_tx> ptx_vector;
std::vector<cryptonote::rpc::ONS_NAMES_TO_OWNERS::response_entry> response;
nlohmann::json response;
try
{
ptx_vector = m_wallet->ons_create_renewal_tx(
@ -6646,7 +6658,7 @@ bool simple_wallet::ons_renew_mapping(std::vector<std::string> args)
else if (type == ons::mapping_type::lokinet_10years) years = 10;
int blocks = BLOCKS_EXPECTED_IN_DAYS(years * ons::REGISTRATION_YEAR_DAYS);
std::cout << boost::format(tr("Renewal years: %d (%d blocks)")) % years % blocks << "\n";
std::cout << boost::format(tr("New expiry: Block %d")) % (*response[0].expiration_height + blocks) << "\n";
std::cout << boost::format(tr("New expiry: Block %d")) % (response[0]["expiration_height"].get<uint64_t>() + blocks) << "\n";
std::cout << std::flush;
if (!confirm_and_send_tx(dsts, ptx_vector, false /*blink*/))
@ -6693,7 +6705,7 @@ bool simple_wallet::ons_update_mapping(std::vector<std::string> args)
SCOPED_WALLET_UNLOCK();
std::string reason;
std::vector<tools::wallet2::pending_tx> ptx_vector;
std::vector<cryptonote::rpc::ONS_NAMES_TO_OWNERS::response_entry> response;
nlohmann::json response;
try
{
ptx_vector = m_wallet->ons_create_update_mapping_tx(type,
@ -6713,7 +6725,7 @@ bool simple_wallet::ons_update_mapping(std::vector<std::string> args)
return true;
}
auto& enc_hex = response[0].encrypted_value;
auto enc_hex = response[0]["encrypted_value"].get<std::string>();
if (!oxenmq::is_hex(enc_hex) || enc_hex.size() % 2 != 0 || enc_hex.size() > 2*ons::mapping_value::BUFFER_SIZE)
{
LOG_ERROR("invalid ONS data returned from oxend");
@ -6756,17 +6768,17 @@ bool simple_wallet::ons_update_mapping(std::vector<std::string> args)
}
if(owner.size()) {
std::cout << boost::format(tr("Old Owner: %s")) % response[0].owner << std::endl;
std::cout << boost::format(tr("Old Owner: %s")) % response[0]["owner"] << std::endl;
std::cout << boost::format(tr("New Owner: %s")) % owner << std::endl;
} else {
std::cout << boost::format(tr("Owner: %s (unchanged)")) % response[0].owner << std::endl;
std::cout << boost::format(tr("Owner: %s (unchanged)")) % response[0]["owner"] << std::endl;
}
if(backup_owner.size()) {
std::cout << boost::format(tr("Old Backup Owner: %s")) % response[0].backup_owner.value_or(NULL_STR) << std::endl;
std::cout << boost::format(tr("Old Backup Owner: %s")) % response[0]["backup_owner"] << std::endl;
std::cout << boost::format(tr("New Backup Owner: %s")) % backup_owner << std::endl;
} else {
std::cout << boost::format(tr("Backup Owner: %s (unchanged)")) % response[0].backup_owner.value_or(NULL_STR) << std::endl;
std::cout << boost::format(tr("Backup Owner: %s (unchanged)")) % response[0]["backup_owner"] << std::endl;
}
if (!confirm_and_send_tx(dsts, ptx_vector, false /*blink*/))
return false;
@ -6938,14 +6950,19 @@ bool simple_wallet::ons_lookup(std::vector<std::string> args)
return true;
}
rpc::ONS_NAMES_TO_OWNERS::request request = {};
nlohmann::json req_params{
{"entries", {}}
};
for (auto& name : args)
{
name = tools::lowercase_ascii_string(std::move(name));
request.entries.push_back({ons::name_to_base64_hash(name), requested_types});
req_params["entries"].emplace_back(nlohmann::json{
{"name_hash", ons::name_to_base64_hash(name)},
{"types", requested_types}
});
}
auto [success, response] = m_wallet->ons_names_to_owners(request);
auto [success, response] = m_wallet->ons_names_to_owners(req_params);
if (!success)
{
fail_msg_writer() << "Connection to daemon failed when requesting ONS owners";
@ -6955,25 +6972,25 @@ bool simple_wallet::ons_lookup(std::vector<std::string> args)
int last_index = -1;
for (auto const &mapping : response)
{
auto& enc_hex = mapping.encrypted_value;
if (mapping.entry_index >= args.size() || !oxenmq::is_hex(enc_hex) || enc_hex.size() % 2 != 0 || enc_hex.size() > 2*ons::mapping_value::BUFFER_SIZE)
auto enc_hex = mapping["encrypted_value"].get<std::string>();
if (mapping["entry_index"].get<uint64_t>() >= args.size() || !oxenmq::is_hex(enc_hex) || enc_hex.size() % 2 != 0 || enc_hex.size() > 2*ons::mapping_value::BUFFER_SIZE)
{
fail_msg_writer() << "Received invalid ONS mapping data from oxend";
return false;
}
// Print any skipped (i.e. not registered) results:
for (size_t i = last_index + 1; i < mapping.entry_index; i++)
for (size_t i = last_index + 1; i < mapping["entry_index"]; i++)
fail_msg_writer() << args[i] << " not found\n";
last_index = mapping.entry_index;
last_index = mapping["entry_index"];
const auto& name = args[mapping.entry_index];
const auto& name = args[mapping["entry_index"]];
ons::mapping_value value{};
value.len = enc_hex.size() / 2;
value.encrypted = true;
oxenmq::from_hex(enc_hex.begin(), enc_hex.end(), value.buffer.begin());
if (!value.decrypt(name, mapping.type))
if (!value.decrypt(name, mapping["type"]))
{
fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_hex;
return false;
@ -6982,15 +6999,15 @@ bool simple_wallet::ons_lookup(std::vector<std::string> args)
auto writer = tools::msg_writer();
writer
<< "Name: " << name
<< "\n Type: " << static_cast<ons::mapping_type>(mapping.type)
<< "\n Value: " << value.to_readable_value(m_wallet->nettype(), mapping.type)
<< "\n Owner: " << mapping.owner;
if (mapping.backup_owner) writer
<< "\n Backup owner: " << *mapping.backup_owner;
<< "\n Type: " << static_cast<ons::mapping_type>(mapping["type"])
<< "\n Value: " << value.to_readable_value(m_wallet->nettype(), mapping["type"])
<< "\n Owner: " << mapping["owner"];
if (auto got = mapping.find("backup_owner"); got != mapping.end()) writer
<< "\n Backup owner: " << mapping["backup_owner"];
writer
<< "\n Last updated height: " << mapping.update_height;
if (mapping.expiration_height) writer
<< "\n Expiration height: " << *mapping.expiration_height;
<< "\n Last updated height: " << mapping["update_height"];
if (auto got = mapping.find("expiration_height"); got != mapping.end()) writer
<< "\n Expiration height: " << mapping["expiration_height"];
writer
<< "\n Encrypted value: " << enc_hex;
writer
@ -6998,9 +7015,9 @@ bool simple_wallet::ons_lookup(std::vector<std::string> args)
tools::wallet2::ons_detail detail =
{
static_cast<ons::mapping_type>(mapping.type),
static_cast<ons::mapping_type>(mapping["type"]),
name,
request.entries[0].name_hash};
req_params[0]["name_hash"]};
m_wallet->set_ons_cache_record(detail);
}
for (size_t i = last_index + 1; i < args.size(); i++)
@ -7014,8 +7031,9 @@ bool simple_wallet::ons_by_owner(const std::vector<std::string>& args)
if (!try_connect_to_daemon())
return false;
std::vector<std::vector<cryptonote::rpc::ONS_OWNERS_TO_NAMES::response_entry>> rpc_results;
std::vector<cryptonote::rpc::ONS_OWNERS_TO_NAMES::request> requests(1);
nlohmann::json req_params{
{"entries", {}}
};
std::unordered_map<std::string, tools::wallet2::ons_detail> cache = m_wallet->get_ons_cache();
@ -7023,10 +7041,7 @@ bool simple_wallet::ons_by_owner(const std::vector<std::string>& args)
{
for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
{
if (requests.back().entries.size() >= cryptonote::rpc::ONS_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}));
req_params["entries"].push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account, index}));
}
}
else
@ -7046,62 +7061,50 @@ bool simple_wallet::ons_by_owner(const std::vector<std::string>& args)
return false;
}
if (requests.back().entries.size() >= cryptonote::rpc::ONS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES)
requests.emplace_back();
requests.back().entries.push_back(arg);
req_params["entries"].push_back(arg);
}
}
rpc_results.reserve(requests.size());
for (auto const &request : requests)
auto [success, result] = m_wallet->ons_owners_to_names(req_params);
if (!success)
{
auto [success, result] = m_wallet->ons_owners_to_names(request);
if (!success)
{
fail_msg_writer() << "Connection to daemon failed when requesting ONS names";
return false;
}
rpc_results.emplace_back(std::move(result));
fail_msg_writer() << "Connection to daemon failed when requesting ONS names";
return false;
}
auto nettype = m_wallet->nettype();
for (size_t i = 0; i < rpc_results.size(); i++)
for (auto const &entry : result["entries"])
{
auto const &rpc = rpc_results[i];
for (auto const &entry : rpc)
std::string_view name;
std::string value;
if (auto got = cache.find(entry["name_hash"]); got != cache.end())
{
std::string_view name;
std::string value;
if (auto got = cache.find(entry.name_hash); got != cache.end())
{
name = got->second.name;
ons::mapping_value mv;
if (ons::mapping_value::validate_encrypted(entry.type, oxenmq::from_hex(entry.encrypted_value), &mv)
&& mv.decrypt(name, entry.type))
value = mv.to_readable_value(nettype, entry.type);
}
auto writer = tools::msg_writer();
writer
<< "Name (hashed): " << entry.name_hash;
if (!name.empty()) writer
<< "\n Name: " << name;
writer
<< "\n Type: " << entry.type;
if (!value.empty()) writer
<< "\n Value: " << value;
writer
<< "\n Owner: " << entry.owner;
if (entry.backup_owner) writer
<< "\n Backup owner: " << *entry.backup_owner;
writer
<< "\n Last updated height: " << entry.update_height;
if (entry.expiration_height) writer
<< "\n Expiration height: " << *entry.expiration_height;
writer
<< "\n Encrypted value: " << entry.encrypted_value;
name = got->second.name;
ons::mapping_value mv;
if (ons::mapping_value::validate_encrypted(static_cast<ons::mapping_type>(entry["type"].get<uint16_t>()), oxenmq::from_hex(entry["encrypted_value"].get<std::string>()), &mv)
&& mv.decrypt(name, static_cast<ons::mapping_type>(entry["type"].get<uint16_t>())))
value = mv.to_readable_value(nettype, static_cast<ons::mapping_type>(entry["type"].get<uint16_t>()));
}
auto writer = tools::msg_writer();
writer
<< "Name (hashed): " << entry["name_hash"];
if (!name.empty()) writer
<< "\n Name: " << name;
writer
<< "\n Type: " << entry["type"];
if (!value.empty()) writer
<< "\n Value: " << value;
writer
<< "\n Owner: " << entry["owner"];
if (auto got = entry.find("backup_owner"); got != entry.end()) writer
<< "\n Backup owner: " << entry["backup_owner"];
writer
<< "\n Last updated height: " << entry["update_height"];
if (auto got = entry.find("expiration_height"); got != entry.end()) writer
<< "\n Expiration height: " << entry["expiration_height"];
writer
<< "\n Encrypted value: " << entry["encrypted_value"];
}
return true;
}
@ -8171,7 +8174,7 @@ bool simple_wallet::check_tx_proof(const std::vector<std::string> &args)
try
{
uint64_t received;
bool in_pool;
bool in_pool = false;
uint64_t confirmations;
if (m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, args.size() == 4 ? args[3] : "", sig_str, received, in_pool, confirmations))
{

View File

@ -80,8 +80,8 @@ bool NodeRPCProxy::get_rpc_version(rpc::version_t &rpc_version) const
if (m_rpc_version == rpc::version_t{0, 0})
{
try {
auto res = invoke_json_rpc<rpc::GET_VERSION>({});
m_rpc_version = rpc::make_version(res.version);
auto res = m_http_client.json_rpc("get_version", {});
m_rpc_version = rpc::make_version(res["version"].get<uint32_t>());
} catch (...) { return false; }
}
rpc_version = m_rpc_version;
@ -103,12 +103,16 @@ bool NodeRPCProxy::get_info() const
if (now >= m_get_info_time + 30s) // re-cache every 30 seconds
{
try {
auto resp_t = invoke_json_rpc<rpc::GET_INFO>({});
auto res = m_http_client.json_rpc("get_info", {});
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;
m_immutable_height = resp_t.immutable_height;
m_height = res["height"].get<uint64_t>();
m_target_height = res["target_height"].get<uint64_t>();
auto it_block_weight_limit = res.find("block_weight_limit");
if (it_block_weight_limit != res.end())
m_block_weight_limit = res["block_weight_limit"];
else
m_block_weight_limit = res["block_size_limit"];
m_immutable_height = res["immutable_height"].get<uint64_t>();
m_get_info_time = now;
m_height_time = now;
} catch (...) { return false; }
@ -157,13 +161,14 @@ bool NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_heigh
return false;
if (m_earliest_height[version] == 0)
{
rpc::HARD_FORK_INFO::request req_t{};
req_t.version = version;
nlohmann::json req_params{
{"version", version}
};
try {
auto resp_t = invoke_json_rpc<rpc::HARD_FORK_INFO>(req_t);
if (!resp_t.earliest_height)
auto res = m_http_client.json_rpc("hard_fork_info", req_params);
if (!res["earliest_height"])
return false;
m_earliest_height[version] = *resp_t.earliest_height;
m_earliest_height[version] = res["earliest_height"].get<uint64_t>();
} catch (...) { return false; }
}
@ -177,7 +182,8 @@ std::optional<uint8_t> NodeRPCProxy::get_hardfork_version() const
return std::nullopt;
try {
return invoke_json_rpc<rpc::HARD_FORK_INFO>({}).version;
auto res = m_http_client.json_rpc("hard_fork_info", {});
return res["version"].get<uint8_t>();
} catch (...) {}
return std::nullopt;
@ -191,14 +197,15 @@ bool NodeRPCProxy::refresh_dynamic_base_fee_cache(uint64_t grace_blocks) const
if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks)
{
rpc::GET_BASE_FEE_ESTIMATE::request req_t{};
req_t.grace_blocks = grace_blocks;
nlohmann::json req_params{
{"grace_blocks", grace_blocks}
};
try {
auto resp_t = invoke_json_rpc<rpc::GET_BASE_FEE_ESTIMATE>(req_t);
m_dynamic_base_fee_estimate = {resp_t.fee_per_byte, resp_t.fee_per_output};
auto res = m_http_client.json_rpc("get_base_fee_estimate", req_params);
m_dynamic_base_fee_estimate = {res["fee_per_byte"].get<uint64_t>(), res["fee_per_output"].get<uint64_t>()};
m_dynamic_base_fee_estimate_cached_height = height;
m_dynamic_base_fee_estimate_grace_blocks = grace_blocks;
m_fee_quantization_mask = resp_t.quantization_mask;
m_fee_quantization_mask = res["quantization_mask"].get<uint64_t>();
} catch (...) { return false; }
}
return true;
@ -226,11 +233,22 @@ bool NodeRPCProxy::get_fee_quantization_mask(uint64_t &fee_quantization_mask) co
return true;
}
std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry>> NodeRPCProxy::get_service_nodes(std::vector<std::string> pubkeys) const
std::pair<bool, nlohmann::json> NodeRPCProxy::get_service_nodes(std::vector<std::string> pubkeys) const
{
rpc::GET_SERVICE_NODES::request req{};
req.service_node_pubkeys = std::move(pubkeys);
return get_result_pair<rpc::GET_SERVICE_NODES>(req, [](auto&& res) { return std::move(res.service_node_states); });
std::pair<bool, nlohmann::json> result;
auto& [success, resolved] = result;
success = false;
nlohmann::json req_params{
{"service_node_pubkeys", pubkeys}
};
try {
auto res = m_http_client.json_rpc("get_service_nodes", pubkeys);
resolved = res["service_node_states"];
} catch (...) {
return result;
}
success = true;
return result;
}
// Updates the cache of all service nodes; the mutex lock must be already held
@ -239,18 +257,18 @@ bool NodeRPCProxy::update_all_service_nodes_cache(uint64_t height) const {
return false;
try {
auto res = invoke_json_rpc<rpc::GET_SERVICE_NODES>({});
auto res = m_http_client.json_rpc("get_service_nodes", {});
m_all_service_nodes_cached_height = height;
m_all_service_nodes = std::move(res.service_node_states);
m_all_service_nodes = std::move(res["service_node_states"]);
} catch (...) { return false; }
return true;
}
std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry>> NodeRPCProxy::get_all_service_nodes() const
std::pair<bool, nlohmann::json> NodeRPCProxy::get_all_service_nodes() const
{
std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry>> result;
std::pair<bool, nlohmann::json> result;
auto& [success, sns] = result;
success = false;
@ -272,9 +290,9 @@ std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry>
// 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::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry>> NodeRPCProxy::get_contributed_service_nodes(const std::string &contributor) const
std::pair<bool, nlohmann::json> NodeRPCProxy::get_contributed_service_nodes(const std::string &contributor) const
{
std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry>> result;
std::pair<bool, nlohmann::json> result;
auto& [success, sns] = result;
success = false;
@ -292,8 +310,8 @@ std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry>
std::copy_if(m_all_service_nodes.begin(), m_all_service_nodes.end(), std::back_inserter(m_contributed_service_nodes),
[&contributor](const auto& sn)
{
return std::any_of(sn.contributors.begin(), sn.contributors.end(),
[&contributor](const auto& c) { return contributor == c.address; });
return std::any_of(sn["contributors"].begin(), sn["contributors"].end(),
[&contributor](const nlohmann::json& c) { return contributor == c["address"].get<std::string>(); });
}
);
m_contributed_service_nodes_cached_height = height;
@ -307,9 +325,9 @@ std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry>
return result;
}
std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry>> NodeRPCProxy::get_service_node_blacklisted_key_images() const
std::pair<bool, nlohmann::json> NodeRPCProxy::get_service_node_blacklisted_key_images() const
{
std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry>> result;
std::pair<bool, nlohmann::json> result;
auto& [success, sns] = result;
success = false;
@ -322,9 +340,9 @@ std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IM
if (m_service_node_blacklisted_key_images_cached_height != height)
{
try {
auto res = invoke_json_rpc<rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES>({});
auto res = m_http_client.json_rpc("get_service_nod_blacklisted_key_images", {});
m_service_node_blacklisted_key_images_cached_height = height;
m_service_node_blacklisted_key_images = std::move(res.blacklist);
m_service_node_blacklisted_key_images = std::move(res["blacklist"]);
} catch (...) {
return result;
}
@ -337,28 +355,55 @@ std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IM
return result;
}
std::pair<bool, std::vector<cryptonote::rpc::ONS_OWNERS_TO_NAMES::response_entry>> NodeRPCProxy::ons_owners_to_names(cryptonote::rpc::ONS_OWNERS_TO_NAMES::request const &request) const
std::pair<bool, nlohmann::json> NodeRPCProxy::ons_owners_to_names(nlohmann::json const &request) const
{
return get_result_pair<rpc::ONS_OWNERS_TO_NAMES>(request, [](auto&& res) { return std::move(res.entries); });
}
std::pair<bool, std::vector<cryptonote::rpc::ONS_NAMES_TO_OWNERS::response_entry>> NodeRPCProxy::ons_names_to_owners(cryptonote::rpc::ONS_NAMES_TO_OWNERS::request const &request) const
{
return get_result_pair<rpc::ONS_NAMES_TO_OWNERS>(request, [](auto&& res) { return std::move(res.entries); });
}
std::pair<bool,cryptonote::rpc::ONS_RESOLVE::response> NodeRPCProxy::ons_resolve(cryptonote::rpc::ONS_RESOLVE::request const &request) const
{
std::pair<bool, cryptonote::rpc::ONS_RESOLVE::response> result;
std::pair<bool, nlohmann::json> result;
auto& [success, resolved] = result;
success = false;
uint64_t height;
if (m_offline || !get_height(height))
if (m_offline || !get_info())
return result;
try {
auto res = m_http_client.json_rpc("ons_owners_to_names", request);
resolved = res;
} catch (...) {
return result;
}
success = true;
return result;
}
std::pair<bool, nlohmann::json> NodeRPCProxy::ons_names_to_owners(nlohmann::json const& request) const
{
std::pair<bool, nlohmann::json> result;
auto& [success, resolved] = result;
success = false;
if (m_offline || !get_info())
return result;
try {
auto res = m_http_client.json_rpc("get_output_histogram", request);
resolved = res;
} catch (...) {
return result;
}
success = true;
return result;
}
std::pair<bool, nlohmann::json> NodeRPCProxy::ons_resolve(nlohmann::json const& request) const
{
std::pair<bool, nlohmann::json> result;
auto& [success, resolved] = result;
success = false;
if (m_offline || !get_info())
return result;
{
try {
auto res = m_http_client.json_rpc<rpc::ONS_RESOLVE>(rpc::ONS_RESOLVE::names().front(), request);
auto res = m_http_client.json_rpc("ons_resolve", request);
resolved = res;
} catch (...) {
return result;

View File

@ -60,11 +60,10 @@ public:
std::pair<bool, nlohmann::json> get_service_nodes(std::vector<std::string> pubkeys) const;
std::pair<bool, nlohmann::json> get_all_service_nodes() const;
std::pair<bool, nlohmann::json> get_contributed_service_nodes(const std::string& contributor) const;
std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry>> get_service_node_blacklisted_key_images() const;
std::pair<bool, std::vector<cryptonote::rpc::ONS_OWNERS_TO_NAMES::response_entry>> ons_owners_to_names(cryptonote::rpc::ONS_OWNERS_TO_NAMES::request const &request) const;
std::pair<bool, std::vector<cryptonote::rpc::ONS_NAMES_TO_OWNERS::response_entry>> ons_names_to_owners(cryptonote::rpc::ONS_NAMES_TO_OWNERS::request const &request) const;
std::pair<bool, nlohmann::json>
ons_resolve(nlohmann::json const &request) const;
std::pair<bool, nlohmann::json> get_service_node_blacklisted_key_images() const;
std::pair<bool, nlohmann::json> ons_owners_to_names(nlohmann::json const &request) const;
std::pair<bool, nlohmann::json> ons_names_to_owners(nlohmann::json const &request) const;
std::pair<bool, nlohmann::json> ons_resolve(nlohmann::json const &request) const;
private:
bool get_info() const;
@ -118,7 +117,7 @@ private:
bool m_offline;
mutable uint64_t m_service_node_blacklisted_key_images_cached_height;
mutable std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> m_service_node_blacklisted_key_images;
mutable nlohmann::json m_service_node_blacklisted_key_images;
bool update_all_service_nodes_cache(uint64_t height) const;

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,7 @@
#include "cryptonote_basic/account_boost_serialization.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "rpc/core_rpc_server_binary_commands.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "cryptonote_core/oxen_name_system.h"
@ -837,10 +838,10 @@ private:
auto get_all_service_nodes() const { return m_node_rpc_proxy.get_all_service_nodes(); }
auto get_service_nodes(std::vector<std::string> const &pubkeys) const { return m_node_rpc_proxy.get_service_nodes(pubkeys); }
auto get_service_node_blacklisted_key_images() const { return m_node_rpc_proxy.get_service_node_blacklisted_key_images(); }
nlohmann::json list_current_stakes();
auto ons_owners_to_names(cryptonote::rpc::ONS_OWNERS_TO_NAMES::request const &request) const { return m_node_rpc_proxy.ons_owners_to_names(request); }
auto ons_owners_to_names(nlohmann::json const &request) const { return m_node_rpc_proxy.ons_owners_to_names(request); }
auto ons_names_to_owners(nlohmann::json const &request) const { return m_node_rpc_proxy.ons_names_to_owners(request); }
auto resolve(nlohmann::json const &request) const { return m_node_rpc_proxy.ons_resolve(request); }
nlohmann::json list_current_stakes();
struct ons_detail
{
@ -1337,6 +1338,11 @@ private:
return false;
}
nlohmann::json json_rpc(std::string command, nlohmann::json params)
{
return m_http_client.json_rpc(command, params);
}
bool set_ring_database(fs::path filename);
const fs::path& get_ring_database() const { return m_ring_database; }
bool get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs);
@ -1432,10 +1438,10 @@ 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<pending_tx> ons_create_update_mapping_tx(ons::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::ONS_NAMES_TO_OWNERS::response_entry> *response = {});
std::vector<pending_tx> ons_create_update_mapping_tx(ons::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 = {}, nlohmann::json *response = {});
// ONS renewal (for lokinet registrations, not for session/wallet)
std::vector<pending_tx> ons_create_renewal_tx(ons::mapping_type type, std::string name, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {}, std::vector<cryptonote::rpc::ONS_NAMES_TO_OWNERS::response_entry> *response = {});
std::vector<pending_tx> ons_create_renewal_tx(ons::mapping_type type, std::string name, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {}, nlohmann::json *response = {});
// Generate just the signature required for putting into ons_update_mapping command in the wallet
bool ons_make_update_mapping_signature(ons::mapping_type type, std::string name, std::string const *value, std::string const *owner, std::string const *backup_owner, ons::generic_signature &signature, uint32_t account_index = 0, std::string *reason = nullptr);
@ -1479,17 +1485,6 @@ private:
std::atomic<bool> m_long_poll_disabled;
static std::string get_default_daemon_address();
/// Requests transactions from daemon given hex strings of the tx ids; throws a wallet exception
/// on error, otherwise returns the response.
cryptonote::rpc::GET_TRANSACTIONS::response request_transactions(std::vector<std::string> txids_hex);
/// Requests transactions from daemon given a vector of crypto::hash. Throws a wallet exception
/// on error, otherwise returns the response.
cryptonote::rpc::GET_TRANSACTIONS::response request_transactions(const std::vector<crypto::hash>& txids);
/// Same as above, but for a single transaction.
cryptonote::rpc::GET_TRANSACTIONS::response request_transaction(const crypto::hash& txid) { return request_transactions(std::vector<crypto::hash>{{txid}}); }
// The wallet's RPC client; public for advanced configuration purposes.
cryptonote::rpc::http_client m_http_client;
@ -1522,7 +1517,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::rpc::GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t &current_height);
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_BIN::block_output_indices>& o_indices, uint64_t& current_height);
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 &last, bool &error, std::exception_ptr &exception);

View File

@ -221,10 +221,12 @@ namespace tools
epee::serialization::portable_storage ps;
if(!ps.load_from_json(body))
return jsonrpc_error_response(res, -32700, "Parse error");
return jsonrpc_error_response(res, -32700, "Parse error", {});
epee::serialization::storage_entry id{std::string{}};
ps.get_value("id", id, nullptr);
epee::serialization::storage_entry epee_id{std::string{}};
ps.get_value("id", epee_id, nullptr);
nlohmann::json id = var::get<std::string>(epee_id);
std::string method;
if(!ps.get_value("method", method, nullptr))
@ -246,7 +248,7 @@ namespace tools
// If it's a restricted command and we're in restricted mode then deny it
if (restricted && m_restricted) {
MWARNING("JSON RPC request for restricted command " << method << " in restricted mode from " << get_remote_address(res));
return jsonrpc_error_response(res, error_code::DENIED, method + " is not available in restricted mode.");
return jsonrpc_error_response(res, error_code::DENIED, method + " is not available in restricted mode.", {});
}
// Try to load "params" into a generic epee value; if it fails (because there is no "params")
@ -259,7 +261,7 @@ namespace tools
wallet_rpc_error json_error{-32603, "Internal error"};
try {
result = invoke_ptr(ps, std::move(id), std::move(params), *this);
result = invoke_ptr(ps, std::move(epee_id), std::move(params), *this);
json_error.code = 0;
} catch (const parse_error& e) {
json_error = {-32602, "Invalid params"}; // Reserved json code/message value for specifically this failure
@ -302,7 +304,7 @@ namespace tools
}
if (json_error.code != 0)
return jsonrpc_error_response(res, json_error.code, std::move(json_error.message));
return jsonrpc_error_response(res, json_error.code, std::move(json_error.message), {});
res.writeHeader("Server", server_header());
res.writeHeader("Content-Type", "application/json");
@ -2376,24 +2378,35 @@ namespace tools
if (req.threads_count < 1 || max_mining_threads_count < req.threads_count)
throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "The specified number of threads is inappropriate."};
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;
rpc::START_MINING::response daemon_res{};
bool r = m_wallet->invoke_http<rpc::START_MINING>(daemon_req, daemon_res);
if (!r || daemon_res.status != rpc::STATUS_OK)
nlohmann::json req_params{
{"miner_address", m_wallet->get_account().get_public_address_str(m_wallet->nettype())},
{"threads_count", req.threads_count}
};
try
{
nlohmann::json res = m_wallet->json_rpc("start_mining", req_params);
if (res["status"] != rpc::STATUS_OK)
throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Couldn't start mining due to unknown error."};
}
catch (...) {
throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Couldn't start mining due to unknown error."};
}
return {};
}
//------------------------------------------------------------------------------------------------------------------------------
STOP_MINING::response wallet_rpc_server::invoke(STOP_MINING::request&& req)
{
require_open();
rpc::STOP_MINING::response daemon_res{};
bool r = m_wallet->invoke_http<rpc::STOP_MINING>({}, daemon_res);
if (!r || daemon_res.status != rpc::STATUS_OK)
try
{
nlohmann::json res = m_wallet->json_rpc("stop_mining", {});
if (res["status"] != rpc::STATUS_OK)
throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Couldn't stop mining due to unknown error."};
}
catch (...) {
throw wallet_rpc_error{error_code::UNKNOWN_ERROR, "Couldn't stop mining due to unknown error."};
}
return {};
}
//------------------------------------------------------------------------------------------------------------------------------
@ -2463,12 +2476,15 @@ namespace {
if (!req.hardware_wallet)
wal->set_seed_language(req.language);
rpc::GET_HEIGHT::request hreq{};
rpc::GET_HEIGHT::response hres{};
hres.height = 0;
bool r = wal->invoke_http<rpc::GET_HEIGHT>(hreq, hres);
if (r)
wal->set_refresh_from_block_height(hres.height);
nlohmann::json req_params{
{"height", 0}
};
try
{
nlohmann::json res = wal->json_rpc("get_height", req_params);
wal->set_refresh_from_block_height(res["height"].get<uint64_t>());
}
catch (...) {}
if (req.hardware_wallet)
wal->restore_from_device(wallet_file, req.password, req.device_name.empty() ? "Ledger" : req.device_name);
@ -3292,8 +3308,10 @@ namespace {
}
auto nettype = m_wallet->nettype();
rpc::ONS_NAMES_TO_OWNERS::request lookup_req{};
lookup_req.include_expired = req.include_expired;
nlohmann::json req_params{
{"include_expired", req.include_expired },
{"entries", {}}
};
uint64_t curr_height = req.include_expired ? m_wallet->get_blockchain_current_height() : 0;
@ -3304,40 +3322,39 @@ namespace {
const auto end = num_entries < rpc::ONS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES
? res.known_names.end()
: it + rpc::ONS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES;
lookup_req.entries.clear();
lookup_req.entries.reserve(std::distance(it, end));
for (auto it2 = it; it2 != end; it2++)
{
auto& e = lookup_req.entries.emplace_back();
e.name_hash = it2->hashed;
e.types.push_back(static_cast<uint16_t>(entry_types[std::distance(res.known_names.begin(), it2)]));
auto& e = req_params["entries"].emplace_back(nlohmann::json{
{"name_hash", it2->hashed},
{"types", static_cast<uint16_t>(entry_types[std::distance(res.known_names.begin(), it2)])}
});
}
if (auto [success, records] = m_wallet->ons_names_to_owners(lookup_req); success)
if (auto [success, records] = m_wallet->ons_names_to_owners(req_params); success)
{
size_t type_offset = std::distance(res.known_names.begin(), it);
for (auto& rec : records)
{
if (rec.entry_index >= num_entries)
if (rec["entry_index"].get<size_t>() >= num_entries)
{
MWARNING("Got back invalid entry_index " << rec.entry_index << " for a request for " << num_entries << " entries");
MWARNING("Got back invalid entry_index " << rec["entry_index"] << " for a request for " << num_entries << " entries");
continue;
}
auto& res_e = *(it + rec.entry_index);
res_e.owner = std::move(rec.owner);
res_e.backup_owner = std::move(rec.backup_owner);
res_e.encrypted_value = std::move(rec.encrypted_value);
res_e.update_height = rec.update_height;
res_e.expiration_height = rec.expiration_height;
auto& res_e = *(it + rec["entry_index"].get<int64_t>());
res_e.owner = std::move(rec["owner"]);
res_e.backup_owner = std::move(rec["backup_owner"]);
res_e.encrypted_value = std::move(rec["encrypted_value"]);
res_e.update_height = rec["update_height"];
res_e.expiration_height = rec["expiration_height"];
if (req.include_expired && res_e.expiration_height)
res_e.expired = *res_e.expiration_height < curr_height;
res_e.txid = std::move(rec.txid);
res_e.txid = std::move(rec["txid"]);
if (req.decrypt && !res_e.encrypted_value.empty() && oxenmq::is_hex(res_e.encrypted_value))
{
ons::mapping_value value;
const auto type = entry_types[type_offset + rec.entry_index];
const auto type = entry_types[type_offset + rec["entry_index"].get<int64_t>()];
std::string errmsg;
if (ons::mapping_value::validate_encrypted(type, oxenmq::from_hex(res_e.encrypted_value), &value, &errmsg)
&& value.decrypt(res_e.name, type))