wallet RPC: make lns_known_names return record with optional decryption

This saves the GUI wallet, in particular, a lot of work: previously it
had to get the values from loki-wallet-rpc, then make a request to lokid
to retrieve the records, then for each of those make another call to the
wallet-rpc to decrypt the value.

This lets lns_known_names do it all in one request.
This commit is contained in:
Jason Rhinelander 2020-10-08 04:54:34 -03:00
parent d3a7a9cef7
commit e6c2cdaefa
3 changed files with 109 additions and 9 deletions

View File

@ -3245,12 +3245,14 @@ namespace {
require_open();
LNS_KNOWN_NAMES::response res{};
std::vector<lns::mapping_type> entry_types;
auto cache = m_wallet->get_lns_cache();
res.known_names.reserve(cache.size());
entry_types.reserve(cache.size());
for (auto& [name, details] : m_wallet->get_lns_cache())
{
auto& entry = res.known_names.emplace_back();
auto type = details.type;
auto& type = entry_types.emplace_back(details.type);
if (type > lns::mapping_type::lokinet && type <= lns::mapping_type::lokinet_10years)
type = lns::mapping_type::lokinet;
entry.type = lns::mapping_type_str(type);
@ -3258,6 +3260,75 @@ namespace {
entry.name = details.name;
}
auto nettype = m_wallet->nettype();
rpc::LNS_NAMES_TO_OWNERS::request lookup_req{};
lookup_req.include_expired = req.include_expired;
uint64_t curr_height = req.include_expired ? m_wallet->get_blockchain_current_height() : 0;
// Query lokid for the full record info
for (auto it = res.known_names.begin(); it != res.known_names.end(); )
{
const size_t num_entries = std::distance(it, res.known_names.end());
const auto end = num_entries < rpc::LNS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES
? res.known_names.end()
: it + rpc::LNS_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)]));
}
if (auto [success, records] = m_wallet->lns_names_to_owners(lookup_req); success)
{
size_t type_offset = std::distance(res.known_names.begin(), it);
for (auto& rec : records)
{
if (rec.entry_index >= num_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;
if (req.include_expired && res_e.expiration_height)
res_e.expired = *res_e.expiration_height < curr_height;
res_e.txid = std::move(rec.txid);
if (req.decrypt && !res_e.encrypted_value.empty() && lokimq::is_hex(res_e.encrypted_value))
{
lns::mapping_value value;
const auto type = entry_types[type_offset + rec.entry_index];
std::string errmsg;
if (lns::mapping_value::validate_encrypted(type, lokimq::from_hex(res_e.encrypted_value), &value, &errmsg)
&& value.decrypt(res_e.name, type))
res_e.value = value.to_readable_value(nettype, type);
else
MWARNING("Failed to decrypt LNS value for " << res_e.name << (errmsg.empty() ? ""s : ": " + errmsg));
}
}
}
it = end;
}
// Erase anything we didn't get a response for (it will have update_height of 0)
res.known_names.erase(std::remove_if(res.known_names.begin(), res.known_names.end(),
[](const auto& n) { return n.update_height == 0; }),
res.known_names.end());
// Now sort whatever we got back
std::sort(res.known_names.begin(), res.known_names.end(),
[](const auto& a, const auto& b) { return std::make_pair(a.name, a.type) < std::make_pair(b.name, b.type); });
return res;
}

View File

@ -1209,10 +1209,24 @@ KV_SERIALIZE_MAP_CODE_BEGIN(LNS_HASH_NAME::response)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_KNOWN_NAMES::known_name)
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_KNOWN_NAMES::request)
KV_SERIALIZE(decrypt)
KV_SERIALIZE(include_expired)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_KNOWN_NAMES::known_record)
KV_SERIALIZE(type)
KV_SERIALIZE(hashed)
KV_SERIALIZE(name)
KV_SERIALIZE(owner)
KV_SERIALIZE(backup_owner)
KV_SERIALIZE(encrypted_value)
KV_SERIALIZE(value)
KV_SERIALIZE(update_height)
KV_SERIALIZE(expiration_height)
KV_SERIALIZE(expired)
KV_SERIALIZE(txid)
KV_SERIALIZE_MAP_CODE_END()

View File

@ -2361,24 +2361,39 @@ This command is only required if the open wallet is one of the owners of a LNS r
};
LOKI_RPC_DOC_INTROSPECT
// Returns a list of known, plain-text LNS names that this wallet knows about.
// Returns a list of known, plain-text LNS names along with record details for names that this
// wallet knows about. This can optionally decrypt the LNS value as well, or else just return the
// encrypted value.
struct LNS_KNOWN_NAMES : RPC_COMMAND
{
static constexpr auto names() { return NAMES("lns_known_names"); }
struct known_name
struct known_record
{
std::string type; // The mapping type, "session" or "lokinet".
std::string hashed; // The hashed name (in base64)
std::string name; // The plaintext name
std::string type; // The mapping type, "session" or "lokinet".
std::string hashed; // The hashed name (in base64)
std::string name; // The plaintext name
std::string owner; // The public key that purchased the Loki Name Service entry.
std::optional<std::string> backup_owner; // The backup public key or wallet that the owner specified when purchasing the Loki Name Service entry. Omitted if no backup owner.
std::string encrypted_value; // The encrypted value that the name maps to, in hex.
std::optional<std::string> value; // Decrypted value that that name maps to. Only provided if `decrypt: true` was specified in the request.
uint64_t update_height; // The last height that this Loki Name Service entry was updated on the Blockchain.
std::optional<uint64_t> expiration_height; // For records that expire, this will be set to the expiration block height.
std::optional<bool> expired; // Indicates whether the record has expired. Only included in the response if "include_expired" is specified in the request.
std::string txid; // The txid of the mapping's most recent update or purchase.
KV_MAP_SERIALIZABLE
};
struct request {
bool decrypt; // If true (default false) then also decrypt and include the `value` field
bool include_expired; // If true (default false) then also include expired records
KV_MAP_SERIALIZABLE
};
struct request : EMPTY {};
struct response
{
std::vector<known_name> known_names; // List of (unhashed) name info known to this wallet
std::vector<known_record> known_names; // List of records known to this wallet
KV_MAP_SERIALIZABLE
};