Lokinet LNS

Revamps how .loki LNS registrations work:

- Enable lokinet registrations beginning at HF16.

- rework renewal so that you can renew at any time and it simply adds to the end of the current
  expiry.  Previously there was only a window in which you could renew.

- Renewals are a new type of LNS transaction, distinct from buys and updates.  (Internally it is an
  update with no fields, which cannot be produced in the existing code).

- Add optional "type=" parameter to lns commands.  Commands default to trying to auto-detect (i.e.
  if the name ends with .loki it is lokinet), but the type allows you to be explicit *and* allows
  select non-default registration lengths for lokinet buys/renewals.

- change .loki naming requirements: we now require <= 32 chars if it doesn't contain a -, and 63 if
  it does.  We also reserve names starting "??--" for any ?? other than xn-- (punycode), as this is
  a DNS restriction.  "loki.loki" and "snode.loki" are also now reserved (just in case someone
  sticks .loki as a DNS search domain).

- Tweak LNS registration times to consider "a year" to be 368 days worth of blocks (to allow for
  leap years and some minor block time drift).

- Overhaul how LNS registrations are displayed in the cli wallet.  For example:

    [wallet L6QPcD]: lns_print_name_to_owners jasonv.loki jason.loki jasonz.loki
    Error: jasonv.loki not found

    Name: jason.loki
        Type: lokinet
        Value: azfoj73snr9f3neh5c6sf7rtbaeabyxhr1m4un5aydsmsrxo964o.loki
        Owner: L6QPcDVp6Fu7HwtXrXjtfvWvgBPvvMQ9FiyquMWn2BBEDsk2vydwu1A3BrK2uQcCo94G7HA5xiKvpZ4CMQva6pxW2GXkCG9
        Last updated height: 46
        Expiration height: 75
        Encrypted value: 870e42cd172a(snip)

    Error: jasonz.loki not found

- Add an RPC end-point to do simple LNS resolution; you can get the same info out of
  names-to-owners, but the new lns_resolve end-point is considerably simpler for doing simple
  lookups (e.g. for lokinet), and allows for a simpler SQL query + processing.

Code changes:

- Rename mapping_type::lokinet_1year to mapping_type::lokinet (see next point).

- Don't store lokinet_2y, etc. in the database, but instead always store as type=2/::lokinet.  The
  LNS extra data can still specify different lengths, but this now just affects the
  expiration_height value that we set.

- Reworked some binding code to use std::variant's and add a bind_container() to simplify passing in
  a variable list of bind parameters of different types.

- Accept both base64 and hex inputs for binary LNS parameters in the RPC interface.

- This commit adds some (incomplete) expiry adjustment code, but ignore it as it all gets replaced
  with the following commit to overhaul record updating.

- Updated a bunch of test suite code, mainly related to lokinet.

- Some random C++17 niceties (string_view, variant, structured binding returns) in the related code.

- Tweaked the test suite to generate a bit fewer blocks in some cases where we just need to
  confirm/unlock a transfers rather than a coinbase tx.
This commit is contained in:
Jason Rhinelander 2020-09-14 23:52:03 -03:00
parent 6d81e03bf9
commit 30480bedee
22 changed files with 1554 additions and 816 deletions

View File

@ -467,8 +467,13 @@ namespace cryptonote
bool field_is_set (lns::extra_field bit) const { return (fields & bit) == bit; }
bool field_any_set(lns::extra_field bit) const { return (fields & bit) != lns::extra_field::none; }
// True if this is updating some LNS info: has a signature and 1 or more updating field
bool is_updating() const { return field_is_set(lns::extra_field::signature) && field_any_set(lns::extra_field::updatable_fields); }
// True if this is buying a new LNS record
bool is_buying() const { return (fields == lns::extra_field::buy || fields == lns::extra_field::buy_no_backup); }
// True if this is renewing an existing LNS: has no fields at all, is a renewal registration (i.e. lokinet),
// and has a non-null txid set (which should point to the most recent registration or update).
bool is_renewing() const { return fields == lns::extra_field::none && prev_txid && is_lokinet_type(type); }
static tx_extra_loki_name_system make_buy(lns::generic_owner const &owner, lns::generic_owner const *backup_owner, lns::mapping_type type, crypto::hash const &name_hash, std::string const &encrypted_value, crypto::hash const &prev_txid)
{
@ -488,6 +493,18 @@ namespace cryptonote
return result;
}
static tx_extra_loki_name_system make_renew(lns::mapping_type type, crypto::hash const &name_hash, crypto::hash const &prev_txid)
{
assert(is_lokinet_type(type) && prev_txid);
tx_extra_loki_name_system result{};
result.fields = lns::extra_field::none;
result.type = type;
result.name_hash = name_hash;
result.prev_txid = prev_txid;
return result;
}
static tx_extra_loki_name_system make_update(lns::generic_signature const &signature,
lns::mapping_type type,
crypto::hash const &name_hash,

View File

@ -3479,7 +3479,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{
cryptonote::tx_extra_loki_name_system data;
std::string fail_reason;
if (!m_lns_db.validate_lns_tx(hf_version, get_current_blockchain_height(), tx, &data, &fail_reason))
if (!m_lns_db.validate_lns_tx(hf_version, get_current_blockchain_height(), tx, data, &fail_reason))
{
MERROR_VER("Failed to validate LNS TX reason: " << fail_reason);
tvc.m_verbose_error = std::move(fail_reason);

File diff suppressed because it is too large Load Diff

View File

@ -25,13 +25,18 @@ class Blockchain;
namespace lns
{
constexpr size_t WALLET_NAME_MAX = 96;
constexpr size_t WALLET_NAME_MAX = 97; // mainnet addresses are 95 but testnet/devnet are 97
constexpr size_t WALLET_ACCOUNT_BINARY_LENGTH = 2 * sizeof(crypto::public_key);
constexpr size_t LOKINET_DOMAIN_NAME_MAX = 253;
constexpr size_t LOKINET_DOMAIN_NAME_MAX = 63 + 5; // DNS components name must be at most 63 (+ 5 for .loki); this limit applies if there is at least one hyphen (and thus includes punycode)
constexpr size_t LOKINET_DOMAIN_NAME_MAX_NOHYPHEN = 32 + 5; // If the name does not contain a - then we restrict it to 32 characters so that it cannot be (and is obviously not) an encoded .loki address (52 characters)
constexpr size_t LOKINET_ADDRESS_BINARY_LENGTH = sizeof(crypto::ed25519_public_key);
constexpr size_t SESSION_DISPLAY_NAME_MAX = 64;
constexpr size_t SESSION_PUBLIC_KEY_BINARY_LENGTH = 1 + sizeof(crypto::ed25519_public_key); // Session keys at prefixed with 0x05 + ed25519 key
constexpr size_t NAME_HASH_SIZE = sizeof(crypto::hash);
constexpr size_t NAME_HASH_SIZE_B64_MIN = (4*NAME_HASH_SIZE + 2) / 3; // No padding
constexpr size_t NAME_HASH_SIZE_B64_MAX = (NAME_HASH_SIZE + 2) / 3 * 4; // With padding
constexpr size_t SODIUM_ENCRYPTION_EXTRA_BYTES = 40; // crypto_aead_xchacha20poly1305_ietf_ABYTES (16) + crypto_aead_xchacha20poly1305_ietf_NPUBBYTES (24), but we don't include sodium here
struct mapping_value
@ -65,11 +70,11 @@ struct mapping_value
//
// If the value is *already* encrypted this fails via assert (in debug compilation) or returns
// false.
//
// Note that, because encryption uses a random nonce, encrypting the same plaintext value multiple
// times will result in different encrypted strings.
bool encrypt(std::string_view name, const crypto::hash* name_hash = nullptr, bool deprecated_heavy = false);
// Makes a copy of *this, calls encrypt() on it, and returns it. See encrypt() above.
mapping_value make_encrypted(std::string_view name, const crypto::hash* name_hash = nullptr, bool deprecated_heavy = false) const;
// Decrypts the mapping value given the name and mapping type. If the name hash is pre-computed
// it can be passed in.
//
@ -79,7 +84,12 @@ struct mapping_value
// false.
bool decrypt(std::string_view name, mapping_type type, const crypto::hash* name_hash = nullptr);
// Makes a copy of *this, calls decrypt() on it, and returns it. See decrypt() above.
// Makes a copy of *this, calls encrypt() on it, and returns it. Unlike encrypt(), this call
// leaves `*this` unencrypted and instead returns an encrypted copy.
mapping_value make_encrypted(std::string_view name, const crypto::hash* name_hash = nullptr, bool deprecated_heavy = false) const;
// Makes a copy of *this, calls decrypt() on it, and returns it. Unlike decrypt(), this call
// leaves `*this` encrypted and instead returns an decrypted copy.
mapping_value make_decrypted(std::string_view name, const crypto::hash* name_hash = nullptr) const;
// Validate a human readable mapping value representation in 'value' and write the binary form into 'blob'.
@ -98,52 +108,74 @@ inline char const *mapping_type_str(mapping_type type)
{
switch(type)
{
case mapping_type::lokinet_1year: return "lokinet_1year";
case mapping_type::lokinet_2years: return "lokinet_2years";
case mapping_type::lokinet_5years: return "lokinet_5years";
case mapping_type::lokinet_10years: return "lokinet_10years";
case mapping_type::lokinet: return "lokinet"; // general type stored in the database; 1 year when in a purchase tx
case mapping_type::lokinet_2years: return "lokinet_2years"; // Only used in a buy tx, not in the DB
case mapping_type::lokinet_5years: return "lokinet_5years"; // "
case mapping_type::lokinet_10years: return "lokinet_10years"; // "
case mapping_type::session: return "session";
case mapping_type::wallet: return "wallet";
default: assert(false); return "xx_unhandled_type";
}
}
inline std::ostream &operator<<(std::ostream &os, mapping_type type) { return os << mapping_type_str(type); }
constexpr bool mapping_type_allowed(uint8_t hf_version, mapping_type type) { return type == mapping_type::session; }
constexpr bool is_lokinet_type (lns::mapping_type type) { return type >= mapping_type::lokinet_1year && type <= mapping_type::lokinet_10years; }
sqlite3 *init_loki_name_system(char const *file_path, bool read_only);
uint64_t constexpr NO_EXPIRY = static_cast<uint64_t>(-1);
// return: The number of blocks until expiry from the registration height, if there is no expiration NO_EXPIRY is returned.
uint64_t expiry_blocks(cryptonote::network_type nettype, mapping_type type, uint64_t *renew_window = nullptr);
bool validate_lns_name(mapping_type type, std::string name, std::string *reason = nullptr);
constexpr bool mapping_type_allowed(uint8_t hf_version, mapping_type type) {
return (type == mapping_type::session && hf_version >= cryptonote::network_version_15_lns)
|| (is_lokinet_type(type) && hf_version >= cryptonote::network_version_16);
}
// Returns all mapping types supported for lookup as of the given hardfork. (Note that this does
// not return the dedicated length types such as mapping_type::lokinet_5years as those are only
// relevant within a LNS buy tx).
std::vector<mapping_type> all_mapping_types(uint8_t hf_version);
sqlite3 *init_loki_name_system(char const *file_path, bool read_only);
/// Returns the integer value used in the database and in RPC lookup calls for the given mapping
/// type. In particularly this maps all mapping_type::lokinet_Xyears values to the underlying value
/// of mapping_type::lokinet.
constexpr uint16_t db_mapping_type(lns::mapping_type type) {
if (is_lokinet_type(type))
return static_cast<uint16_t>(mapping_type::lokinet);
return static_cast<uint16_t>(type);
}
// Returns the length of the given mapping type, in blocks, or std::nullopt if the mapping type never expires.
std::optional<uint64_t> expiry_blocks(cryptonote::network_type nettype, mapping_type type);
// Returns *the* proper representation of a name_hash for querying the database, which is 44 base64
// characters (43 significant chars + a padding '='). External input values should always get
// converted to bytes and then back to base64 through this function (even if initially provided in
// base64) to ensure the correct exact representation. Input must be exactly 32 bytes (a
// std::runtime_error is raised if this is not the case).
std::string name_hash_bytes_to_base64(std::string_view bytes);
// Similar to the above, but takes a value as any of:
// - 32 bytes
// - 64 hex characters
// - 43 or 44 base64 characters (decoded value must be exactly 32 bytes)
// Returns a string of the canonical base64-encoded value *if* the input was valid, std::nullopt
// otherwise.
std::optional<std::string> name_hash_input_to_base64(std::string_view input);
bool validate_lns_name(mapping_type type, std::string name, std::string *reason = nullptr);
generic_signature make_monero_signature(crypto::hash const &hash, crypto::public_key const &pkey, crypto::secret_key const &skey);
generic_signature make_ed25519_signature(crypto::hash const &hash, crypto::ed25519_secret_key const &skey);
generic_owner make_monero_owner(cryptonote::account_public_address const &owner, bool is_subaddress);
generic_owner make_ed25519_owner(crypto::ed25519_public_key const &pkey);
bool parse_owner_to_generic_owner(cryptonote::network_type nettype, std::string const &owner, generic_owner &key, std::string *reason);
bool parse_owner_to_generic_owner(cryptonote::network_type nettype, std::string_view owner, generic_owner &key, std::string *reason);
crypto::hash tx_extra_signature_hash(std::string_view value, generic_owner const *owner, generic_owner const *backup_owner, crypto::hash const &prev_txid);
// Validate a human readable mapping value representation in 'value' and write the binary form into 'blob'.
// value: if type is session, 66 character hex string of an ed25519 public key
// lokinet, 52 character base32z string of an ed25519 public key
// wallet, the wallet public address string
// blob: (optional) if function returns true, validate_mapping_value will convert the 'value' into a binary format suitable for encryption in encrypt_mapping_value(...)
bool validate_mapping_value(cryptonote::network_type nettype, mapping_type type, std::string const &value, mapping_value *blob = nullptr, std::string *reason = nullptr);
bool validate_encrypted_mapping_value(mapping_type type, std::string const &value, std::string *reason = nullptr);
enum struct lns_tx_type { lookup, buy, update, renew };
// Converts a human readable case-insensitive string denoting the mapping type into a value suitable for storing into the LNS DB.
// Currently only accepts "session"
// Currently accepts "session" or "lokinet" for lookups, buys, updates, and renewals; for buys and renewals also accepts "lokinet_Ny[ear]" for N=2,5,10
// Lookups are implied by none of buy/update/renew.
// mapping_type: (optional) if function returns true, the uint16_t value of the 'type' will be set
bool validate_mapping_type(std::string const &type, mapping_type *mapping_type, std::string *reason);
bool validate_mapping_type(std::string_view type, uint8_t hf_version, lns_tx_type txtype, mapping_type *mapping_type, std::string *reason);
crypto::hash name_to_hash(std::string const &name); // Takes a human readable name and hashes it.
std::string name_to_base64_hash(std::string const &name); // Takes a human readable name, hashes it and returns a base64 representation of the hash, suitable for storage into the LNS DB.
// Takes a binary value and encrypts it using 'name' as a secret key or vice versa, suitable for storing into the LNS DB.
// Only basic overflow validation is attempted, values should be pre-validated in the validate* functions.
bool encrypt_mapping_value(std::string const &name, mapping_value const &value, mapping_value &encrypted_value);
bool decrypt_mapping_value(std::string const &name, mapping_value const &encrypted_value, mapping_value &value);
crypto::hash name_to_hash(std::string_view name, const std::optional<crypto::hash>& key = std::nullopt); // Takes a human readable name and hashes it. Takes an optional value to use as a key to produce a keyed hash.
std::string name_to_base64_hash(std::string_view name); // Takes a human readable name, hashes it and returns a base64 representation of the hash, suitable for storage into the LNS DB.
struct owner_record
{
@ -170,8 +202,8 @@ struct mapping_record
// keep all LNS entries indefinitely to support large blockchain detachments.
// A mapping_record forms a linked list of TXID's which allows us to revert
// the LNS DB to any arbitrary height at a small additional storage cost.
// return: if the record is still active and hasn't expired.
bool active(cryptonote::network_type nettype, uint64_t blockchain_height) const;
// return: if the record exists and hasn't expired.
bool active(uint64_t blockchain_height) const;
operator bool() const { return loaded; }
bool loaded;
@ -180,6 +212,7 @@ struct mapping_record
std::string name_hash; // name hashed and represented in base64 encoding
mapping_value encrypted_value;
uint64_t register_height;
std::optional<uint64_t> expiration_height;
uint64_t update_height;
crypto::hash txid;
crypto::hash prev_txid;
@ -237,7 +270,7 @@ struct name_system_db
// Signifies the blockchain has reorganized commences the rollback and pruning procedures.
void block_detach (cryptonote::Blockchain const &blockchain, uint64_t new_blockchain_height);
bool save_owner (generic_owner const &owner, int64_t *row_id);
bool save_mapping (crypto::hash const &tx_hash, cryptonote::tx_extra_loki_name_system const &src, uint64_t height, int64_t owner_id, int64_t backup_owner_id = 0);
bool save_mapping (crypto::hash const &tx_hash, cryptonote::tx_extra_loki_name_system const &src, uint64_t height, std::optional<uint64_t> expiration_height, int64_t owner_id, std::optional<int64_t> backup_owner_id);
bool save_settings (uint64_t top_height, crypto::hash const &top_hash, int version);
// Delete all mappings that are registered on height or newer followed by deleting all owners no longer referenced in the DB
@ -245,14 +278,22 @@ struct name_system_db
owner_record get_owner_by_key (generic_owner const &owner);
owner_record get_owner_by_id (int64_t owner_id);
mapping_record get_mapping (mapping_type type, std::string const &name_base64_hash);
std::vector<mapping_record> get_mappings (std::vector<uint16_t> const &types, std::string const &name_base64_hash);
std::vector<mapping_record> get_mappings_by_owner (generic_owner const &key);
std::vector<mapping_record> get_mappings_by_owners(std::vector<generic_owner> const &keys);
// The get_mapping* methods can return any mapping, or only active mappings: for only active
// mappings, pass in the blockchain height. If you omit it (or explicitly pass std::nullopt) then
// you will get the latest mappingsvalues regardless of whether expired or not they are expired.
mapping_record get_mapping (mapping_type type, std::string_view name_base64_hash, std::optional<uint64_t> blockchain_height = std::nullopt);
std::vector<mapping_record> get_mappings (std::vector<mapping_type> const &types, std::string_view name_base64_hash, std::optional<uint64_t> blockchain_height = std::nullopt);
std::vector<mapping_record> get_mappings_by_owner (generic_owner const &key, std::optional<uint64_t> blockchain_height = std::nullopt);
std::vector<mapping_record> get_mappings_by_owners(std::vector<generic_owner> const &keys, std::optional<uint64_t> blockchain_height = std::nullopt);
settings_record get_settings ();
// entry: (optional) if function returns true, the Loki Name System entry in the TX extra is copied into 'entry'
bool validate_lns_tx (uint8_t hf_version, uint64_t blockchain_height, cryptonote::transaction const &tx, cryptonote::tx_extra_loki_name_system *entry = nullptr, std::string *reason = nullptr);
// Resolves a mapping of the given type and name hash. Returns a null optional if the value was
// not found or expired, otherwise returns the encrypted value.
std::optional<mapping_value> resolve(mapping_type type, std::string_view name_hash_b64, uint64_t blockchain_height);
// Validates an LNS transaction. If the function returns true then entry will be populated with
// the LNS details. On a false return, `reason` is instead populated with the failure reason.
bool validate_lns_tx(uint8_t hf_version, uint64_t blockchain_height, cryptonote::transaction const &tx, cryptonote::tx_extra_loki_name_system &entry, std::string *reason);
// Destructor; closes the sqlite3 database if one is open
~name_system_db();
@ -269,6 +310,7 @@ private:
sql_compiled_statement get_owner_by_key_sql{*this};
sql_compiled_statement get_owner_by_id_sql{*this};
sql_compiled_statement get_mapping_sql{*this};
sql_compiled_statement resolve_sql{*this};
sql_compiled_statement get_settings_sql{*this};
sql_compiled_statement prune_mappings_sql{*this};
sql_compiled_statement prune_owners_sql{*this};

View File

@ -81,7 +81,7 @@ struct extra_printer {
std::cout << "LNS " << (x.is_buying() ? "registration" : x.is_updating() ? "update" : "(unknown)");
switch (x.type)
{
case lns::mapping_type::lokinet_1year: std::cout << " - Lokinet (1y)"; break;
case lns::mapping_type::lokinet: std::cout << " - Lokinet (1y)"; break;
case lns::mapping_type::lokinet_2years: std::cout << " - Lokinet (2y)"; break;
case lns::mapping_type::lokinet_5years: std::cout << " - Lokinet (5y)"; break;
case lns::mapping_type::lokinet_10years: std::cout << " - Lokinet (10y)"; break;

View File

@ -58,9 +58,9 @@ namespace lns
{
enum struct mapping_type : uint16_t
{
session,
wallet,
lokinet_1year,
session = 0,
wallet = 1,
lokinet = 2, // the type value stored in the database; counts as 1-year when used in a buy tx.
lokinet_2years,
lokinet_5years,
lokinet_10years,
@ -68,6 +68,12 @@ enum struct mapping_type : uint16_t
update_record_internal,
};
constexpr bool is_lokinet_type(mapping_type t) { return t >= mapping_type::lokinet && t <= mapping_type::lokinet_10years; }
// How many days we add per "year" of LNS lokinet registration. We slightly extend this to the 368
// days per registration "year" to allow for some blockchain time drift + leap years.
constexpr uint64_t REGISTRATION_YEAR_DAYS = 368;
constexpr uint64_t burn_needed(uint8_t /*hf_version*/, mapping_type type)
{
uint64_t result = 0;
@ -77,7 +83,7 @@ constexpr uint64_t burn_needed(uint8_t /*hf_version*/, mapping_type type)
result = 0;
break;
case mapping_type::lokinet_1year: /* FALLTHRU */
case mapping_type::lokinet: /* FALLTHRU */
case mapping_type::session: /* FALLTHRU */
case mapping_type::wallet: /* FALLTHRU */
default:

View File

@ -35,10 +35,14 @@
#include <boost/endian/conversion.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <type_traits>
#include <variant>
#include <lokimq/base64.h>
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/loki_name_system.h"
#include "include_base_utils.h"
#include "loki_economy.h"
#include "string_tools.h"
#include "core_rpc_server.h"
#include "common/command_line.h"
@ -766,24 +770,27 @@ namespace cryptonote { namespace rpc {
}
void operator()(const tx_extra_loki_name_system& x) {
auto& lns = entry.lns.emplace();
lns.blocks = lns::expiry_blocks(nettype, x.type);
switch (x.type)
{
case lns::mapping_type::lokinet_1year: lns.type = "lokinet"; lns.blocks = BLOCKS_EXPECTED_IN_YEARS(1); break;
case lns::mapping_type::lokinet_2years: lns.type = "lokinet"; lns.blocks = BLOCKS_EXPECTED_IN_YEARS(2); break;
case lns::mapping_type::lokinet_5years: lns.type = "lokinet"; lns.blocks = BLOCKS_EXPECTED_IN_YEARS(5); break;
case lns::mapping_type::lokinet_10years: lns.type = "lokinet"; lns.blocks = BLOCKS_EXPECTED_IN_YEARS(10); break;
case lns::mapping_type::lokinet: [[fallthrough]];
case lns::mapping_type::lokinet_2years: [[fallthrough]];
case lns::mapping_type::lokinet_5years: [[fallthrough]];
case lns::mapping_type::lokinet_10years: lns.type = "lokinet"; break;
case lns::mapping_type::session: lns.type = "session"; break;
case lns::mapping_type::wallet: lns.type = "wallet"; break;
case lns::mapping_type::update_record_internal:
case lns::mapping_type::update_record_internal: [[fallthrough]];
case lns::mapping_type::_count:
break;
}
if (x.is_buying())
lns.buy = true;
else if (lns.blocks.has_value())
lns.blocks.reset();
if (x.is_updating())
else if (x.is_updating())
lns.update = true;
else if (x.is_renewing())
lns.renew = true;
lns.name = x.name_hash;
if (x.prev_txid != crypto::null_hash)
lns.prev_txid = tools::type_to_hex(x.prev_txid);
@ -3294,6 +3301,12 @@ namespace cryptonote { namespace rpc {
if (!context.admin)
check_quantity_limit(req.entries.size(), LNS_NAMES_TO_OWNERS::MAX_REQUEST_ENTRIES);
std::optional<uint64_t> height = m_core.get_current_blockchain_height();
uint8_t hf_version = m_core.get_hard_fork_version(*height);
if (req.include_expired) height = std::nullopt;
std::vector<lns::mapping_type> types;
lns::name_system_db &db = m_core.get_blockchain_storage().name_system_db();
for (size_t request_index = 0; request_index < req.entries.size(); request_index++)
{
@ -3301,18 +3314,34 @@ namespace cryptonote { namespace rpc {
if (!context.admin)
check_quantity_limit(request.types.size(), LNS_NAMES_TO_OWNERS::MAX_TYPE_REQUEST_ENTRIES, "types");
std::vector<lns::mapping_record> records = db.get_mappings(request.types, request.name_hash);
types.clear();
if (types.capacity() < request.types.size())
types.reserve(request.types.size());
for (auto type : request.types)
{
types.push_back(static_cast<lns::mapping_type>(type));
if (!lns::mapping_type_allowed(hf_version, types.back()))
throw rpc_error{ERROR_WRONG_PARAM, "Invalid lokinet type '" + std::to_string(type) + "'"};
}
// This also takes 32 raw bytes, but that is undocumented (because it is painful to pass
// through json).
auto name_hash = lns::name_hash_input_to_base64(request.name_hash);
if (!name_hash)
throw rpc_error{ERROR_WRONG_PARAM, "Invalid name_hash: expected hash as 64 hex digits or 43/44 base64 characters"};
std::vector<lns::mapping_record> records = db.get_mappings(types, *name_hash, height);
for (auto const &record : records)
{
res.entries.emplace_back();
LNS_NAMES_TO_OWNERS::response_entry &entry = res.entries.back();
auto& entry = res.entries.emplace_back();
entry.entry_index = request_index;
entry.type = static_cast<uint16_t>(record.type);
entry.type = record.type;
entry.name_hash = record.name_hash;
entry.owner = record.owner.to_string(nettype());
if (record.backup_owner) entry.backup_owner = record.backup_owner.to_string(nettype());
entry.encrypted_value = lokimq::to_hex(record.encrypted_value.to_view());
entry.register_height = record.register_height;
entry.expiration_height = record.expiration_height;
entry.update_height = record.update_height;
entry.txid = tools::type_to_hex(record.txid);
if (record.prev_txid) entry.prev_txid = tools::type_to_hex(record.prev_txid);
@ -3351,11 +3380,11 @@ namespace cryptonote { namespace rpc {
}
lns::name_system_db &db = m_core.get_blockchain_storage().name_system_db();
std::vector<lns::mapping_record> records = db.get_mappings_by_owners(owners);
auto height = m_core.get_current_blockchain_height();
std::vector<lns::mapping_record> records = db.get_mappings_by_owners(owners, height);
for (auto &record : records)
{
res.entries.emplace_back();
LNS_OWNERS_TO_NAMES::response_entry &entry = res.entries.back();
auto& entry = res.entries.emplace_back();
auto it = owner_to_request_index.find(record.owner);
if (it == owner_to_request_index.end())
@ -3363,13 +3392,14 @@ namespace cryptonote { namespace rpc {
", could not be mapped back a index in the request 'entries' array"};
entry.request_index = it->second;
entry.type = static_cast<uint16_t>(record.type);
entry.type = record.type;
entry.name_hash = std::move(record.name_hash);
if (record.owner) entry.owner = record.owner.to_string(nettype());
if (record.backup_owner) entry.backup_owner = record.backup_owner.to_string(nettype());
entry.encrypted_value = lokimq::to_hex(record.encrypted_value.to_view());
entry.register_height = record.register_height;
entry.update_height = record.update_height;
entry.expiration_height = record.expiration_height;
entry.txid = tools::type_to_hex(record.txid);
if (record.prev_txid) entry.prev_txid = tools::type_to_hex(record.prev_txid);
}
@ -3378,4 +3408,35 @@ namespace cryptonote { namespace rpc {
return res;
}
//------------------------------------------------------------------------------------------------------------------------------
LNS_RESOLVE::response core_rpc_server::invoke(LNS_RESOLVE::request&& req, rpc_context context)
{
LNS_RESOLVE::response res{};
if (req.type >= tools::enum_count<lns::mapping_type>)
throw rpc_error{ERROR_WRONG_PARAM, "Unable to resolve LNS address: 'type' parameter not specified"};
auto name_hash = lns::name_hash_input_to_base64(req.name_hash);
if (!name_hash)
throw rpc_error{ERROR_WRONG_PARAM, "Unable to resolve LNS address: invalid 'name_hash' value '" + req.name_hash + "'"};
uint8_t hf_version = m_core.get_hard_fork_version(m_core.get_current_blockchain_height());
auto type = static_cast<lns::mapping_type>(req.type);
if (!lns::mapping_type_allowed(hf_version, type))
throw rpc_error{ERROR_WRONG_PARAM, "Invalid lokinet type '" + std::to_string(req.type) + "'"};
if (auto mapping = m_core.get_blockchain_storage().name_system_db().resolve(
type, *name_hash, m_core.get_current_blockchain_height()))
{
auto [val, nonce] = mapping->value_nonce(type);
res.encrypted_value = lokimq::to_hex(val);
MFATAL("val: " << lokimq::to_hex(val));
MFATAL("nonce: " << lokimq::to_hex(nonce));
MFATAL("all: " << lokimq::to_hex(mapping->to_view()));
if (val.size() < mapping->to_view().size())
res.nonce = lokimq::to_hex(nonce);
}
return res;
}
} } // namespace cryptonote

View File

@ -264,6 +264,7 @@ namespace cryptonote { namespace rpc {
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);
LNS_RESOLVE::response invoke(LNS_RESOLVE::request&& req, rpc_context context);
FLUSH_CACHE::response invoke(FLUSH_CACHE::request&& req, rpc_context);
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)

View File

@ -111,6 +111,7 @@ KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_TRANSACTIONS::extra_entry::lns_details)
KV_SERIALIZE(buy)
KV_SERIALIZE(update)
KV_SERIALIZE(renew)
KV_SERIALIZE(type)
KV_SERIALIZE(blocks)
KV_SERIALIZE_VAL_POD_AS_BLOB(name)
@ -1305,18 +1306,20 @@ KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_NAMES_TO_OWNERS::request)
KV_SERIALIZE(entries)
KV_SERIALIZE(include_expired)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_NAMES_TO_OWNERS::response_entry)
KV_SERIALIZE(entry_index)
KV_SERIALIZE(type)
KV_SERIALIZE_ENUM(type)
KV_SERIALIZE(name_hash)
KV_SERIALIZE(owner)
KV_SERIALIZE(backup_owner)
KV_SERIALIZE(encrypted_value)
KV_SERIALIZE(register_height)
KV_SERIALIZE(update_height)
KV_SERIALIZE(expiration_height)
KV_SERIALIZE(txid)
KV_SERIALIZE(prev_txid)
KV_SERIALIZE_MAP_CODE_END()
@ -1335,7 +1338,7 @@ KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_OWNERS_TO_NAMES::response_entry)
KV_SERIALIZE(request_index)
KV_SERIALIZE(type)
KV_SERIALIZE_ENUM(type)
KV_SERIALIZE(name_hash)
KV_SERIALIZE(owner)
KV_SERIALIZE(backup_owner)
@ -1353,6 +1356,18 @@ KV_SERIALIZE_MAP_CODE_BEGIN(LNS_OWNERS_TO_NAMES::response)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_RESOLVE::request)
KV_SERIALIZE(name_hash)
KV_SERIALIZE_OPT(type, static_cast<uint16_t>(-1))
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_RESOLVE::response)
KV_SERIALIZE(encrypted_value)
KV_SERIALIZE(nonce)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(FLUSH_CACHE::request)
KV_SERIALIZE_OPT(bad_txs, false)
KV_SERIALIZE_OPT(bad_blocks, false)

View File

@ -303,8 +303,9 @@ namespace rpc {
{
std::optional<bool> buy; // Provided and true iff this is an LNS buy record
std::optional<bool> update; // Provided and true iff this is an LNS record update
std::optional<bool> renew; // Provided and true iff this is an LNS record renewal
std::string type; // The LNS request type. For registrations: "lokinet", "session", "wallet"; for a record update: "update"
std::optional<uint32_t> blocks; // The registration length in blocks (only applies to lokinet registrations; session/wallet registrations do not expire)
std::optional<uint64_t> blocks; // The registration length in blocks (only applies to lokinet registrations; session/wallet registrations do not expire)
crypto::hash name; // The hashed name of the record being purchased/updated (the actual name is not provided on the blockchain).
std::optional<std::string> prev_txid; // For an update, this points at the txid of the previous lns update transaction.
std::optional<std::string> value; // The encrypted value of the record; note that this is encrypted using the actual name itself (*not* the hashed name).
@ -2389,7 +2390,7 @@ namespace rpc {
LOKI_RPC_DOC_INTROSPECT
// Get the name mapping for a Loki Name Service entry. Loki currently supports mappings
// for Session.
// for Session and Lokinet.
struct LNS_NAMES_TO_OWNERS : PUBLIC
{
static constexpr auto names() { return NAMES("lns_names_to_owners"); }
@ -2398,15 +2399,16 @@ namespace rpc {
static constexpr size_t MAX_TYPE_REQUEST_ENTRIES = 8;
struct request_entry
{
std::string name_hash; // The name hashed using libsodium's crypto_generichash_blake2b in base64 to resolve to a public key via Loki Name Service
std::vector<uint16_t> types; // If empty, query all types. Currently only Session(0). In future updates more mapping types will be available.
std::string name_hash; // The 32-byte BLAKE2b hash of the name to resolve to a public key via Loki Name Service. The value must be provided either in hex (64 hex digits) or base64 (44 characters with padding, or 43 characters without).
std::vector<uint16_t> types; // If empty, query all types. Currently supported types are 0 (session) and 2 (lokinet). In future updates more mapping types will be available.
KV_MAP_SERIALIZABLE
};
struct request
{
std::vector<request_entry> entries;
std::vector<request_entry> entries; // Entries to look up
bool include_expired; // Optional: if provided and true, include entries in the results even if they are expired
KV_MAP_SERIALIZABLE
};
@ -2414,15 +2416,16 @@ namespace rpc {
struct response_entry
{
uint64_t entry_index; // The index in request_entry's `entries` array that was resolved via Loki Name Service.
uint16_t type; // The type of Loki Name Service entry that the owner owns.
std::string name_hash; // The hash of the name that was queried in base64
lns::mapping_type type; // The type of Loki Name Service entry that the owner owns: currently supported values are 0 (session), 2 (lokinet)
std::string name_hash; // The hash of the name that was queried, in base64
std::string owner; // The public key that purchased the Loki Name Service entry.
std::string backup_owner; // The backup public key that the owner specified when purchasing the Loki Name Service entry.
std::string encrypted_value; // The encrypted value that the name maps to. This value is encrypted using the name (not the hash) as the secret.
std::optional<std::string> backup_owner; // The backup public key 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. See the `LNS_RESOLVE` description for information on how this value can be decrypted.
uint64_t register_height; // The height that this Loki Name Service entry was purchased on the Blockchain.
uint64_t update_height; // The last height that this Loki Name Service entry was updated on the Blockchain.
std::string txid; // The txid of who purchased the mapping, null hash if not applicable.
std::string prev_txid; // The previous txid that purchased the mapping, null hash if not applicable.
std::optional<uint64_t> expiration_height; // For records that expire, this will be set to the expiration block height.
std::string txid; // The txid of the mapping's most recent update or purchase.
std::optional<std::string> prev_txid; // The txid of the second-most recent update or purchase; omitted if there is no previous tx.
KV_MAP_SERIALIZABLE
};
@ -2454,15 +2457,16 @@ namespace rpc {
struct response_entry
{
uint64_t request_index; // (Deprecated) The index in request's `entries` array that was resolved via Loki Name Service.
uint16_t type; // The category the Loki Name Service entry belongs to, currently only Session whose value is 0.
lns::mapping_type type; // The category the Loki Name Service entry belongs to; currently 0 for Session and 2 for Lokinet.
std::string name_hash; // The hash of the name that the owner purchased via Loki Name Service in base64
std::string owner; // The backup public key specified by the owner that purchased the Loki Name Service entry.
std::string backup_owner; // The backup public key specified by the owner that purchased the Loki Name Service entry.
std::string encrypted_value; // The encrypted value that the name maps to. This value is encrypted using the name (not the hash) as the secret.
std::optional<std::string> backup_owner; // The backup public key specified by the owner that purchased the Loki Name Service entry. Omitted if no backup owner.
std::string encrypted_value; // The encrypted value that the name maps to, in hex. This value is encrypted using the name (not the hash) as the secret.
uint64_t register_height; // The height that this Loki Name Service entry was purchased on the Blockchain.
uint64_t update_height; // The last height that this Loki Name Service entry was updated on the Blockchain.
std::string txid; // The txid of who purchases the mapping.
std::string prev_txid; // The previous txid that purchased the mapping, null hash if not applicable.
std::optional<uint64_t> expiration_height; // For records that expire, this will be set to the expiration block height.
std::string txid; // The txid of the mapping's most recent update or purchase.
std::optional<std::string> prev_txid; // The txid of the second-most recent update or purchase; omitted if there is no previous tx.
KV_MAP_SERIALIZABLE
};
@ -2476,6 +2480,46 @@ namespace rpc {
};
};
LOKI_RPC_DOC_INTROSPECT
// Performs a simple LNS lookup of a BLAKE2b-hashed name. This RPC method is meant for simple,
// single-value resolutions that do not care about registration details, etc.; if you need more
// information use LNS_NAMES_TO_OWNERS instead.
//
// Technical details: the returned value is encrypted using the name itself so that neither this
// lokid responding to the RPC request nor any other blockchain observers can (easily) obtain the
// name of registered addresses or the registration details. Thus, from a client's point of view,
// resolving an LNS record involves:
//
// - Lower-case the name.
// - Calculate the name hash as a null-key, 32-byte BLAKE2b hash of the lower-case name.
// - Obtain the encrypted value and the nonce from this RPC call (or LNS_NAMES_TO_OWNERS); (encode
// the name hash using either hex or base64.).
// - Calculate the decryption key as a 32-byte BLAKE2b keyed hash of the name using the
// (unkeyed) name hash calculated above as the hash key.
// - Decrypt (and verify) using XChaCha20-Poly1305 (for example libsodium's
// crypto_aead_xchacha20poly1305_ietf_decrypt) using the above decryption key and using the
// first 24 bytes of the name hash as the public nonce.
struct LNS_RESOLVE : PUBLIC
{
static constexpr auto names() { return NAMES("lns_resolve"); }
struct request
{
uint16_t type; // The LNS type (mandatory); currently supported values are: 0 = session, 2 = lokinet.
std::string name_hash; // The 32-byte BLAKE2b hash of the name to look up, encoded as 64 hex digits or 44/43 base64 characters (with/without padding).
KV_MAP_SERIALIZABLE
};
struct response
{
std::optional<std::string> encrypted_value; // The encrypted LNS value, in hex. Will be omitted from the response if the given name_hash is not registered.
std::optional<std::string> nonce; // The nonce value used for encryption, in hex.
KV_MAP_SERIALIZABLE
};
};
LOKI_RPC_DOC_INTROSPECT
// Clear TXs from the daemon cache, currently only the cache storing TX hashes that were previously verified bad by the daemon.
struct FLUSH_CACHE : RPC_COMMAND
@ -2576,6 +2620,7 @@ namespace rpc {
TEST_TRIGGER_P2P_RESYNC,
LNS_NAMES_TO_OWNERS,
LNS_OWNERS_TO_NAMES,
LNS_RESOLVE,
FLUSH_CACHE
>;

View File

@ -35,6 +35,8 @@
* \brief Source file that defines simple_wallet class.
*/
#include "common/string_util.h"
#include "loki_economy.h"
#include <chrono>
#ifdef _WIN32
#define __STDC_FORMAT_MACROS // NOTE(loki): Explicitly define the PRIu64 macro on Mingw
@ -264,13 +266,14 @@ namespace
const char* USAGE_REQUEST_STAKE_UNLOCK("request_stake_unlock <service_node_pubkey>");
const char* USAGE_PRINT_LOCKED_STAKES("print_locked_stakes");
const char* USAGE_LNS_BUY_MAPPING("lns_buy_mapping [index=<N1>[,<N2>,...]] [<priority>] [owner=<value>] [backup_owner=<value>] <name> <value>");
const char* USAGE_LNS_UPDATE_MAPPING("lns_update_mapping [index=<N1>[,<N2>,...]] [<priority>] [owner=<value>] [backup_owner=<value>] [value=<lns_value>] [signature=<hex_signature>] <name>");
const char* USAGE_LNS_BUY_MAPPING("lns_buy_mapping [index=<N1>[,<N2>,...]] [<priority>] [type=session|lokinet|lokinet_2y|lokinet_5y|lokinet_10y] [owner=<value>] [backup_owner=<value>] <name> <value>");
const char* USAGE_LNS_RENEW_MAPPING("lns_renew_mapping [index=<N1>[,<N2>,...]] [<priority>] [type=lokinet|lokinet_2y|lokinet_5y|lokinet_10y] <name>");
const char* USAGE_LNS_UPDATE_MAPPING("lns_update_mapping [index=<N1>[,<N2>,...]] [<priority>] [type=session|lokinet] [owner=<value>] [backup_owner=<value>] [value=<lns_value>] [signature=<hex_signature>] <name>");
// TODO(loki): Currently defaults to session, in future allow specifying Lokinet and Wallet when they are enabled
const char* USAGE_LNS_MAKE_UPDATE_MAPPING_SIGNATURE("lns_make_update_mapping_signature [owner=<value>] [backup_owner=<value>] [value=<lns_value>] <name>");
const char* USAGE_LNS_PRINT_OWNERS_TO_NAMES("lns_print_owners_to_names [<owner>, ...]");
const char* USAGE_LNS_PRINT_NAME_TO_OWNERS("lns_print_name_to_owners [type=<N1|all>[,<N2>...]] <name>");
const char* USAGE_LNS_ENCRYPT("lns_encrypt [type=session|lokinet] <name> <value>");
const char* USAGE_LNS_MAKE_UPDATE_MAPPING_SIGNATURE("lns_make_update_mapping_signature [type=session|lokinet] [owner=<value>] [backup_owner=<value>] [value=<encrypted_lns_value>] <name>");
const char* USAGE_LNS_PRINT_OWNERS_TO_NAMES("lns_print_owners_to_names [<owner> ...]");
const char* USAGE_LNS_PRINT_NAME_TO_OWNERS("lns_print_name_to_owners [type=session|lokinet] <name> [<name> ...]");
#if defined (LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
std::string input_line(const std::string &prompt, bool yesno = false)
@ -3171,6 +3174,11 @@ Pending or Failed: "failed"|"pending", "out", Lock, Checkpointed, Time, Amount*
tr(USAGE_LNS_BUY_MAPPING),
tr(tools::wallet_rpc::LNS_BUY_MAPPING::description));
m_cmd_binder.set_handler("lns_renew_mapping",
[this](const auto& x) { return lns_renew_mapping(x); },
tr(USAGE_LNS_RENEW_MAPPING),
tr(tools::wallet_rpc::LNS_RENEW_MAPPING::description));
m_cmd_binder.set_handler("lns_update_mapping",
[this](const auto& x) { return lns_update_mapping(x); },
tr(USAGE_LNS_UPDATE_MAPPING),
@ -6415,14 +6423,14 @@ bool simple_wallet::print_locked_stakes(const std::vector<std::string>& /*args*/
return true;
}
//----------------------------------------------------------------------------------------------------
std::string eat_named_argument(std::vector<std::string> &args, char const *prefix, size_t prefix_len)
std::string eat_named_argument(std::vector<std::string> &args, std::string_view prefix)
{
std::string result = {};
for (auto it = args.begin(); it != args.end(); it++)
{
if (it->size() > prefix_len && memcmp(it->data(), prefix, prefix_len) == 0)
if (it->size() > prefix.size() && tools::starts_with(*it, prefix))
{
result = it->substr(prefix_len, it->size() - prefix_len);
result = it->substr(prefix.size());
args.erase(it);
break;
}
@ -6430,37 +6438,78 @@ std::string eat_named_argument(std::vector<std::string> &args, char const *prefi
return result;
}
//----------------------------------------------------------------------------------------------------
constexpr char const LNS_OWNER_PREFIX[] = "owner=";
constexpr char const LNS_BACKUP_OWNER_PREFIX[] = "backup_owner=";
constexpr char const LNS_VALUE_PREFIX[] = "value=";
constexpr char const LNS_SIGNATURE_PREFIX[] = "signature=";
bool simple_wallet::lns_buy_mapping(const std::vector<std::string>& args)
template <typename... Prefixes>
std::array<std::string, sizeof...(Prefixes)> eat_named_arguments(std::vector<std::string> &args, const Prefixes&... prefixes)
{
return { eat_named_argument(args, prefixes)... };
}
// Parse a user-provided typestring value; if not provided, guess from the provided name and value.
static std::optional<lns::mapping_type> guess_lns_type(tools::wallet2& wallet, std::string_view typestr, std::string_view name, std::string_view value)
{
if (typestr.empty())
{
if (tools::ends_with(name, ".loki") && (tools::ends_with(value, ".loki") || value.empty()))
return lns::mapping_type::lokinet;
if (!tools::ends_with(name, ".loki") && tools::starts_with(value, "05") && value.length() == 2*lns::SESSION_PUBLIC_KEY_BINARY_LENGTH)
return lns::mapping_type::session;
fail_msg_writer() << tr("Could not infer LNS type from name/value; trying using the type= argument or see `help' for more details");
return std::nullopt;
}
auto hf_version = wallet.get_hard_fork_version();
if (!hf_version)
{
tools::fail_msg_writer() << tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED;
return std::nullopt;
}
std::string reason;
if (lns::mapping_type type; lns::validate_mapping_type(typestr, *hf_version, lns::lns_tx_type::buy, &type, &reason))
return type;
fail_msg_writer() << reason;
return std::nullopt;
}
//----------------------------------------------------------------------------------------------------
static constexpr auto LNS_OWNER_PREFIX = "owner="sv;
static constexpr auto LNS_BACKUP_OWNER_PREFIX = "backup_owner="sv;
static constexpr auto LNS_TYPE_PREFIX = "type="sv;
static constexpr auto LNS_VALUE_PREFIX = "value="sv;
static constexpr auto LNS_SIGNATURE_PREFIX = "signature="sv;
static char constexpr NULL_STR[] = "(none)";
bool simple_wallet::lns_buy_mapping(std::vector<std::string> args)
{
std::vector<std::string> local_args = args;
uint32_t priority = 0;
std::set<uint32_t> subaddr_indices = {};
if (!parse_subaddr_indices_and_priority(*m_wallet, local_args, subaddr_indices, priority, m_current_subaddress_account)) return false;
if (!parse_subaddr_indices_and_priority(*m_wallet, args, subaddr_indices, priority, m_current_subaddress_account)) return false;
std::string owner = eat_named_argument(local_args, LNS_OWNER_PREFIX, loki::char_count(LNS_OWNER_PREFIX));
std::string backup_owner = eat_named_argument(local_args, LNS_BACKUP_OWNER_PREFIX, loki::char_count(LNS_BACKUP_OWNER_PREFIX));
auto [owner, backup_owner, typestr] = eat_named_arguments(args, LNS_OWNER_PREFIX, LNS_BACKUP_OWNER_PREFIX, LNS_TYPE_PREFIX);
if (local_args.size() != 2)
if (args.size() != 2)
{
PRINT_USAGE(USAGE_LNS_BUY_MAPPING);
return true;
}
std::string const &name = local_args[0];
std::string const &value = local_args[1];
std::string const &name = args[0];
std::string const &value = args[1];
lns::mapping_type type;
if (auto t = guess_lns_type(*m_wallet, typestr, name, value))
type = *t;
else return false;
SCOPED_WALLET_UNLOCK();
std::string reason;
std::vector<tools::wallet2::pending_tx> ptx_vector;
try
{
ptx_vector = m_wallet->lns_create_buy_mapping_tx(lns::mapping_type::session,
ptx_vector = m_wallet->lns_create_buy_mapping_tx(type,
owner.size() ? &owner : nullptr,
backup_owner.size() ? &backup_owner : nullptr,
name,
@ -6482,8 +6531,22 @@ bool simple_wallet::lns_buy_mapping(const std::vector<std::string>& args)
dsts.push_back(info);
std::cout << std::endl << tr("Buying Loki Name System Record") << std::endl << std::endl;
std::cout << boost::format(tr("Name: %s")) % name << std::endl;
std::cout << boost::format(tr("Value: %s")) % value << boost::format(tr(" for %s")) % "Session" << std::endl;
if (type == lns::mapping_type::session)
std::cout << boost::format(tr("Session Name: %s")) % name << std::endl;
else if (lns::is_lokinet_type(type))
{
std::cout << boost::format(tr("Lokinet Name: %s")) % name << std::endl;
int years =
type == lns::mapping_type::lokinet_10years ? 10 :
type == lns::mapping_type::lokinet_5years ? 5 :
type == lns::mapping_type::lokinet_2years ? 2 :
1;
int blocks = BLOCKS_EXPECTED_IN_DAYS(years * lns::REGISTRATION_YEAR_DAYS);
std::cout << boost::format(tr("Registration: %d years (%d blocks)")) % years % blocks << "\n";
}
else
std::cout << boost::format(tr("Name: %s")) % name << std::endl;
std::cout << boost::format(tr("Value: %s")) % value << std::endl;
std::cout << boost::format(tr("Owner: %s")) % (owner.size() ? owner : m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}) + " (this wallet) ") << std::endl;
if(backup_owner.size()) {
std::cout << boost::format(tr("Backup Owner: %s")) % backup_owner << std::endl;
@ -6509,25 +6572,24 @@ bool simple_wallet::lns_buy_mapping(const std::vector<std::string>& args)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::lns_update_mapping(const std::vector<std::string>& args)
bool simple_wallet::lns_renew_mapping(std::vector<std::string> args)
{
std::vector<std::string> local_args = args;
uint32_t priority = 0;
std::set<uint32_t> subaddr_indices = {};
if (!parse_subaddr_indices_and_priority(*m_wallet, local_args, subaddr_indices, priority, m_current_subaddress_account)) return false;
if (!parse_subaddr_indices_and_priority(*m_wallet, args, subaddr_indices, priority, m_current_subaddress_account)) return false;
std::string owner = eat_named_argument(local_args, LNS_OWNER_PREFIX, loki::char_count(LNS_OWNER_PREFIX));
std::string backup_owner = eat_named_argument(local_args, LNS_BACKUP_OWNER_PREFIX, loki::char_count(LNS_BACKUP_OWNER_PREFIX));
std::string value = eat_named_argument(local_args, LNS_VALUE_PREFIX, loki::char_count(LNS_VALUE_PREFIX));
std::string signature = eat_named_argument(local_args, LNS_SIGNATURE_PREFIX, loki::char_count(LNS_SIGNATURE_PREFIX));
if (local_args.empty())
std::string typestr = eat_named_argument(args, LNS_TYPE_PREFIX);
if (args.empty())
{
PRINT_USAGE(USAGE_LNS_UPDATE_MAPPING);
PRINT_USAGE(USAGE_LNS_RENEW_MAPPING);
return false;
}
std::string const &name = local_args[0];
std::string const &name = args[0];
lns::mapping_type type;
if (auto t = guess_lns_type(*m_wallet, typestr, name, ""))
type = *t;
else return false;
SCOPED_WALLET_UNLOCK();
std::string reason;
@ -6535,9 +6597,89 @@ bool simple_wallet::lns_update_mapping(const std::vector<std::string>& args)
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> response;
try
{
ptx_vector = m_wallet->lns_create_renewal_tx(
type,
name,
&reason,
priority,
m_current_subaddress_account,
subaddr_indices,
&response);
if (ptx_vector.empty())
{
tools::fail_msg_writer() << reason;
return true;
}
std::vector<cryptonote::address_parse_info> dsts;
cryptonote::address_parse_info info = {};
info.address = m_wallet->get_subaddress({m_current_subaddress_account, 0});
info.is_subaddress = m_current_subaddress_account != 0;
dsts.push_back(info);
std::cout << "\n" << tr("Renew Loki Name System Record") << "\n\n";
if (lns::is_lokinet_type(type))
std::cout << boost::format(tr("Lokinet Name: %s")) % name << "\n";
else
std::cout << boost::format(tr("Name: %s")) % name << "\n";
int years = 1;
if (type == lns::mapping_type::lokinet_2years) years = 2;
else if (type == lns::mapping_type::lokinet_5years) years = 5;
else if (type == lns::mapping_type::lokinet_10years) years = 10;
int blocks = BLOCKS_EXPECTED_IN_DAYS(years * lns::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 << std::flush;
if (!confirm_and_send_tx(dsts, ptx_vector, false /*blink*/))
return false;
}
catch (const std::exception &e)
{
handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
return true;
}
catch (...)
{
LOG_ERROR("unknown error");
fail_msg_writer() << tr("unknown error");
return true;
}
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::lns_update_mapping(std::vector<std::string> args)
{
uint32_t priority = 0;
std::set<uint32_t> subaddr_indices = {};
if (!parse_subaddr_indices_and_priority(*m_wallet, args, subaddr_indices, priority, m_current_subaddress_account)) return false;
ptx_vector = m_wallet->lns_create_update_mapping_tx(lns::mapping_type::session,
auto [owner, backup_owner, value, signature, typestr] =
eat_named_arguments(args, LNS_OWNER_PREFIX, LNS_BACKUP_OWNER_PREFIX, LNS_VALUE_PREFIX, LNS_SIGNATURE_PREFIX, LNS_TYPE_PREFIX);
if (args.empty())
{
PRINT_USAGE(USAGE_LNS_UPDATE_MAPPING);
return false;
}
std::string const &name = args[0];
lns::mapping_type type;
if (auto t = guess_lns_type(*m_wallet, typestr, name, value))
type = *t;
else return false;
SCOPED_WALLET_UNLOCK();
std::string reason;
std::vector<tools::wallet2::pending_tx> ptx_vector;
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> response;
try
{
ptx_vector = m_wallet->lns_create_update_mapping_tx(type,
name,
value.size() ? &value : nullptr,
owner.size() ? &owner : nullptr,
@ -6554,14 +6696,22 @@ bool simple_wallet::lns_update_mapping(const std::vector<std::string>& args)
return true;
}
lns::mapping_value encrypted_value = {};
encrypted_value.len = response[0].encrypted_value.size() / 2;
lokimq::from_hex(response[0].encrypted_value.begin(), response[0].encrypted_value.end(), encrypted_value.buffer.begin());
lns::mapping_value old_value = {};
if (!lns::decrypt_mapping_value(tools::lowercase_ascii_string(name), encrypted_value, old_value))
auto& enc_hex = response[0].encrypted_value;
if (!lokimq::is_hex(enc_hex) || enc_hex.size() % 2 != 0 || enc_hex.size() > 2*lns::mapping_value::BUFFER_SIZE)
{
fail_msg_writer() << "Failed to decrypt the mapping value=" << response[0].encrypted_value;
LOG_ERROR("invalid LNS data returned from lokid");
fail_msg_writer() << tr("invalid LNS data returned from lokid");
return true;
}
lns::mapping_value mval{};
mval.len = enc_hex.size() / 2;
mval.encrypted = true;
lokimq::from_hex(enc_hex.begin(), enc_hex.end(), mval.buffer.begin());
if (!mval.decrypt(tools::lowercase_ascii_string(name), type))
{
fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_hex;
return false;
}
@ -6572,13 +6722,18 @@ bool simple_wallet::lns_update_mapping(const std::vector<std::string>& args)
dsts.push_back(info);
std::cout << std::endl << tr("Updating Loki Name System Record") << std::endl << std::endl;
std::cout << boost::format(tr("Name: %s")) % name << std::endl;
if (type == lns::mapping_type::session)
std::cout << boost::format(tr("Session Name: %s")) % name << std::endl;
else if (lns::is_lokinet_type(type))
std::cout << boost::format(tr("Lokinet Name: %s")) % name << std::endl;
else
std::cout << boost::format(tr("Name: %s")) % name << std::endl;
if(value.size()) {
std::cout << boost::format(tr("Old Value: %s")) % old_value.to_readable_value(m_wallet->nettype(),static_cast<lns::mapping_type>(response[0].type)) << std::endl;
std::cout << boost::format(tr("Old Value: %s")) % mval.to_readable_value(m_wallet->nettype(), type) << std::endl;
std::cout << boost::format(tr("New Value: %s")) % value << std::endl;
} else {
std::cout << boost::format(tr("Value: %s")) % old_value.to_readable_value(m_wallet->nettype(),static_cast<lns::mapping_type>(response[0].type)) << std::endl;
std::cout << boost::format(tr("Value: %s (unchanged)")) % mval.to_readable_value(m_wallet->nettype(), type) << std::endl;
}
if(owner.size()) {
@ -6589,10 +6744,10 @@ bool simple_wallet::lns_update_mapping(const std::vector<std::string>& args)
}
if(backup_owner.size()) {
std::cout << boost::format(tr("Old Backup Owner: %s")) % (response[0].backup_owner.empty() ? "(none)" : response[0].backup_owner) << std::endl;
std::cout << boost::format(tr("Old Backup Owner: %s")) % response[0].backup_owner.value_or(NULL_STR) << 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.empty() ? "(none)" : response[0].backup_owner) << std::endl;
std::cout << boost::format(tr("Backup Owner: %s (unchanged)")) % response[0].backup_owner.value_or(NULL_STR) << std::endl;
}
if (!confirm_and_send_tx(dsts, ptx_vector, false /*blink*/))
return false;
@ -6668,23 +6823,21 @@ bool simple_wallet::lns_encrypt(std::vector<std::string> args)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::lns_make_update_mapping_signature(const std::vector<std::string> &args)
bool simple_wallet::lns_make_update_mapping_signature(std::vector<std::string> args)
{
if (!try_connect_to_daemon())
return true;
std::vector<std::string> local_args = args;
std::string owner = eat_named_argument(local_args, LNS_OWNER_PREFIX, loki::char_count(LNS_OWNER_PREFIX));
std::string backup_owner = eat_named_argument(local_args, LNS_BACKUP_OWNER_PREFIX, loki::char_count(LNS_BACKUP_OWNER_PREFIX));
std::string value = eat_named_argument(local_args, LNS_VALUE_PREFIX, loki::char_count(LNS_VALUE_PREFIX));
auto [owner, backup_owner, value, typestr] =
eat_named_arguments(args, LNS_OWNER_PREFIX, LNS_BACKUP_OWNER_PREFIX, LNS_VALUE_PREFIX, LNS_TYPE_PREFIX);
if (local_args.empty())
if (args.empty())
{
PRINT_USAGE(USAGE_LNS_MAKE_UPDATE_MAPPING_SIGNATURE);
return false;
}
std::string const &name = local_args[0];
std::string const &name = args[0];
SCOPED_WALLET_UNLOCK();
lns::generic_signature signature_binary;
std::string reason;
@ -6703,8 +6856,7 @@ bool simple_wallet::lns_make_update_mapping_signature(const std::vector<std::str
return true;
}
//----------------------------------------------------------------------------------------------------
static char constexpr NULL_STR[] = "(none)";
bool simple_wallet::lns_print_name_to_owners(const std::vector<std::string>& args)
bool simple_wallet::lns_print_name_to_owners(std::vector<std::string> args)
{
if (!try_connect_to_daemon())
return false;
@ -6715,46 +6867,56 @@ bool simple_wallet::lns_print_name_to_owners(const std::vector<std::string>& arg
return true;
}
std::string typestr = eat_named_argument(args, LNS_TYPE_PREFIX);
std::vector<uint16_t> requested_types;
size_t name_index = 0;
// Parse LNS Types
{
std::string const &first = args[0];
char const type_prefix[] = "type=";
if (first.size() >= loki::char_count(type_prefix) && strncmp(first.data(), type_prefix, loki::char_count(type_prefix)) == 0)
if (!typestr.empty()) {
auto hf_version = m_wallet->get_hard_fork_version();
if (!hf_version)
{
name_index = 1;
std::string type_substr = first.substr(loki::char_count(type_prefix), first.size() - loki::char_count(type_prefix));
std::vector<std::string> split_types;
boost::split(split_types, type_substr, boost::is_any_of(","));
fail_msg_writer() << tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED;
return false;
}
for (std::string const &type : split_types)
for (auto type : tools::split(typestr, ","))
{
lns::mapping_type mapping_type;
std::string reason;
if (!lns::validate_mapping_type(type, *hf_version, lns::lns_tx_type::lookup, &mapping_type, &reason))
{
lns::mapping_type mapping_type;
std::string reason;
if (!lns::validate_mapping_type(type, &mapping_type, &reason))
{
fail_msg_writer() << reason;
return false;
}
requested_types.push_back(static_cast<uint16_t>(mapping_type));
fail_msg_writer() << reason;
return false;
}
requested_types.push_back(lns::db_mapping_type(mapping_type));
}
}
if (name_index >= args.size())
if (requested_types.empty())
{
auto hf_version = m_wallet->get_hard_fork_version();
if (!hf_version)
{
fail_msg_writer() << tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED;
return false;
}
auto all_types = lns::all_mapping_types(*hf_version);
std::transform(all_types.begin(), all_types.end(), std::back_inserter(requested_types), lns::db_mapping_type);
}
if (args.empty())
{
PRINT_USAGE(USAGE_LNS_PRINT_NAME_TO_OWNERS);
return true;
}
std::string const &name = tools::lowercase_ascii_string(args[name_index]);
rpc::LNS_NAMES_TO_OWNERS::request request = {};
request.entries.push_back({lns::name_to_base64_hash(name), std::move(requested_types)});
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));
rpc::LNS_NAMES_TO_OWNERS::request request = {};
for (auto& name : args)
{
name = tools::lowercase_ascii_string(std::move(name));
request.entries.push_back({lns::name_to_base64_hash(name), requested_types});
}
auto [success, response] = m_wallet->lns_names_to_owners(request);
if (!success)
@ -6763,29 +6925,54 @@ bool simple_wallet::lns_print_name_to_owners(const std::vector<std::string>& arg
return false;
}
int last_index = -1;
for (auto const &mapping : response)
{
lns::mapping_value encrypted_value = {};
encrypted_value.len = mapping.encrypted_value.size() / 2;
lokimq::from_hex(mapping.encrypted_value.begin(), mapping.encrypted_value.end(), encrypted_value.buffer.begin());
lns::mapping_value value = {};
if (!lns::decrypt_mapping_value(name, encrypted_value, value))
auto& enc_hex = mapping.encrypted_value;
if (mapping.entry_index >= args.size() || !lokimq::is_hex(enc_hex) || enc_hex.size() % 2 != 0 || enc_hex.size() > 2*lns::mapping_value::BUFFER_SIZE)
{
fail_msg_writer() << "Failed to decrypt the mapping value=" << mapping.encrypted_value;
fail_msg_writer() << "Received invalid LNS mapping data from lokid";
return false;
}
tools::msg_writer() << "name_hash=" << request.entries[0].name_hash // NOTE: We only query one name at a time
<< ", type=" << static_cast<lns::mapping_type>(mapping.type)
<< ", owner=" << mapping.owner
<< ", backup_owner=" << (mapping.backup_owner.empty() ? NULL_STR : mapping.backup_owner)
<< ", height=" << mapping.register_height
<< ", update_height=" << mapping.update_height
<< ", encrypted_value=" << mapping.encrypted_value
<< ", value=" << value.to_readable_value(m_wallet->nettype(), static_cast<lns::mapping_type>(mapping.type))
<< ", prev_txid=" << (mapping.prev_txid.empty() ? NULL_STR : mapping.prev_txid);
// Print any skipped (i.e. not registered) results:
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;
const auto& name = args[mapping.entry_index];
lns::mapping_value value{};
value.len = enc_hex.size() / 2;
value.encrypted = true;
lokimq::from_hex(enc_hex.begin(), enc_hex.end(), value.buffer.begin());
if (!value.decrypt(name, mapping.type))
{
fail_msg_writer() << "Failed to decrypt the mapping value=" << enc_hex;
return false;
}
auto writer = tools::msg_writer();
writer
<< "Name: " << name
<< "\n Type: " << static_cast<lns::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;
writer
<< "\n Registered/last updated height: " << mapping.register_height << "/" << mapping.update_height;
if (mapping.expiration_height) writer
<< "\n Expiration height: " << *mapping.expiration_height;
writer
<< "\n Encrypted value: " << enc_hex;
if (mapping.prev_txid) writer
<< "\n Last update txid: " << *mapping.prev_txid;
writer
<< "\n";
}
for (size_t i = last_index + 1; i < args.size(); i++)
fail_msg_writer() << args[i] << " not found\n";
return true;
}
@ -6857,14 +7044,21 @@ bool simple_wallet::lns_print_owners_to_names(const std::vector<std::string>& ar
continue;
}
tools::msg_writer() << "owner=" << *owner
<< ", backup_owner=" << (entry.backup_owner.empty() ? NULL_STR : entry.backup_owner)
<< ", type=" << static_cast<lns::mapping_type>(entry.type)
<< ", height=" << entry.register_height
<< ", update_height=" << entry.update_height
<< ", name_hash=" << entry.name_hash
<< ", encrypted_value=" << entry.encrypted_value
<< ", prev_txid=" << (entry.prev_txid.empty() ? NULL_STR : entry.prev_txid);
auto writer = tools::msg_writer();
writer
<< "Name (hashed): " << entry.name_hash
<< "\n Type: " << entry.type
<< "\n Owner: " << *owner;
if (entry.backup_owner) writer
<< "\n Backup owner: " << *entry.backup_owner;
writer
<< "\n Registered/last updated height: " << entry.register_height << "/" << entry.update_height;
if (entry.expiration_height) writer
<< "\n Expiration height: " << *entry.expiration_height;
writer
<< "\n Encrypted value: " << entry.encrypted_value;
if (entry.prev_txid) writer
<< "\n Last update txid: " << *entry.prev_txid;
}
}
return true;

View File

@ -181,12 +181,13 @@ namespace cryptonote
bool request_stake_unlock(const std::vector<std::string> &args_);
bool print_locked_stakes(const std::vector<std::string>& /*args*/);
bool query_locked_stakes(bool print_result);
bool lns_buy_mapping(const std::vector<std::string> &args);
bool lns_update_mapping(const std::vector<std::string> &args);
bool lns_make_update_mapping_signature(const std::vector<std::string> &args);
bool lns_buy_mapping(std::vector<std::string> args);
bool lns_renew_mapping(std::vector<std::string> args);
bool lns_update_mapping(std::vector<std::string> args);
bool lns_encrypt(std::vector<std::string> args);
bool lns_make_update_mapping_signature(std::vector<std::string> args);
bool lns_print_owners_to_names(const std::vector<std::string> &args);
bool lns_print_name_to_owners(const std::vector<std::string> &args);
bool lns_print_name_to_owners(std::vector<std::string> args);
enum class sweep_type_t { stake, register_stake, all_or_below, single };
bool sweep_main_internal(sweep_type_t sweep_type, std::vector<tools::wallet2::pending_tx> &ptx_vector, cryptonote::address_parse_info const &dest, bool blink);

View File

@ -38,8 +38,10 @@
#include <openssl/pem.h>
#include <type_traits>
#include <cpr/parameters.h>
#include <lokimq/base64.h>
#include "common/password.h"
#include "common/string_util.h"
#include "cryptonote_core/loki_name_system.h"
#include "include_base_utils.h"
#include "common/rules.h"
#include "cryptonote_config.h"
@ -8673,11 +8675,12 @@ static bool try_generate_lns_signature(wallet2 const &wallet, std::string const
static lns_prepared_args prepare_tx_extra_loki_name_system_values(wallet2 const &wallet,
lns::mapping_type type,
uint32_t priority,
std::string &name,
std::string name,
std::string const *value,
std::string const *owner,
std::string const *backup_owner,
bool make_signature,
lns::lns_tx_type txtype,
uint32_t account_index,
std::string *reason,
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> *response)
@ -8686,19 +8689,18 @@ static lns_prepared_args prepare_tx_extra_loki_name_system_values(wallet2 const
if (priority == tools::tx_priority_blink)
{
if (reason) *reason = "Can not request a blink TX for Loki Name Service transactions";
return result;
return {};
}
name = tools::lowercase_ascii_string(name);
if (!lns::validate_lns_name(type, name, reason))
return result;
return {};
result.name_hash = lns::name_to_hash(name);
if (value)
{
lns::mapping_value binary_value = {};
if (!lns::validate_mapping_value(wallet.nettype(), type, *value, &binary_value, reason))
return result;
if (!lns::mapping_value::validate(wallet.nettype(), type, *value, &result.encrypted_value, reason))
return {};
if (!result.encrypted_value.encrypt(name, &result.name_hash))
{
@ -8716,10 +8718,9 @@ static lns_prepared_args prepare_tx_extra_loki_name_system_values(wallet2 const
{
cryptonote::rpc::LNS_NAMES_TO_OWNERS::request request = {};
{
request.entries.emplace_back();
auto &request_entry = request.entries.back();
request_entry.name_hash = epee::string_encoding::base64_encode(reinterpret_cast<unsigned char const *>(result.name_hash.data), sizeof(result.name_hash));
request_entry.types.push_back(static_cast<uint16_t>(type));
auto &request_entry = request.entries.emplace_back();
request_entry.name_hash = lokimq::to_base64(tools::view_guts(result.name_hash));
request_entry.types.push_back(lns::db_mapping_type(type));
}
auto [success, response_] = wallet.lns_names_to_owners(request);
@ -8742,7 +8743,7 @@ static lns_prepared_args prepare_tx_extra_loki_name_system_values(wallet2 const
}
}
if (make_signature)
if (txtype == lns::lns_tx_type::update && make_signature)
{
if (response->empty())
{
@ -8752,21 +8753,31 @@ static lns_prepared_args prepare_tx_extra_loki_name_system_values(wallet2 const
cryptonote::address_parse_info curr_owner_parsed = {};
cryptonote::address_parse_info curr_backup_owner_parsed = {};
bool curr_owner = cryptonote::get_account_address_from_str(curr_owner_parsed, wallet.nettype(), (*response)[0].owner);
bool curr_backup_owner = cryptonote::get_account_address_from_str(curr_backup_owner_parsed, wallet.nettype(), (*response)[0].backup_owner);
if (!try_generate_lns_signature(wallet, (*response)[0].owner, owner, backup_owner, result))
auto& rowner = response->front().owner;
auto& rbackup_owner = response->front().backup_owner;
bool curr_owner = cryptonote::get_account_address_from_str(curr_owner_parsed, wallet.nettype(), rowner);
bool curr_backup_owner = rbackup_owner && cryptonote::get_account_address_from_str(curr_backup_owner_parsed, wallet.nettype(), *rbackup_owner);
if (!try_generate_lns_signature(wallet, rowner, owner, backup_owner, result))
{
if (!try_generate_lns_signature(wallet, (*response)[0].backup_owner, owner, backup_owner, result))
if (!rbackup_owner || !try_generate_lns_signature(wallet, *rbackup_owner, owner, backup_owner, result))
{
if (reason)
{
*reason = "Signature requested when preparing LNS TX, but this wallet is not the owner of the record owner=" + (*response)[0].owner;
if ((*response)[0].backup_owner.size()) *reason += ", backup_owner=" + (*response)[0].backup_owner;
*reason = "Signature requested when preparing LNS TX, but this wallet is not the owner of the record owner=" + rowner;
if (rbackup_owner) *reason += ", backup_owner=" + *rbackup_owner;
}
return result;
}
}
}
else if (txtype == lns::lns_tx_type::renew)
{
if (response->empty())
{
if (reason) *reason = "Renewal requested but record to renew does not exist or has expired";
return result;
}
}
}
result.prepared = true;
@ -8784,7 +8795,8 @@ std::vector<wallet2::pending_tx> wallet2::lns_create_buy_mapping_tx(lns::mapping
std::set<uint32_t> subaddr_indices)
{
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> response;
lns_prepared_args prepared_args = prepare_tx_extra_loki_name_system_values(*this, type, priority, name, &value, owner, backup_owner, false /*make_signature*/, account_index, reason, &response);
constexpr bool make_signature = false;
lns_prepared_args prepared_args = prepare_tx_extra_loki_name_system_values(*this, type, priority, name, &value, owner, backup_owner, make_signature, lns::lns_tx_type::buy, account_index, reason, &response);
if (!owner)
prepared_args.owner = lns::make_monero_owner(get_subaddress({account_index, 0}), account_index != 0);
@ -8830,14 +8842,86 @@ std::vector<wallet2::pending_tx> wallet2::lns_create_buy_mapping_tx(std::string
uint32_t account_index,
std::set<uint32_t> subaddr_indices)
{
lns::mapping_type mapping_type = lns::mapping_type::session;
if (!lns::validate_mapping_type(type, &mapping_type, reason))
std::optional<uint8_t> hf_version = get_hard_fork_version();
if (!hf_version)
{
if (reason) *reason = ERR_MSG_NETWORK_VERSION_QUERY_FAILED;
return {};
}
lns::mapping_type mapping_type;
if (!lns::validate_mapping_type(type, *hf_version, lns::lns_tx_type::buy, &mapping_type, reason))
return {};
std::vector<wallet2::pending_tx> result = lns_create_buy_mapping_tx(mapping_type, owner, backup_owner, name, value, reason, priority, account_index, subaddr_indices);
return result;
}
std::vector<wallet2::pending_tx> wallet2::lns_create_renewal_tx(
lns::mapping_type type,
std::string name,
std::string *reason,
uint32_t priority,
uint32_t account_index,
std::set<uint32_t> subaddr_indices,
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> *response
)
{
constexpr bool make_signature = false;
lns_prepared_args prepared_args = prepare_tx_extra_loki_name_system_values(*this, type, priority, name, nullptr, nullptr, nullptr, make_signature, lns::lns_tx_type::renew, account_index, reason, response);
if (!prepared_args)
return {};
std::vector<uint8_t> extra;
auto entry = cryptonote::tx_extra_loki_name_system::make_renew(
type,
prepared_args.name_hash,
prepared_args.prev_txid);
add_loki_name_system_to_tx_extra(extra, entry);
std::optional<uint8_t> hf_version = get_hard_fork_version();
if (!hf_version)
{
if (reason) *reason = ERR_MSG_NETWORK_VERSION_QUERY_FAILED;
return {};
}
loki_construct_tx_params tx_params = wallet2::construct_params(*hf_version, txtype::loki_name_system, priority, type);
auto result = create_transactions_2({} /*dests*/,
CRYPTONOTE_DEFAULT_TX_MIXIN,
0 /*unlock_at_block*/,
priority,
extra,
account_index,
subaddr_indices,
tx_params);
return result;
}
std::vector<wallet2::pending_tx> wallet2::lns_create_renewal_tx(
std::string const &type,
std::string const &name,
std::string *reason,
uint32_t priority,
uint32_t account_index,
std::set<uint32_t> subaddr_indices,
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> *response
)
{
lns::mapping_type mapping_type = lns::mapping_type::session;
std::optional<uint8_t> hf_version = get_hard_fork_version();
if (!hf_version)
{
if (reason) *reason = ERR_MSG_NETWORK_VERSION_QUERY_FAILED;
return {};
}
if (!lns::validate_mapping_type(type, *hf_version, lns::lns_tx_type::renew, &mapping_type, reason))
return {};
return lns_create_renewal_tx(mapping_type, name, reason, priority, account_index, subaddr_indices, response);
}
std::vector<wallet2::pending_tx> wallet2::lns_create_update_mapping_tx(lns::mapping_type type,
std::string name,
std::string const *value,
@ -8857,7 +8941,7 @@ std::vector<wallet2::pending_tx> wallet2::lns_create_update_mapping_tx(lns::mapp
}
bool make_signature = signature == nullptr;
lns_prepared_args prepared_args = prepare_tx_extra_loki_name_system_values(*this, type, priority, name, value, owner, backup_owner, make_signature, account_index, reason, response);
lns_prepared_args prepared_args = prepare_tx_extra_loki_name_system_values(*this, type, priority, name, value, owner, backup_owner, make_signature, lns::lns_tx_type::update, account_index, reason, response);
if (!prepared_args) return {};
if (!make_signature)
@ -8910,7 +8994,13 @@ std::vector<wallet2::pending_tx> wallet2::lns_create_update_mapping_tx(std::stri
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> *response)
{
lns::mapping_type mapping_type = lns::mapping_type::session;
if (!lns::validate_mapping_type(type, &mapping_type, reason))
std::optional<uint8_t> hf_version = get_hard_fork_version();
if (!hf_version)
{
if (reason) *reason = ERR_MSG_NETWORK_VERSION_QUERY_FAILED;
return {};
}
if (!lns::validate_mapping_type(type, *hf_version, lns::lns_tx_type::update, &mapping_type, reason))
return {};
std::vector<wallet2::pending_tx> result = lns_create_update_mapping_tx(mapping_type, name, value, owner, backup_owner, signature, reason, priority, account_index, subaddr_indices, response);
@ -8953,7 +9043,8 @@ bool wallet2::lns_make_update_mapping_signature(lns::mapping_type type,
std::string *reason)
{
std::vector<cryptonote::rpc::LNS_NAMES_TO_OWNERS::response_entry> response;
lns_prepared_args prepared_args = prepare_tx_extra_loki_name_system_values(*this, type, tx_priority_unimportant, name, value, owner, backup_owner, true /*make_signature*/, account_index, reason, &response);
constexpr bool make_signature = true;
lns_prepared_args prepared_args = prepare_tx_extra_loki_name_system_values(*this, type, tx_priority_unimportant, name, value, owner, backup_owner, make_signature, lns::lns_tx_type::update, account_index, reason, &response);
if (!prepared_args) return false;
if (prepared_args.prev_txid == crypto::null_hash)

View File

@ -1347,6 +1347,10 @@ private:
std::vector<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<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 = {});
// LNS renewal (for lokinet registrations, not for session/wallet)
std::vector<pending_tx> lns_create_renewal_tx(lns::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::LNS_NAMES_TO_OWNERS::response_entry> *response = {});
std::vector<pending_tx> lns_create_renewal_tx(std::string const &type, std::string const &name, 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);

View File

@ -3060,6 +3060,36 @@ namespace {
return res;
}
LNS_RENEW_MAPPING::response wallet_rpc_server::invoke(LNS_RENEW_MAPPING::request&& req)
{
require_open();
LNS_RENEW_MAPPING::response res{};
std::string reason;
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->lns_create_renewal_tx(
req.type, req.name, &reason, req.priority, req.account_index, req.subaddr_indices);
if (ptx_vector.empty())
throw wallet_rpc_error{error_code::TX_NOT_POSSIBLE, "Failed to create LNS transaction: " + reason};
fill_response( ptx_vector,
req.get_tx_key,
res.tx_key,
res.amount,
res.fee,
res.multisig_txset,
res.unsigned_txset,
req.do_not_relay,
false /*blink*/,
res.tx_hash,
req.get_tx_hex,
res.tx_blob,
req.get_tx_metadata,
res.tx_metadata);
return res;
}
LNS_UPDATE_MAPPING::response wallet_rpc_server::invoke(LNS_UPDATE_MAPPING::request&& req)
{
require_open();
@ -3106,13 +3136,15 @@ namespace {
std::string reason;
lns::mapping_type type;
if (!lns::validate_mapping_type(req.type, &type, &reason))
std::optional<uint8_t> hf_version = m_wallet->get_hard_fork_version();
if (!hf_version) throw wallet_rpc_error{error_code::HF_QUERY_FAILED, tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED};
if (!lns::validate_mapping_type(req.type, *hf_version, lns::lns_tx_type::update, &type, &reason))
throw wallet_rpc_error{error_code::WRONG_LNS_TYPE, "Wrong lns type given=" + reason};
lns::generic_signature signature;
if (!m_wallet->lns_make_update_mapping_signature(type,
req.name,
req.value.size() ? &req.value : nullptr,
req.encrypted_value.size() ? &req.encrypted_value : nullptr,
req.owner.size() ? &req.owner : nullptr,
req.backup_owner.size() ? &req.backup_owner : nullptr,
signature,
@ -3131,7 +3163,9 @@ namespace {
std::string reason;
lns::mapping_type type;
if (!lns::validate_mapping_type(req.type, &type, &reason))
std::optional<uint8_t> hf_version = m_wallet->get_hard_fork_version();
if (!hf_version) throw wallet_rpc_error{error_code::HF_QUERY_FAILED, tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED};
if (!lns::validate_mapping_type(req.type, *hf_version, lns::lns_tx_type::lookup, &type, &reason))
throw wallet_rpc_error{error_code::WRONG_LNS_TYPE, "Wrong lns type given=" + reason};
if (!lns::validate_lns_name(type, req.name, &reason))
@ -3167,12 +3201,15 @@ namespace {
// ---------------------------------------------------------------------------------------------
std::string reason;
lns::mapping_type type = {};
std::optional<uint8_t> hf_version = m_wallet->get_hard_fork_version();
if (!hf_version) throw wallet_rpc_error{error_code::HF_QUERY_FAILED, tools::ERR_MSG_NETWORK_VERSION_QUERY_FAILED};
{
if (!lns::validate_mapping_type(req.type, &type, &reason))
throw wallet_rpc_error{error_code::WRONG_LNS_TYPE, "Wrong lns type given=" + reason};
if (!lns::validate_mapping_type(req.type, *hf_version, lns::lns_tx_type::lookup, &type, &reason))
throw wallet_rpc_error{error_code::WRONG_LNS_TYPE, "Invalid LNS type: " + reason};
if (!lns::validate_lns_name(type, req.name, &reason))
throw wallet_rpc_error{error_code::LNS_VALUE_NOT_HEX, "Value is not hex=" + req.encrypted_value};
throw wallet_rpc_error{error_code::LNS_BAD_NAME, "Invalid LNS name '" + req.name + "': " + reason};
}
// ---------------------------------------------------------------------------------------------

View File

@ -157,6 +157,7 @@ namespace tools
wallet_rpc::CAN_REQUEST_STAKE_UNLOCK::response invoke(wallet_rpc::CAN_REQUEST_STAKE_UNLOCK::request&& req);
wallet_rpc::REQUEST_STAKE_UNLOCK::response invoke(wallet_rpc::REQUEST_STAKE_UNLOCK::request&& req);
wallet_rpc::LNS_BUY_MAPPING::response invoke(wallet_rpc::LNS_BUY_MAPPING::request&& req);
wallet_rpc::LNS_RENEW_MAPPING::response invoke(wallet_rpc::LNS_RENEW_MAPPING::request&& req);
wallet_rpc::LNS_UPDATE_MAPPING::response invoke(wallet_rpc::LNS_UPDATE_MAPPING::request&& req);
wallet_rpc::LNS_MAKE_UPDATE_SIGNATURE::response invoke(wallet_rpc::LNS_MAKE_UPDATE_SIGNATURE::request&& req);
wallet_rpc::LNS_HASH_NAME::response invoke(wallet_rpc::LNS_HASH_NAME::request&& req);

View File

@ -1146,6 +1146,19 @@ KV_SERIALIZE_MAP_CODE_BEGIN(LNS_BUY_MAPPING::response)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_RENEW_MAPPING::request)
KV_SERIALIZE (type);
KV_SERIALIZE (name);
KV_SERIALIZE_OPT(account_index, (uint32_t)0);
KV_SERIALIZE (subaddr_indices);
KV_SERIALIZE_OPT(priority, (uint32_t)0);
KV_SERIALIZE (get_tx_key)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
KV_SERIALIZE_OPT(get_tx_metadata, false)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(LNS_UPDATE_MAPPING::request)
KV_SERIALIZE (type);
KV_SERIALIZE (name);

View File

@ -420,7 +420,7 @@ namespace tools::wallet_rpc {
struct response
{
std::string tx_hash; // Publically searchable transaction hash.
std::string tx_hash; // Publicly searchable transaction hash.
std::string tx_key; // Transaction key if get_tx_key is true, otherwise, blank string.
uint64_t amount; // Amount transferred for the transaction.
uint64_t fee; // Fee charged for the txn.
@ -2007,7 +2007,7 @@ namespace tools::wallet_rpc {
struct response
{
std::string tx_hash; // Publically searchable transaction hash.
std::string tx_hash; // Publicly searchable transaction hash.
std::string tx_key; // Transaction key if `get_tx_key` is `true`, otherwise, blank string.
uint64_t amount; // Amount transferred for the transaction in atomic units.
uint64_t fee; // Value in atomic units of the fee charged for the tx.
@ -2039,7 +2039,7 @@ namespace tools::wallet_rpc {
struct response
{
std::string tx_hash; // Publically searchable transaction hash.
std::string tx_hash; // Publicly searchable transaction hash.
std::string tx_key; // Transaction key if get_tx_key is true, otherwise, blank string.
uint64_t amount; // Amount transferred for the transaction in atomic units.
uint64_t fee; // Value in atomic units of the fee charged for the tx.
@ -2185,18 +2185,21 @@ namespace tools::wallet_rpc {
static constexpr auto names() { return NAMES("lns_buy_mapping"); }
static constexpr const char *description =
R"(Buy a Loki Name System mapping. Loki Name System allows multiple owners that are authorised to update the underlying mapping. An owner can be either a ed25519 public key or a wallet address. By default if no owner is specified, the purchasing wallet's active [sub]address is stored as the owner.
- For Session, the recommended owner is the ed25519 public key of the user's Session ID set to owner
R"(Buy a Loki Name System (LNS) mapping that maps a unique name to a Session ID or Lokinet address.
In future, support for mappings on Blockchain wallets and Lokinet addresses will be available. The recommended owner is the wallet's [sub]address.
Currently supports Session and Lokinet registrations. Lokinet registrations can be for 1, 2, 5, or 10 years by specifying a type value of "lokinet", "lokinet_2y", "lokinet_5y", "lokinet_10y". Session registrations do not expire.
When specifying owners, either a wallet [sub]address or standard ed25519 public key is supported per mapping. Updating the value that a name maps to requires one of the owner's to sign the update transaction. For wallets, this is signed using the subaddress's spend key.
The owner of the LNS entry (by default, the purchasing wallet) will be permitted to submit LNS update transactions to the Loki blockchain (for example to update a Session pubkey or the target Lokinet address). You may change the primary owner or add a backup owner in the registration and can change them later with update transactions. Owner addresses can be either Loki wallets, or generic ed25519 pubkeys (for advanced uses).
For information on updating & signing, refer to COMMAND_RPC_LNS_UPDATE_MAPPING)";
For Session, the recommended owner or backup owner is the ed25519 public key of the user's Session ID.
When specifying owners, either a wallet (sub)address or standard ed25519 public key is supported per mapping. Updating the value that a name maps to requires one of the owners to sign the update transaction. For wallets, this is signed using the (sub)address's spend key.
For more information on updating and signing see the LNS_UPDATE_MAPPING documentation.)";
struct request
{
std::string type; // The mapping type, currently we only support "session". In future "lokinet" and "blockchain" mappings will be available.
std::string type; // The mapping type: "session", "lokinet", "lokinet_2y", "lokinet_5y", "lokinet_10y".
std::string owner; // (Optional): The ed25519 public key or wallet address that has authority to update the mapping.
std::string backup_owner; // (Optional): The secondary, backup public key that has authority to update the mapping.
std::string name; // The name to purchase via Loki Name Service
@ -2215,7 +2218,7 @@ For information on updating & signing, refer to COMMAND_RPC_LNS_UPDATE_MAPPING)"
struct response
{
std::string tx_hash; // Publically searchable transaction hash.
std::string tx_hash; // Publicly searchable transaction hash.
std::string tx_key; // Transaction key if `get_tx_key` is `true`, otherwise, blank string.
uint64_t amount; // Amount transferred for the transaction in atomic units.
uint64_t fee; // Value in atomic units of the fee charged for the tx.
@ -2228,6 +2231,36 @@ For information on updating & signing, refer to COMMAND_RPC_LNS_UPDATE_MAPPING)"
};
};
LOKI_RPC_DOC_INTROSPECT
// Renew an active lokinet LNS registration
struct LNS_RENEW_MAPPING : RESTRICTED
{
static constexpr auto names() { return NAMES("lns_renew_mapping"); }
static constexpr const char *description =
R"(Renews a Loki Name System lokinet mapping by adding to the existing expiry time.
The renewal can be for 1, 2, 5, or 10 years by specifying a `type` value of "lokinet_2y", "lokinet_10y", etc.)";
struct request
{
std::string type; // The mapping type, "lokinet" (1-year), or "lokinet_2y", "lokinet_5y", "lokinet_10y" for multi-year registrations.
std::string name; // The name to update
uint32_t account_index; // (Optional) Transfer from this account index. (Defaults to 0)
std::set<uint32_t> subaddr_indices; // (Optional) Transfer from this set of subaddresses. (Defaults to 0)
uint32_t priority; // Set a priority for the transaction. Accepted values are: 0-4 for: default, unimportant, normal, elevated, priority.
bool get_tx_key; // (Optional) Return the transaction key after sending.
bool do_not_relay; // (Optional) If true, the newly created transaction will not be relayed to the loki network. (Defaults to false)
bool get_tx_hex; // Return the transaction as hex string after sending (Defaults to false)
bool get_tx_metadata; // Return the metadata needed to relay the transaction. (Defaults to false)
KV_MAP_SERIALIZABLE
};
using response = LNS_BUY_MAPPING::response;
};
LOKI_RPC_DOC_INTROSPECT
// Update the underlying value in the name->value mapping via Loki Name Service.
struct LNS_UPDATE_MAPPING : RESTRICTED
@ -2235,20 +2268,19 @@ For information on updating & signing, refer to COMMAND_RPC_LNS_UPDATE_MAPPING)"
static constexpr auto names() { return NAMES("lns_update_mapping"); }
static constexpr const char *description =
R"(Update a Loki Name System mapping's underlying value. The pre-existing owner (wallet address/ed25519 public key) of the mapping must be able to validate. At least one field must be specified to update, otherwise the funtion returns an error message.
R"(Update a Loki Name System mapping to refer to a new address or owner.
The signature is generated from signing a hash generated by using Libsodium's generichash on the {new value (if provided), new owner (if provided), new backup_owner (if provided), prev_txid field (in the current mapping to update)} in binary.
Depending on the owner associated with the mapping the signature can be generated in 2 ways. If the owner associated with the mapping is-
- a wallet [sub]address, the signature is generated by using Monero's crypto::generate_signature on the hash with the [sub]address's spend key.
- a ed25519 public key, the signature is generated by using Libsodium's crypto_sign_detached on the hash
At least one field (value, owner, or backup owner) must be specified in the update.
Providing the signature is an optional field and if not provided, will default to attempting to sign using the wallet's active [sub]address's spend keys.)";
The existing owner (wallet address or ed25519 public key) of the mapping must be used to sign the update. If no signature is provided then the wallet's active address (or subaddress) will be used to sign the update.
If signing is performed externally then you must first encrypt the `value` (if being updated), then sign a BLAKE2b hash of {encryptedvalue || owner || backup_owner || txid} (where txid is the most recent LNS update or registration transaction of this mapping; each of encrypted/owner/backup are empty strings if not being updated). For a wallet owner this is signed using the owning wallet's spend key; for a Ed25519 key this is a standard Ed25519 signature.)";
struct request
{
std::string type; // The mapping type, currently we only support "session". In future "lokinet" and "blockchain" mappings will be available.
std::string type; // The mapping type, "session" or "lokinet".
std::string name; // The name to update via Loki Name Service
std::string value; // (Optional): The new value that the name maps to via Loki Name Service. If not specified or given the empty string "", then the mapping's value remains unchanged.
std::string value; // (Optional): The new value that the name maps to via Loki Name Service. If not specified or given the empty string "", then the mapping's value remains unchanged. If using a `signature` then this value (if non-empty) must be already encrypted.
std::string owner; // (Optional): The new owner of the mapping. If not specified or given the empty string "", then the mapping's owner remains unchanged.
std::string backup_owner; // (Optional): The new backup owner of the mapping. If not specified or given the empty string "", then the mapping's backup owner remains unchanged.
std::string signature; // (Optional): Signature derived using libsodium generichash on {current txid blob, new value blob} of the mapping to update. By default the hash is signed using the wallet's spend key as an ed25519 keypair, if signature is specified.
@ -2267,7 +2299,7 @@ Providing the signature is an optional field and if not provided, will default t
struct response
{
std::string tx_hash; // Publically searchable transaction hash.
std::string tx_hash; // Publicly searchable transaction hash.
std::string tx_key; // Transaction key if `get_tx_key` is `true`, otherwise, blank string.
uint64_t amount; // Amount transferred for the transaction in atomic units.
uint64_t fee; // Value in atomic units of the fee charged for the tx.
@ -2318,7 +2350,7 @@ This command is only required if the open wallet is one of the owners of a LNS r
struct request
{
std::string type; // The mapping type, currently we only support "session". In future "lokinet" and "blockchain" mappings will be available.
std::string type; // The mapping type, "session" or "lokinet".
std::string name; // The desired name to hash
KV_MAP_SERIALIZABLE

View File

@ -72,17 +72,28 @@ loki_generate_sequential_hard_fork_table(uint8_t max_hf_version)
assert(max_hf_version < cryptonote::network_version_count);
std::vector<std::pair<uint8_t, uint64_t>> result = {};
uint64_t version_height = 0;
for (uint8_t version = cryptonote::network_version_7; version <= max_hf_version; version++)
// HF15 reduces and HF16 eliminates miner block rewards, so we need to ensure we have enough
// pre-HF15 blocks to generate enough LOKI for tests:
constexpr uint64_t pos_delay = 60;
bool delayed = false;
for (uint8_t version = cryptonote::network_version_7; version <= max_hf_version; )
{
if (version == cryptonote::network_version_15_lns)
if (version >= cryptonote::network_version_15_lns && !delayed)
{
version_height += 60;
result.emplace_back(std::make_pair(version, version_height++));
version_height += pos_delay;
delayed = true;
}
result.emplace_back(version, version_height++);
// If we're going to HF16 or above, just skip over the RandomX version (HF12 through 15) so that
// we can avoid initializing the RandomX engine (which slows down the test suite quite
// substantially).
if (max_hf_version >= cryptonote::network_version_16 && version == cryptonote::network_version_11_infinite_staking)
version = cryptonote::network_version_16;
else
{
result.emplace_back(std::make_pair(version, version_height++));
}
version++;
}
return result;
}
@ -283,6 +294,11 @@ void loki_chain_generator::add_mined_money_unlock_blocks()
add_n_blocks(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
}
void loki_chain_generator::add_transfer_unlock_blocks()
{
add_n_blocks(CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
}
void loki_chain_generator::add_tx(cryptonote::transaction const &tx, bool can_be_added_to_blockchain, std::string const &fail_msg, bool kept_by_block)
{
loki_transaction tx_entry = {tx, kept_by_block};
@ -292,6 +308,7 @@ void loki_chain_generator::add_tx(cryptonote::transaction const &tx, bool can_be
cryptonote::transaction
loki_chain_generator::create_and_add_loki_name_system_tx(cryptonote::account_base const &src,
uint8_t hf_version,
lns::mapping_type type,
std::string const &name,
lns::mapping_value const &value,
@ -299,13 +316,14 @@ loki_chain_generator::create_and_add_loki_name_system_tx(cryptonote::account_bas
lns::generic_owner const *backup_owner,
bool kept_by_block)
{
cryptonote::transaction t = create_loki_name_system_tx(src, type, name, value, owner, backup_owner);
cryptonote::transaction t = create_loki_name_system_tx(src, hf_version, type, name, value, owner, backup_owner);
add_tx(t, true /*can_be_added_to_blockchain*/, ""/*fail_msg*/, kept_by_block);
return t;
}
cryptonote::transaction
loki_chain_generator::create_and_add_loki_name_system_tx_update(cryptonote::account_base const &src,
uint8_t hf_version,
lns::mapping_type type,
std::string const &name,
lns::mapping_value const *value,
@ -314,11 +332,24 @@ loki_chain_generator::create_and_add_loki_name_system_tx_update(cryptonote::acco
lns::generic_signature *signature,
bool kept_by_block)
{
cryptonote::transaction t = create_loki_name_system_tx_update(src, type, name, value, owner, backup_owner, signature);
cryptonote::transaction t = create_loki_name_system_tx_update(src, hf_version, type, name, value, owner, backup_owner, signature);
add_tx(t, true /*can_be_added_to_blockchain*/, ""/*fail_msg*/, kept_by_block);
return t;
}
cryptonote::transaction
loki_chain_generator::create_and_add_loki_name_system_tx_renew(cryptonote::account_base const &src,
uint8_t hf_version,
lns::mapping_type type,
std::string const &name,
bool kept_by_block)
{
cryptonote::transaction t = create_loki_name_system_tx_renew(src, hf_version, type, name);
add_tx(t, true /*can_be_added_to_blockchain*/, ""/*fail_msg*/, kept_by_block);
return t;
}
cryptonote::transaction loki_chain_generator::create_and_add_tx(const cryptonote::account_base &src,
const cryptonote::account_public_address &dest,
uint64_t amount,
@ -526,12 +557,13 @@ cryptonote::checkpoint_t loki_chain_generator::create_service_node_checkpoint(ui
}
cryptonote::transaction loki_chain_generator::create_loki_name_system_tx(cryptonote::account_base const &src,
uint8_t hf_version,
lns::mapping_type type,
std::string const &name,
lns::mapping_value const &value,
lns::generic_owner const *owner,
lns::generic_owner const *backup_owner,
uint64_t burn) const
std::optional<uint64_t> burn_override) const
{
lns::generic_owner generic_owner = {};
if (owner)
@ -546,13 +578,12 @@ cryptonote::transaction loki_chain_generator::create_loki_name_system_tx(crypton
cryptonote::block const &head = top().block;
uint64_t new_height = get_block_height(top().block) + 1;
uint8_t new_hf_version = get_hf_version_at(new_height);
if (burn == LNS_AUTO_BURN)
burn = lns::burn_needed(new_hf_version, type);
uint64_t burn = burn_override.value_or(lns::burn_needed(new_hf_version, type));
crypto::hash name_hash = lns::name_to_hash(name);
std::string name_base64_hash = lns::name_to_base64_hash(name);
crypto::hash prev_txid = crypto::null_hash;
if (lns::mapping_record mapping = lns_db_->get_mapping(type, name_base64_hash))
if (lns::mapping_record mapping = lns_db_->get_mapping(type, name_base64_hash, new_height))
prev_txid = mapping.txid;
lns::mapping_value encrypted_value = value;
@ -574,6 +605,7 @@ cryptonote::transaction loki_chain_generator::create_loki_name_system_tx(crypton
}
cryptonote::transaction loki_chain_generator::create_loki_name_system_tx_update(cryptonote::account_base const &src,
uint8_t hf_version,
lns::mapping_type type,
std::string const &name,
lns::mapping_value const *value,
@ -630,7 +662,7 @@ cryptonote::transaction loki_chain_generator::create_loki_name_system_tx_update(
}
cryptonote::transaction
loki_chain_generator::create_loki_name_system_tx_update_w_extra(cryptonote::account_base const &src, cryptonote::tx_extra_loki_name_system const &lns_extra) const
loki_chain_generator::create_loki_name_system_tx_update_w_extra(cryptonote::account_base const &src, uint8_t hf_version, cryptonote::tx_extra_loki_name_system const &lns_extra) const
{
std::vector<uint8_t> extra;
cryptonote::add_loki_name_system_to_tx_extra(extra, lns_extra);
@ -648,6 +680,40 @@ loki_chain_generator::create_loki_name_system_tx_update_w_extra(cryptonote::acco
return result;
}
cryptonote::transaction loki_chain_generator::create_loki_name_system_tx_renew(cryptonote::account_base const &src,
uint8_t hf_version,
lns::mapping_type type,
std::string const &name,
std::optional<uint64_t> burn_override) const
{
crypto::hash name_hash = lns::name_to_hash(name);
crypto::hash prev_txid = {};
{
std::string name_base64_hash = lns::name_to_base64_hash(name);
lns::mapping_record mapping = lns_db_->get_mapping(type, name_base64_hash);
prev_txid = mapping.txid;
}
uint8_t new_hf_version = get_hf_version_at(get_block_height(top().block) + 1);
uint64_t burn = burn_override.value_or(lns::burn_needed(new_hf_version, type));
std::vector<uint8_t> extra;
cryptonote::tx_extra_loki_name_system data = cryptonote::tx_extra_loki_name_system::make_renew(type, name_hash, prev_txid);
cryptonote::add_loki_name_system_to_tx_extra(extra, data);
cryptonote::add_burned_amount_to_tx_extra(extra, burn);
cryptonote::block const &head = top().block;
cryptonote::transaction result = {};
loki_tx_builder(events_, result, head, src /*from*/, src.get_keys().m_account_address, 0 /*amount*/, new_hf_version)
.with_tx_type(cryptonote::txtype::loki_name_system)
.with_extra(extra)
.with_fee(burn + TESTS_DEFAULT_FEE)
.build();
return result;
}
static void fill_nonce_with_test_generator(test_generator *generator, cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height)
{
cryptonote::randomx_longhash_context randomx_context = {};

View File

@ -1421,6 +1421,7 @@ struct loki_chain_generator
void add_blocks_until_next_checkpointable_height();
void add_service_node_checkpoint(uint64_t block_height, size_t num_votes);
void add_mined_money_unlock_blocks(); // NOTE: Unlock all Loki generated from mining prior to this call i.e. CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW
void add_transfer_unlock_blocks(); // Unlock funds from (standard) transfers prior to this call, i.e. CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
// NOTE: Add an event that is just a user specified message to signify progress in the test
void add_event_msg(std::string const &msg) { events_.push_back(msg); }
@ -1428,8 +1429,9 @@ struct loki_chain_generator
// NOTE: Add constructed TX to events_ and assume that it is valid to add to the blockchain. If the TX is meant to be unaddable to the blockchain use the individual create + add functions to
// be able to mark the add TX event as something that should trigger a failure.
cryptonote::transaction create_and_add_loki_name_system_tx(cryptonote::account_base const &src, lns::mapping_type type, std::string const &name, lns::mapping_value const &value, lns::generic_owner const *owner = nullptr, lns::generic_owner const *backup_owner = nullptr, bool kept_by_block = false);
cryptonote::transaction create_and_add_loki_name_system_tx_update(cryptonote::account_base const &src, lns::mapping_type type, std::string const &name, lns::mapping_value const *value, lns::generic_owner const *owner = nullptr, lns::generic_owner const *backup_owner = nullptr, lns::generic_signature *signature = nullptr, bool kept_by_block = false);
cryptonote::transaction create_and_add_loki_name_system_tx(cryptonote::account_base const &src, uint8_t hf_version, lns::mapping_type type, std::string const &name, lns::mapping_value const &value, lns::generic_owner const *owner = nullptr, lns::generic_owner const *backup_owner = nullptr, bool kept_by_block = false);
cryptonote::transaction create_and_add_loki_name_system_tx_update(cryptonote::account_base const &src, uint8_t hf_version, lns::mapping_type type, std::string const &name, lns::mapping_value const *value, lns::generic_owner const *owner = nullptr, lns::generic_owner const *backup_owner = nullptr, lns::generic_signature *signature = nullptr, bool kept_by_block = false);
cryptonote::transaction create_and_add_loki_name_system_tx_renew(cryptonote::account_base const &src, uint8_t hf_version, lns::mapping_type type, std::string const &name, bool kept_by_block = false);
cryptonote::transaction create_and_add_tx (const cryptonote::account_base& src, const cryptonote::account_public_address& dest, uint64_t amount, uint64_t fee = TESTS_DEFAULT_FEE, bool kept_by_block = false);
cryptonote::transaction create_and_add_state_change_tx(service_nodes::new_state state, const crypto::public_key& pub_key, uint64_t height = -1, const std::vector<uint64_t>& voters = {}, uint64_t fee = 0, bool kept_by_block = false);
cryptonote::transaction create_and_add_registration_tx(const cryptonote::account_base& src, const cryptonote::keypair& sn_keys = cryptonote::keypair::generate(hw::get_device("default")), bool kept_by_block = false);
@ -1450,9 +1452,10 @@ struct loki_chain_generator
// value: Takes the binary value NOT the human readable version, of the name->value mapping
static const uint64_t LNS_AUTO_BURN = static_cast<uint64_t>(-1);
cryptonote::transaction create_loki_name_system_tx(cryptonote::account_base const &src, lns::mapping_type type, std::string const &name, lns::mapping_value const &value, lns::generic_owner const *owner = nullptr, lns::generic_owner const *backup_owner = nullptr, uint64_t burn = LNS_AUTO_BURN) const;
cryptonote::transaction create_loki_name_system_tx_update(cryptonote::account_base const &src, lns::mapping_type type, std::string const &name, lns::mapping_value const *value, lns::generic_owner const *owner = nullptr, lns::generic_owner const *backup_owner = nullptr, lns::generic_signature *signature = nullptr, bool use_asserts = false) const;
cryptonote::transaction create_loki_name_system_tx_update_w_extra(cryptonote::account_base const &src, cryptonote::tx_extra_loki_name_system const &lns_extra) const;
cryptonote::transaction create_loki_name_system_tx(cryptonote::account_base const &src, uint8_t hf_version, lns::mapping_type type, std::string const &name, lns::mapping_value const &value, lns::generic_owner const *owner = nullptr, lns::generic_owner const *backup_owner = nullptr, std::optional<uint64_t> burn_override = std::nullopt) const;
cryptonote::transaction create_loki_name_system_tx_update(cryptonote::account_base const &src, uint8_t hf_version, lns::mapping_type type, std::string const &name, lns::mapping_value const *value, lns::generic_owner const *owner = nullptr, lns::generic_owner const *backup_owner = nullptr, lns::generic_signature *signature = nullptr, bool use_asserts = false) const;
cryptonote::transaction create_loki_name_system_tx_update_w_extra(cryptonote::account_base const &src, uint8_t hf_version, cryptonote::tx_extra_loki_name_system const &lns_extra) const;
cryptonote::transaction create_loki_name_system_tx_renew(cryptonote::account_base const &src, uint8_t hf_version, lns::mapping_type type, std::string const &name, std::optional<uint64_t> burn_override = std::nullopt) const;
loki_blockchain_entry create_genesis_block(const cryptonote::account_base &miner, uint64_t timestamp);
loki_blockchain_entry create_next_block(const std::vector<cryptonote::transaction>& txs = {}, cryptonote::checkpoint_t const *checkpoint = nullptr, uint64_t total_fee = 0);

View File

@ -1059,6 +1059,7 @@ static bool verify_lns_mapping_record(char const *perr_context,
lns::mapping_value const &value,
uint64_t register_height,
uint64_t update_height,
std::optional<uint64_t> expiration_height,
crypto::hash const &txid,
crypto::hash const &prev_txid,
lns::generic_owner const &owner,
@ -1072,6 +1073,9 @@ static bool verify_lns_mapping_record(char const *perr_context,
CHECK_EQ(decrypted, value);
CHECK_EQ(record.register_height, register_height);
CHECK_EQ(record.update_height, update_height);
CHECK_EQ(record.expiration_height.has_value(), expiration_height.has_value());
if (expiration_height)
CHECK_EQ(*record.expiration_height, *expiration_height);
CHECK_EQ(record.txid, txid);
CHECK_EQ(record.prev_txid, prev_txid);
CHECK_TEST_CONDITION_MSG(record.owner == owner, record.owner.to_string(cryptonote::FAKECHAIN) << " == "<< owner.to_string(cryptonote::FAKECHAIN));
@ -1093,7 +1097,7 @@ bool loki_name_system_disallow_reserved_type::generate(std::vector<test_event_en
auto unusable_type = static_cast<lns::mapping_type>(-1);
assert(!lns::mapping_type_allowed(gen.hardfork(), unusable_type));
cryptonote::transaction tx1 = gen.create_loki_name_system_tx(miner, unusable_type, "FriendlyName", mapping_value);
cryptonote::transaction tx1 = gen.create_loki_name_system_tx(miner, gen.hardfork(), unusable_type, "FriendlyName", mapping_value);
gen.add_tx(tx1, false /*can_be_added_to_blockchain*/, "Can't create a LNS TX that requests a LNS type that is unused but reserved by the protocol");
return true;
}
@ -1124,6 +1128,13 @@ static lns_keys_t make_lns_keys(cryptonote::account_base const &src)
return result;
}
// Lokinet FAKECHAIN LNS expiry blocks
uint64_t lokinet_expiry(lns::mapping_type type) {
auto exp = lns::expiry_blocks(cryptonote::FAKECHAIN, type);
if (!exp) throw std::logic_error{"test suite bug: lokinet_expiry called with non-lokinet mapping type"};
return *exp;
}
bool loki_name_system_expiration::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
@ -1134,19 +1145,19 @@ bool loki_name_system_expiration::generate(std::vector<test_event_entry> &events
gen.add_mined_money_unlock_blocks();
lns_keys_t miner_key = make_lns_keys(miner);
for (auto mapping_type = lns::mapping_type::lokinet_1year;
for (auto mapping_type = lns::mapping_type::lokinet;
mapping_type <= lns::mapping_type::lokinet_10years;
mapping_type = static_cast<lns::mapping_type>(static_cast<uint16_t>(mapping_type) + 1))
{
std::string const name = "mydomain.loki";
if (lns::mapping_type_allowed(gen.hardfork(), mapping_type))
{
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, mapping_type, name, miner_key.lokinet_value);
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), mapping_type, name, miner_key.lokinet_value);
gen.create_and_add_next_block({tx});
crypto::hash tx_hash = cryptonote::get_transaction_hash(tx);
uint64_t height_of_lns_entry = gen.height();
uint64_t expected_expiry_block = height_of_lns_entry + lns::expiry_blocks(cryptonote::FAKECHAIN, mapping_type, nullptr);
uint64_t expected_expiry_block = height_of_lns_entry + lokinet_expiry(mapping_type);
std::string name_hash = lns::name_to_base64_hash(name);
loki_register_callback(events, "check_lns_entries", [=](cryptonote::core &c, size_t ev_index)
@ -1161,7 +1172,7 @@ bool loki_name_system_expiration::generate(std::vector<test_event_entry> &events
<< " == " << owner.address.to_string(cryptonote::FAKECHAIN));
lns::mapping_record record = lns_db.get_mapping(mapping_type, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet_1year, name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, tx_hash, crypto::null_hash, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet, name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, height_of_lns_entry + lokinet_expiry(mapping_type), tx_hash, crypto::null_hash, miner_key.owner, {} /*backup_owner*/));
return true;
});
@ -1182,14 +1193,14 @@ bool loki_name_system_expiration::generate(std::vector<test_event_entry> &events
<< " == " << owner.address.to_string(cryptonote::FAKECHAIN));
lns::mapping_record record = lns_db.get_mapping(mapping_type, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet_1year, name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, tx_hash, crypto::null_hash, miner_key.owner, {} /*backup_owner*/));
CHECK_EQ(record.active(cryptonote::FAKECHAIN, blockchain_height), false);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet, name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, height_of_lns_entry + lokinet_expiry(mapping_type), tx_hash, crypto::null_hash, miner_key.owner, {} /*backup_owner*/));
CHECK_EQ(record.active(blockchain_height), false);
return true;
});
}
else
{
cryptonote::transaction tx = gen.create_loki_name_system_tx(miner, mapping_type, name, miner_key.lokinet_value);
cryptonote::transaction tx = gen.create_loki_name_system_tx(miner, gen.hardfork(), mapping_type, name, miner_key.lokinet_value);
gen.add_tx(tx, false /*can_be_added_to_blockchain*/, "Can not add LNS TX that uses disallowed type");
}
}
@ -1209,9 +1220,13 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
{
gen.add_mined_money_unlock_blocks();
cryptonote::transaction transfer = gen.create_and_add_tx(miner, bob.get_keys().m_account_address, MK_COINS(400));
gen.create_and_add_next_block({transfer});
gen.add_mined_money_unlock_blocks();
// Chop this transfer into multiple txes because we need enough inputs to send multiple txes at once below.
std::vector<cryptonote::transaction> txs;
txs.reserve(6);
for (int i = 0; i < 6; i++)
txs.push_back(gen.create_and_add_tx(miner, bob.get_keys().m_account_address, MK_COINS(100)));
gen.create_and_add_next_block(std::move(txs));
gen.add_transfer_unlock_blocks();
}
lns_keys_t bob_key = make_lns_keys(bob);
@ -1219,8 +1234,8 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
std::string session_name2 = "AnotherName";
crypto::hash session_name1_txid = {}, session_name2_txid = {};
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, session_name1, bob_key.session_value);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, session_name2, bob_key.session_value, &bob_key.owner);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, gen.hardfork(), lns::mapping_type::session, session_name1, bob_key.session_value);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, session_name2, bob_key.session_value, &bob_key.owner);
gen.create_and_add_next_block({tx1, tx2});
session_name1_txid = get_transaction_hash(tx1);
session_name2_txid = get_transaction_hash(tx2);
@ -1231,10 +1246,10 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
std::string lokinet_name1 = "lorem.loki";
std::string lokinet_name2 = "ipsum.loki";
crypto::hash lokinet_name1_txid = {}, lokinet_name2_txid = {};
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet_1year))
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::lokinet_1year, lokinet_name1, bob_key.lokinet_value);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet_1year, lokinet_name2, bob_key.lokinet_value, &bob_key.owner);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, gen.hardfork(), lns::mapping_type::lokinet, lokinet_name1, bob_key.lokinet_value);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::lokinet_5years, lokinet_name2, bob_key.lokinet_value, &bob_key.owner);
gen.create_and_add_next_block({tx1, tx2});
lokinet_name1_txid = get_transaction_hash(tx1);
lokinet_name2_txid = get_transaction_hash(tx2);
@ -1248,8 +1263,8 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::wallet))
{
std::string bob_addr = cryptonote::get_account_address_as_str(cryptonote::FAKECHAIN, false, bob.get_keys().m_account_address);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::wallet, wallet_name1, bob_key.wallet_value);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::wallet, wallet_name2, bob_key.wallet_value, &bob_key.owner);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, gen.hardfork(), lns::mapping_type::wallet, wallet_name1, bob_key.wallet_value);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::wallet, wallet_name2, bob_key.wallet_value, &bob_key.owner);
gen.create_and_add_next_block({tx1, tx2});
wallet_name1_txid = get_transaction_hash(tx1);
wallet_name2_txid = get_transaction_hash(tx2);
@ -1265,25 +1280,25 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
size_t expected_size = 0;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::session)) expected_size += 2;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::wallet)) expected_size += 2;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet_1year)) expected_size += 2;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet)) expected_size += 2;
CHECK_EQ(records.size(), expected_size);
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::session))
{
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, session_name1, bob_key.session_value, session_height, session_height, session_name1_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[1], lns::mapping_type::session, session_name2, bob_key.session_value, session_height, session_height, session_name2_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, session_name1, bob_key.session_value, session_height, session_height, std::nullopt, session_name1_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[1], lns::mapping_type::session, session_name2, bob_key.session_value, session_height, session_height, std::nullopt, session_name2_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
}
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet_1year))
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet))
{
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[2], lns::mapping_type::lokinet_1year, lokinet_name1, bob_key.lokinet_value, lokinet_height, lokinet_height, lokinet_name1_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[3], lns::mapping_type::lokinet_1year, lokinet_name2, bob_key.lokinet_value, lokinet_height, lokinet_height, lokinet_name2_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[2], lns::mapping_type::lokinet, lokinet_name1, bob_key.lokinet_value, lokinet_height, lokinet_height, lokinet_height + lokinet_expiry(lns::mapping_type::lokinet), lokinet_name1_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[3], lns::mapping_type::lokinet, lokinet_name2, bob_key.lokinet_value, lokinet_height, lokinet_height, lokinet_height + lokinet_expiry(lns::mapping_type::lokinet_5years), lokinet_name2_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
}
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::wallet))
{
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[4], lns::mapping_type::wallet, wallet_name1, bob_key.wallet_value, wallet_height, wallet_height, wallet_name1_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[5], lns::mapping_type::wallet, wallet_name2, bob_key.wallet_value, wallet_height, wallet_height, wallet_name2_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[4], lns::mapping_type::wallet, wallet_name1, bob_key.wallet_value, wallet_height, wallet_height, std::nullopt, wallet_name1_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[5], lns::mapping_type::wallet, wallet_name2, bob_key.wallet_value, wallet_height, wallet_height, std::nullopt, wallet_name2_txid, crypto::null_hash, bob_key.owner, {} /*backup_owner*/));
}
return true;
});
@ -1305,7 +1320,7 @@ bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_en
gen.add_mined_money_unlock_blocks();
cryptonote::transaction transfer = gen.create_and_add_tx(miner, bob.get_keys().m_account_address, MK_COINS(400));
gen.create_and_add_next_block({transfer});
gen.add_mined_money_unlock_blocks();
gen.add_transfer_unlock_blocks();
}
lns_keys_t bob_key = make_lns_keys(bob);
@ -1314,7 +1329,7 @@ bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_en
std::string session_name1 = "MyName";
crypto::hash session_tx_hash1;
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, session_name1, bob_key.session_value);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, gen.hardfork(), lns::mapping_type::session, session_name1, bob_key.session_value);
session_tx_hash1 = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
@ -1324,7 +1339,7 @@ bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_en
std::string session_name2 = "MyName2";
crypto::hash session_tx_hash2;
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, session_name2, bob_key.session_value);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, gen.hardfork(), lns::mapping_type::session, session_name2, bob_key.session_value);
session_tx_hash2 = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
@ -1334,7 +1349,7 @@ bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_en
std::string session_name3 = "MyName3";
crypto::hash session_tx_hash3;
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, session_name3, miner_key.session_value);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, session_name3, miner_key.session_value);
session_tx_hash3 = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
@ -1352,9 +1367,9 @@ bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_en
});
int index = 0;
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[index++], lns::mapping_type::session, session_name1, bob_key.session_value, session_height1, session_height1, session_tx_hash1, crypto::null_hash, bob_key.owner, {}));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[index++], lns::mapping_type::session, session_name2, bob_key.session_value, session_height2, session_height2, session_tx_hash2, crypto::null_hash, bob_key.owner, {}));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[index++], lns::mapping_type::session, session_name3, miner_key.session_value, session_height3, session_height3, session_tx_hash3, crypto::null_hash, miner_key.owner, {}));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[index++], lns::mapping_type::session, session_name1, bob_key.session_value, session_height1, session_height1, std::nullopt, session_tx_hash1, crypto::null_hash, bob_key.owner, {}));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[index++], lns::mapping_type::session, session_name2, bob_key.session_value, session_height2, session_height2, std::nullopt, session_tx_hash2, crypto::null_hash, bob_key.owner, {}));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[index++], lns::mapping_type::session, session_name3, miner_key.session_value, session_height3, session_height3, std::nullopt, session_tx_hash3, crypto::null_hash, miner_key.owner, {}));
return true;
});
@ -1376,14 +1391,14 @@ bool loki_name_system_get_mappings::generate(std::vector<test_event_entry> &even
cryptonote::transaction transfer = gen.create_and_add_tx(miner, bob.get_keys().m_account_address, MK_COINS(400));
gen.create_and_add_next_block({transfer});
gen.add_mined_money_unlock_blocks();
gen.add_transfer_unlock_blocks();
}
lns_keys_t bob_key = make_lns_keys(bob);
std::string session_name1 = "MyName";
crypto::hash session_tx_hash;
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, session_name1, bob_key.session_value);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, gen.hardfork(), lns::mapping_type::session, session_name1, bob_key.session_value);
session_tx_hash = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
@ -1394,9 +1409,9 @@ bool loki_name_system_get_mappings::generate(std::vector<test_event_entry> &even
DEFINE_TESTS_ERROR_CONTEXT("check_lns_entries");
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
std::string session_name_hash = lns::name_to_base64_hash(session_name1);
std::vector<lns::mapping_record> records = lns_db.get_mappings({static_cast<uint16_t>(lns::mapping_type::session)}, session_name_hash);
std::vector<lns::mapping_record> records = lns_db.get_mappings({lns::mapping_type::session}, session_name_hash);
CHECK_EQ(records.size(), 1);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, session_name1, bob_key.session_value, session_height, session_height, session_tx_hash, crypto::null_hash /*prev_txid*/, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, session_name1, bob_key.session_value, session_height, session_height, std::nullopt, session_tx_hash, crypto::null_hash /*prev_txid*/, bob_key.owner, {} /*backup_owner*/));
return true;
});
@ -1416,7 +1431,7 @@ bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_eve
cryptonote::transaction transfer = gen.create_and_add_tx(miner, bob.get_keys().m_account_address, MK_COINS(400));
gen.create_and_add_next_block({transfer});
gen.add_mined_money_unlock_blocks();
gen.add_transfer_unlock_blocks();
lns_keys_t miner_key = make_lns_keys(miner);
lns_keys_t bob_key = make_lns_keys(bob);
@ -1426,15 +1441,15 @@ bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_eve
crypto::hash session_tx_hash = {}, lokinet_tx_hash = {};
{
// NOTE: Allow duplicates with the same name but different type
cryptonote::transaction bar = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, session_name, bob_key.session_value);
cryptonote::transaction bar = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, session_name, bob_key.session_value);
session_tx_hash = get_transaction_hash(bar);
std::vector<cryptonote::transaction> txs;
txs.push_back(bar);
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet_1year))
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
cryptonote::transaction bar3 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet_1year, session_name, miner_key.lokinet_value);
cryptonote::transaction bar3 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::lokinet_2years, session_name, miner_key.lokinet_value);
txs.push_back(bar3);
lokinet_tx_hash = get_transaction_hash(bar3);
}
@ -1444,11 +1459,11 @@ bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_eve
uint64_t height_of_lns_entry = gen.height();
{
cryptonote::transaction bar6 = gen.create_loki_name_system_tx(bob, lns::mapping_type::session, session_name, bob_key.session_value);
cryptonote::transaction bar6 = gen.create_loki_name_system_tx(bob, gen.hardfork(), lns::mapping_type::session, session_name, bob_key.session_value);
gen.add_tx(bar6, false /*can_be_added_to_blockchain*/, "Duplicate name requested by new owner: original already exists in lns db");
}
loki_register_callback(events, "check_lns_entries", [=](cryptonote::core &c, size_t ev_index)
loki_register_callback(events, "check_lns_entries", [=, blockchain_height=gen.chain_height()](cryptonote::core &c, size_t ev_index)
{
DEFINE_TESTS_ERROR_CONTEXT("check_lns_entries");
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
@ -1462,14 +1477,15 @@ bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_eve
std::string session_name_hash = lns::name_to_base64_hash(session_name);
lns::mapping_record record1 = lns_db.get_mapping(lns::mapping_type::session, session_name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record1, lns::mapping_type::session, session_name, bob_key.session_value, height_of_lns_entry, height_of_lns_entry, session_tx_hash, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record1, lns::mapping_type::session, session_name, bob_key.session_value, height_of_lns_entry, height_of_lns_entry, std::nullopt, session_tx_hash, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_EQ(record1.owner_id, owner.id);
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet_1year))
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet))
{
lns::mapping_record record2 = lns_db.get_mapping(lns::mapping_type::lokinet_1year, session_name);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record2, lns::mapping_type::lokinet_1year, lokinet_name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, lokinet_tx_hash, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
lns::mapping_record record2 = lns_db.get_mapping(lns::mapping_type::lokinet, session_name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record2, lns::mapping_type::lokinet, lokinet_name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, height_of_lns_entry + lokinet_expiry(lns::mapping_type::lokinet_2years), lokinet_tx_hash, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_EQ(record2.owner_id, owner.id);
CHECK_EQ(record2.active(blockchain_height), true);
}
lns::owner_record owner2 = lns_db.get_owner_by_key(bob_key.owner);
@ -1492,7 +1508,7 @@ bool loki_name_system_handles_duplicate_in_tx_pool::generate(std::vector<test_ev
cryptonote::transaction transfer = gen.create_and_add_tx(miner, bob.get_keys().m_account_address, MK_COINS(400));
gen.create_and_add_next_block({transfer});
gen.add_mined_money_unlock_blocks();
gen.add_transfer_unlock_blocks();
}
lns_keys_t bob_key = make_lns_keys(bob);
@ -1501,13 +1517,13 @@ bool loki_name_system_handles_duplicate_in_tx_pool::generate(std::vector<test_ev
auto custom_type = static_cast<lns::mapping_type>(3928);
{
// NOTE: Allow duplicates with the same name but different type
cryptonote::transaction bar = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, session_name, bob_key.session_value);
cryptonote::transaction bar = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, session_name, bob_key.session_value);
if (lns::mapping_type_allowed(gen.hardfork(), custom_type))
cryptonote::transaction bar2 = gen.create_and_add_loki_name_system_tx(miner, custom_type, session_name, bob_key.session_value);
cryptonote::transaction bar2 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), custom_type, session_name, bob_key.session_value);
// NOTE: Make duplicate in the TX pool, this should be rejected
cryptonote::transaction bar4 = gen.create_loki_name_system_tx(bob, lns::mapping_type::session, session_name, bob_key.session_value);
cryptonote::transaction bar4 = gen.create_loki_name_system_tx(bob, gen.hardfork(), lns::mapping_type::session, session_name, bob_key.session_value);
gen.add_tx(bar4, false /*can_be_added_to_blockchain*/, "Duplicate name requested by new owner: original already exists in tx pool");
}
return true;
@ -1585,9 +1601,9 @@ bool loki_name_system_invalid_tx_extra_params::generate(std::vector<test_event_e
}
}
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet_1year))
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
valid_data.type = lns::mapping_type::lokinet_1year;
valid_data.type = lns::mapping_type::lokinet;
// Lokinet name empty
{
cryptonote::tx_extra_loki_name_system data = valid_data;
@ -1659,34 +1675,33 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
cryptonote::transaction transfer = gen.create_and_add_tx(miner, bob.get_keys().m_account_address, MK_COINS(400));
gen.create_and_add_next_block({transfer});
gen.add_mined_money_unlock_blocks();
gen.add_transfer_unlock_blocks();
}
// NOTE: Generate the first round of LNS transactions belonging to miner
uint64_t first_lns_height = 0;
uint64_t miner_earliest_renewable_height = 0;
std::string const lokinet_name1 = "website.loki";
std::string const wallet_name1 = "MyWallet";
std::string const session_name1 = "I Like Loki";
std::string const session_name1 = "I-Like-Loki";
crypto::hash session_tx_hash1 = {}, wallet_tx_hash1 = {}, lokinet_tx_hash1 = {};
{
// NOTE: Generate and add the (transactions + block) to the blockchain
{
std::vector<cryptonote::transaction> txs;
cryptonote::transaction session_tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, session_name1, miner_key.session_value);
cryptonote::transaction session_tx = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, session_name1, miner_key.session_value);
session_tx_hash1 = get_transaction_hash(session_tx);
txs.push_back(session_tx);
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::wallet))
{
cryptonote::transaction wallet_tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::wallet, wallet_name1, miner_key.wallet_value);
cryptonote::transaction wallet_tx = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::wallet, wallet_name1, miner_key.wallet_value);
txs.push_back(wallet_tx);
wallet_tx_hash1 = get_transaction_hash(wallet_tx);
}
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet_1year))
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet_10years))
{
cryptonote::transaction lokinet_tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet_1year, lokinet_name1, miner_key.lokinet_value);
cryptonote::transaction lokinet_tx = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::lokinet, lokinet_name1, miner_key.lokinet_value);
txs.push_back(lokinet_tx);
lokinet_tx_hash1 = get_transaction_hash(lokinet_tx);
}
@ -1694,14 +1709,6 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
}
first_lns_height = gen.height();
// NOTE: Determine the earliest height we can renew the Lokinet Entry
{
uint64_t height_of_lns_entry = gen.height();
uint64_t renew_window = 0;
uint64_t expiry_blocks = lns::expiry_blocks(cryptonote::FAKECHAIN, lns::mapping_type::lokinet_1year, &renew_window);
miner_earliest_renewable_height = first_lns_height + expiry_blocks - renew_window;
}
loki_register_callback(events, "check_first_lns_entries", [=](cryptonote::core &c, size_t ev_index)
{
DEFINE_TESTS_ERROR_CONTEXT("check_first_lns_entries");
@ -1711,17 +1718,17 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
size_t expected_size = 1;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::wallet)) expected_size += 1;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet_1year)) expected_size += 1;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet)) expected_size += 1;
CHECK_EQ(records.size(), expected_size);
for (lns::mapping_record const &record : records)
{
if (record.type == lns::mapping_type::session)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, session_name1, miner_key.session_value, first_lns_height, first_lns_height, session_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else if (record.type == lns::mapping_type::lokinet_1year)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet_1year, lokinet_name1, miner_key.lokinet_value, first_lns_height, first_lns_height, lokinet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, session_name1, miner_key.session_value, first_lns_height, first_lns_height, std::nullopt, session_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else if (record.type == lns::mapping_type::lokinet)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet, lokinet_name1, miner_key.lokinet_value, first_lns_height, first_lns_height, first_lns_height + lokinet_expiry(lns::mapping_type::lokinet_10years), lokinet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else if (record.type == lns::mapping_type::wallet)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::wallet, wallet_name1, miner_key.wallet_value, first_lns_height, first_lns_height, wallet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::wallet, wallet_name1, miner_key.wallet_value, first_lns_height, first_lns_height, std::nullopt, wallet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else
{
assert(false);
@ -1731,25 +1738,24 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
});
}
while (gen.height() <= miner_earliest_renewable_height)
gen.create_and_add_next_block();
// NOTE: Generate and add the second round of (transactions + block) to the blockchain, renew lokinet and add bob's session, update miner's session value to other's session value
cryptonote::account_base const other = gen.add_account();
lns_keys_t const other_key = make_lns_keys(other);
uint64_t second_lns_height = 0;
{
std::string const bob_session_name1 = "I Like Session";
std::string const bob_session_name1 = "I-Like-Session";
crypto::hash session_tx_hash2 = {}, lokinet_tx_hash2 = {}, session_tx_hash3;
{
std::vector<cryptonote::transaction> txs;
txs.push_back(gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, bob_session_name1, bob_key.session_value));
txs.push_back(gen.create_and_add_loki_name_system_tx(bob, gen.hardfork(), lns::mapping_type::session, bob_session_name1, bob_key.session_value));
session_tx_hash2 = cryptonote::get_transaction_hash(txs[0]);
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet_1year))
txs.push_back(gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet_1year, "loki.loki", miner_key.lokinet_value));
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
txs.push_back(gen.create_and_add_loki_name_system_tx_renew(miner, gen.hardfork(), lns::mapping_type::lokinet_5years, lokinet_name1));
}
txs.push_back(gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, session_name1, &other_key.session_value));
txs.push_back(gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, session_name1, &other_key.session_value));
session_tx_hash3 = cryptonote::get_transaction_hash(txs.back());
gen.create_and_add_next_block(txs);
@ -1763,17 +1769,16 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
CHECK_EQ(lns_db.height(), second_lns_height);
// NOTE: Check miner's record
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet_1year))
{
std::vector<lns::mapping_record> records = lns_db.get_mappings_by_owner(miner_key.owner);
for (lns::mapping_record const &record : records)
{
if (record.type == lns::mapping_type::session)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, session_name1, other_key.session_value, first_lns_height, second_lns_height, session_tx_hash3, session_tx_hash1 /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else if (record.type == lns::mapping_type::lokinet_1year)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet_1year, lokinet_name1, miner_key.lokinet_value, second_lns_height, second_lns_height, lokinet_tx_hash2, lokinet_tx_hash1 /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, session_name1, other_key.session_value, first_lns_height, second_lns_height, std::nullopt, session_tx_hash3, session_tx_hash1 /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else if (record.type == lns::mapping_type::lokinet)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet, lokinet_name1, miner_key.lokinet_value, second_lns_height, second_lns_height, second_lns_height + lokinet_expiry(lns::mapping_type::lokinet_5years), lokinet_tx_hash2, lokinet_tx_hash1 /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else if (record.type == lns::mapping_type::wallet)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::wallet, wallet_name1, miner_key.wallet_value, first_lns_height, first_lns_height, wallet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::wallet, wallet_name1, miner_key.wallet_value, first_lns_height, first_lns_height, std::nullopt, wallet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else
{
assert(false);
@ -1785,7 +1790,7 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
{
std::vector<lns::mapping_record> records = lns_db.get_mappings_by_owner(bob_key.owner);
CHECK_EQ(records.size(), 1);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, bob_session_name1, bob_key.session_value, second_lns_height, second_lns_height, session_tx_hash2, crypto::null_hash /*prev_txid*/, bob_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, bob_session_name1, bob_key.session_value, second_lns_height, second_lns_height, std::nullopt, session_tx_hash2, crypto::null_hash /*prev_txid*/, bob_key.owner, {} /*backup_owner*/));
}
return true;
@ -1818,17 +1823,17 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
std::vector<lns::mapping_record> records = lns_db.get_mappings_by_owner(miner_key.owner);
size_t expected_size = 1;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::wallet)) expected_size += 1;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet_1year)) expected_size += 1;
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet)) expected_size += 1;
CHECK_EQ(records.size(), expected_size);
for (lns::mapping_record const &record : records)
{
if (record.type == lns::mapping_type::session)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, session_name1, miner_key.session_value, first_lns_height, first_lns_height, session_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else if (record.type == lns::mapping_type::lokinet_1year)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet_1year, lokinet_name1, miner_key.lokinet_value, first_lns_height, first_lns_height, lokinet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, session_name1, miner_key.session_value, first_lns_height, first_lns_height, std::nullopt, session_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else if (record.type == lns::mapping_type::lokinet)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet, lokinet_name1, miner_key.lokinet_value, first_lns_height, first_lns_height, first_lns_height + lokinet_expiry(lns::mapping_type::lokinet_5years), lokinet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else if (record.type == lns::mapping_type::wallet)
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::wallet, wallet_name1, miner_key.wallet_value, first_lns_height, first_lns_height, wallet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::wallet, wallet_name1, miner_key.wallet_value, first_lns_height, first_lns_height, std::nullopt, wallet_tx_hash1, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
else
{
assert(false);
@ -1870,7 +1875,7 @@ bool loki_name_system_name_renewal::generate(std::vector<test_event_entry> &even
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
if (!lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet_1year))
if (!lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
return true;
{
@ -1880,15 +1885,12 @@ bool loki_name_system_name_renewal::generate(std::vector<test_event_entry> &even
lns_keys_t miner_key = make_lns_keys(miner);
std::string const name = "mydomain.loki";
lns::mapping_type mapping_type = lns::mapping_type::lokinet_1year;
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, mapping_type, name, miner_key.lokinet_value);
lns::mapping_type mapping_type = lns::mapping_type::lokinet;
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), mapping_type, name, miner_key.lokinet_value);
gen.create_and_add_next_block({tx});
crypto::hash prev_txid = get_transaction_hash(tx);
uint64_t height_of_lns_entry = gen.height();
uint64_t renew_window = 0;
uint64_t expiry_blocks = lns::expiry_blocks(cryptonote::FAKECHAIN, mapping_type, &renew_window);
uint64_t renew_window_block = height_of_lns_entry + expiry_blocks - renew_window;
loki_register_callback(events, "check_lns_entries", [=](cryptonote::core &c, size_t ev_index)
{
@ -1903,16 +1905,15 @@ bool loki_name_system_name_renewal::generate(std::vector<test_event_entry> &even
<< " == " << owner.address.to_string(cryptonote::FAKECHAIN));
std::string name_hash = lns::name_to_base64_hash(name);
lns::mapping_record record = lns_db.get_mapping(lns::mapping_type::lokinet_1year, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet_1year, name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, prev_txid, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
lns::mapping_record record = lns_db.get_mapping(lns::mapping_type::lokinet, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet, name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, height_of_lns_entry + lokinet_expiry(mapping_type), prev_txid, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
return true;
});
while (gen.height() <= renew_window_block)
gen.create_and_add_next_block();
gen.create_and_add_next_block();
// In the renewal window, try and renew the lokinet entry
cryptonote::transaction renew_tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet_1year, name, miner_key.lokinet_value);
cryptonote::transaction renew_tx = gen.create_and_add_loki_name_system_tx_renew(miner, gen.hardfork(), lns::mapping_type::lokinet_5years, name);
gen.create_and_add_next_block({renew_tx});
crypto::hash txid = cryptonote::get_transaction_hash(renew_tx);
uint64_t renewal_height = gen.height();
@ -1930,8 +1931,10 @@ bool loki_name_system_name_renewal::generate(std::vector<test_event_entry> &even
<< " == " << owner.address.to_string(cryptonote::FAKECHAIN));
std::string name_hash = lns::name_to_base64_hash(name);
lns::mapping_record record = lns_db.get_mapping(lns::mapping_type::lokinet_1year, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet_1year, name, miner_key.lokinet_value, renewal_height, renewal_height, txid, prev_txid, miner_key.owner, {} /*backup_owner*/));
lns::mapping_record record = lns_db.get_mapping(lns::mapping_type::lokinet, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet, name, miner_key.lokinet_value, height_of_lns_entry, renewal_height,
height_of_lns_entry + lokinet_expiry(mapping_type) + lokinet_expiry(lns::mapping_type::lokinet_5years),
txid, prev_txid, miner_key.owner, {} /*backup_owner*/));
return true;
});
@ -1985,17 +1988,12 @@ bool loki_name_system_name_value_max_lengths::generate(std::vector<test_event_en
}
// Lokinet
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet_1year))
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
std::string name(lns::LOKINET_DOMAIN_NAME_MAX, 'A');
size_t last_index = name.size() - 1;
name[last_index--] = 'i';
name[last_index--] = 'k';
name[last_index--] = 'o';
name[last_index--] = 'l';
name[last_index--] = '.';
name.replace(name.size() - 6, 5, ".loki");
data.type = lns::mapping_type::lokinet_1year;
data.type = lns::mapping_type::lokinet;
data.name_hash = lns::name_to_hash(name);
data.encrypted_value = miner_key.lokinet_value.make_encrypted(name).to_string();
make_lns_tx_with_custom_extra(gen, events, miner, data);
@ -2023,26 +2021,26 @@ bool loki_name_system_update_mapping_after_expiry_fails::generate(std::vector<te
gen.add_mined_money_unlock_blocks();
lns_keys_t miner_key = make_lns_keys(miner);
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet_1year))
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
std::string const name = "mydomain.loki";
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet_1year, name, miner_key.lokinet_value);
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::lokinet, name, miner_key.lokinet_value);
crypto::hash tx_hash = cryptonote::get_transaction_hash(tx);
gen.create_and_add_next_block({tx});
uint64_t height_of_lns_entry = gen.height();
uint64_t expected_expiry_block = height_of_lns_entry + lns::expiry_blocks(cryptonote::FAKECHAIN, lns::mapping_type::lokinet_1year, nullptr);
uint64_t expected_expiry_block = height_of_lns_entry + lokinet_expiry(lns::mapping_type::lokinet);
while (gen.height() <= expected_expiry_block)
gen.create_and_add_next_block();
{
lns_keys_t bob_key = make_lns_keys(gen.add_account());
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update(miner, lns::mapping_type::lokinet_1year, name, &bob_key.lokinet_value);
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::lokinet, name, &bob_key.lokinet_value);
gen.add_tx(tx1, false /*can_be_added_to_blockchain*/, "Can not update a LNS record that is already expired");
}
loki_register_callback(events, "check_still_expired", [=](cryptonote::core &c, size_t ev_index)
loki_register_callback(events, "check_still_expired", [=, blockchain_height=gen.chain_height()](cryptonote::core &c, size_t ev_index)
{
DEFINE_TESTS_ERROR_CONTEXT("check_still_expired");
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
@ -2055,8 +2053,9 @@ bool loki_name_system_update_mapping_after_expiry_fails::generate(std::vector<te
<< " == " << owner.address.to_string(cryptonote::FAKECHAIN));
std::string name_hash = lns::name_to_base64_hash(name);
lns::mapping_record record = lns_db.get_mapping(lns::mapping_type::lokinet_1year, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet_1year, name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, tx_hash, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
lns::mapping_record record = lns_db.get_mapping(lns::mapping_type::lokinet, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::lokinet, name, miner_key.lokinet_value, height_of_lns_entry, height_of_lns_entry, height_of_lns_entry + lokinet_expiry(lns::mapping_type::lokinet), tx_hash, crypto::null_hash /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_EQ(record.active(blockchain_height), false);
CHECK_EQ(record.owner_id, owner.id);
return true;
});
@ -2093,10 +2092,10 @@ bool loki_name_system_update_mapping::generate(std::vector<test_event_entry> &ev
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
std::string name_hash = lns::name_to_base64_hash(session_name1);
std::vector<lns::mapping_record> records = lns_db.get_mappings({static_cast<uint16_t>(lns::mapping_type::session)}, name_hash);
std::vector<lns::mapping_record> records = lns_db.get_mappings({lns::mapping_type::session}, name_hash);
CHECK_EQ(records.size(), 1);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, session_name1, miner_key.session_value, register_height, register_height, session_tx_hash1, {} /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, session_name1, miner_key.session_value, register_height, register_height, std::nullopt, session_tx_hash1, {} /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
return true;
});
@ -2119,10 +2118,10 @@ bool loki_name_system_update_mapping::generate(std::vector<test_event_entry> &ev
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
std::string name_hash = lns::name_to_base64_hash(session_name1);
std::vector<lns::mapping_record> records = lns_db.get_mappings({static_cast<uint16_t>(lns::mapping_type::session)}, name_hash);
std::vector<lns::mapping_record> records = lns_db.get_mappings({lns::mapping_type::session}, name_hash);
CHECK_EQ(records.size(), 1);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, session_name1, bob_key.session_value, register_height, blockchain_height, session_tx_hash2, session_tx_hash1 /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, records[0], lns::mapping_type::session, session_name1, bob_key.session_value, register_height, blockchain_height, std::nullopt, session_tx_hash2, session_tx_hash1 /*prev_txid*/, miner_key.owner, {} /*backup_owner*/));
return true;
});
@ -2154,7 +2153,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
std::string name = "Hello_World";
std::string name_hash = lns::name_to_base64_hash(name);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, name, miner_key.session_value, &owner1, &owner2);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, name, miner_key.session_value, &owner1, &owner2);
gen.create_and_add_next_block({tx1});
uint64_t height = gen.height();
crypto::hash txid = cryptonote::get_transaction_hash(tx1);
@ -2165,7 +2164,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
const char* perr_context = "check_update0";
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
lns::mapping_record const record = lns_db.get_mapping(lns::mapping_type::session, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, miner_key.session_value, height, height, txid, prev_txid, owner1, owner2 /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, miner_key.session_value, height, height, std::nullopt, txid, prev_txid, owner1, owner2 /*backup_owner*/));
return true;
});
@ -2176,7 +2175,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_ed25519_signature(hash, owner1_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &temp_keys.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
prev_txid = txid;
txid = cryptonote::get_transaction_hash(tx2);
@ -2186,7 +2185,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
const char* perr_context = "check_update1";
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
lns::mapping_record const record = lns_db.get_mapping(lns::mapping_type::session, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, txid, prev_txid, owner1, owner2 /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, std::nullopt, txid, prev_txid, owner1, owner2 /*backup_owner*/));
return true;
});
}
@ -2198,7 +2197,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_ed25519_signature(hash, owner2_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &temp_keys.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
prev_txid = txid;
txid = cryptonote::get_transaction_hash(tx2);
@ -2208,7 +2207,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
const char* perr_context = "check_update2";
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
lns::mapping_record const record = lns_db.get_mapping(lns::mapping_type::session, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, txid, prev_txid, owner1, owner2 /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, std::nullopt, txid, prev_txid, owner1, owner2 /*backup_owner*/));
return true;
});
}
@ -2223,7 +2222,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
std::string name = "Hello_Sailor";
std::string name_hash = lns::name_to_base64_hash(name);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, name, miner_key.session_value, &owner1, &owner2);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, name, miner_key.session_value, &owner1, &owner2);
gen.create_and_add_next_block({tx1});
uint64_t height = gen.height();
crypto::hash txid = cryptonote::get_transaction_hash(tx1);
@ -2236,7 +2235,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_monero_signature(hash, owner1.wallet.address.m_spend_public_key, account1.get_keys().m_spend_secret_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &temp_keys.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
prev_txid = txid;
txid = cryptonote::get_transaction_hash(tx2);
@ -2246,7 +2245,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
const char* perr_context = "check_update3";
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
lns::mapping_record const record = lns_db.get_mapping(lns::mapping_type::session, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, txid, prev_txid, owner1, owner2 /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, std::nullopt, txid, prev_txid, owner1, owner2 /*backup_owner*/));
return true;
});
}
@ -2258,7 +2257,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_monero_signature(hash, owner2.wallet.address.m_spend_public_key, account2.get_keys().m_spend_secret_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &temp_keys.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
prev_txid = txid;
txid = cryptonote::get_transaction_hash(tx2);
@ -2268,7 +2267,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
const char* perr_context = "check_update3";
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
lns::mapping_record const record = lns_db.get_mapping(lns::mapping_type::session, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, txid, prev_txid, owner1, owner2 /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, std::nullopt, txid, prev_txid, owner1, owner2 /*backup_owner*/));
return true;
});
}
@ -2287,7 +2286,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
std::string name = "Hello_Driver";
std::string name_hash = lns::name_to_base64_hash(name);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, name, miner_key.session_value, &owner1, &owner2);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, name, miner_key.session_value, &owner1, &owner2);
gen.create_and_add_next_block({tx1});
uint64_t height = gen.height();
crypto::hash txid = cryptonote::get_transaction_hash(tx1);
@ -2300,7 +2299,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_ed25519_signature(hash, owner1_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &temp_keys.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
prev_txid = txid;
txid = cryptonote::get_transaction_hash(tx2);
@ -2310,7 +2309,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
const char* perr_context = "check_update4";
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
lns::mapping_record const record = lns_db.get_mapping(lns::mapping_type::session, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, txid, prev_txid, owner1, owner2 /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, std::nullopt, txid, prev_txid, owner1, owner2 /*backup_owner*/));
return true;
});
}
@ -2322,7 +2321,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_monero_signature(hash, owner2.wallet.address.m_spend_public_key, account2.get_keys().m_spend_secret_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &temp_keys.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
prev_txid = txid;
txid = cryptonote::get_transaction_hash(tx2);
@ -2332,7 +2331,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
const char* perr_context = "check_update5";
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
lns::mapping_record const record = lns_db.get_mapping(lns::mapping_type::session, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, txid, prev_txid, owner1, owner2 /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, std::nullopt, txid, prev_txid, owner1, owner2 /*backup_owner*/));
return true;
});
}
@ -2350,7 +2349,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
std::string name = "Hello_Passenger";
std::string name_hash = lns::name_to_base64_hash(name);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, name, miner_key.session_value, &owner1, &owner2);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, name, miner_key.session_value, &owner1, &owner2);
gen.create_and_add_next_block({tx1});
uint64_t height = gen.height();
crypto::hash txid = cryptonote::get_transaction_hash(tx1);
@ -2364,7 +2363,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_monero_signature(hash, owner1.wallet.address.m_spend_public_key, account1.get_keys().m_spend_secret_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &temp_keys.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
prev_txid = txid;
txid = cryptonote::get_transaction_hash(tx2);
@ -2374,7 +2373,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
const char* perr_context = "check_update6";
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
lns::mapping_record const record = lns_db.get_mapping(lns::mapping_type::session, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, txid, prev_txid, owner1, owner2 /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, std::nullopt, txid, prev_txid, owner1, owner2 /*backup_owner*/));
return true;
});
}
@ -2387,7 +2386,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_view(), nullptr /*owner*/, nullptr /*backup_owner*/, txid);
auto signature = lns::make_ed25519_signature(hash, owner2_key);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &temp_keys.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &signature);
gen.create_and_add_next_block({tx2});
prev_txid = txid;
txid = cryptonote::get_transaction_hash(tx2);
@ -2397,7 +2396,7 @@ bool loki_name_system_update_mapping_multiple_owners::generate(std::vector<test_
const char* perr_context = "check_update7";
lns::name_system_db &lns_db = c.get_blockchain_storage().name_system_db();
lns::mapping_record const record = lns_db.get_mapping(lns::mapping_type::session, name_hash);
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, txid, prev_txid, owner1, owner2 /*backup_owner*/));
CHECK_TEST_CONDITION(verify_lns_mapping_record(perr_context, record, lns::mapping_type::session, name, temp_keys.session_value, height, blockchain_height, std::nullopt, txid, prev_txid, owner1, owner2 /*backup_owner*/));
return true;
});
}
@ -2414,8 +2413,8 @@ bool loki_name_system_update_mapping_non_existent_name_fails::generate(std::vect
cryptonote::account_base miner = gen.first_miner_;
lns_keys_t miner_key = make_lns_keys(miner);
std::string name = "Hello World";
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &miner_key.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, nullptr /*signature*/, false /*use_asserts*/);
std::string name = "Hello-World";
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &miner_key.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, nullptr /*signature*/, false /*use_asserts*/);
gen.add_tx(tx1, false /*can_be_added_to_blockchain*/, "Can not add a updating LNS TX referencing a non-existent LNS entry");
return true;
}
@ -2430,14 +2429,14 @@ bool loki_name_system_update_mapping_invalid_signature::generate(std::vector<tes
cryptonote::account_base miner = gen.first_miner_;
lns_keys_t miner_key = make_lns_keys(miner);
std::string const name = "Hello World";
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, name, miner_key.session_value);
std::string const name = "Hello-World";
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, name, miner_key.session_value);
gen.create_and_add_next_block({tx1});
lns_keys_t bob_key = make_lns_keys(gen.add_account());
lns::mapping_value encrypted_value = bob_key.session_value.make_encrypted(name);
lns::generic_signature invalid_signature = {};
cryptonote::transaction tx2 = gen.create_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &bob_key.session_value, nullptr /*owner*/, nullptr /*backup_owner*/, &invalid_signature, false /*use_asserts*/);
cryptonote::transaction tx2 = gen.create_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &encrypted_value, nullptr /*owner*/, nullptr /*backup_owner*/, &invalid_signature, false /*use_asserts*/);
gen.add_tx(tx2, false /*can_be_added_to_blockchain*/, "Can not add a updating LNS TX with an invalid signature");
return true;
}
@ -2454,17 +2453,17 @@ bool loki_name_system_update_mapping_replay::generate(std::vector<test_event_ent
lns_keys_t bob_key = make_lns_keys(gen.add_account());
lns_keys_t alice_key = make_lns_keys(gen.add_account());
std::string const name = "Hello World";
std::string const name = "Hello-World";
// Make LNS Mapping
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, name, miner_key.session_value);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, gen.hardfork(), lns::mapping_type::session, name, miner_key.session_value);
gen.create_and_add_next_block({tx1});
}
// (1) Update LNS Mapping
cryptonote::tx_extra_loki_name_system lns_entry = {};
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &bob_key.session_value);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &bob_key.session_value);
gen.create_and_add_next_block({tx1});
[[maybe_unused]] bool found_tx_extra = cryptonote::get_field_from_tx_extra(tx1.extra, lns_entry);
assert(found_tx_extra);
@ -2472,14 +2471,14 @@ bool loki_name_system_update_mapping_replay::generate(std::vector<test_event_ent
// Replay the (1)st update mapping, should fail because the update is to the same session value
{
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update_w_extra(miner, lns_entry);
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update_w_extra(miner, gen.hardfork(), lns_entry);
gen.add_tx(tx1, false /*can_be_added_to_blockchain*/, "Can not replay an older update mapping to the same session value");
}
// (2) Update Again
crypto::hash new_hash = {};
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, name, &alice_key.session_value);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx_update(miner, gen.hardfork(), lns::mapping_type::session, name, &alice_key.session_value);
gen.create_and_add_next_block({tx1});
new_hash = cryptonote::get_transaction_hash(tx1);
}
@ -2487,7 +2486,7 @@ bool loki_name_system_update_mapping_replay::generate(std::vector<test_event_ent
// Replay the (1)st update mapping, should fail now even though it's not to the same session value, but that the signature no longer matches so you can't replay.
lns_entry.prev_txid = new_hash;
{
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update_w_extra(miner, lns_entry);
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update_w_extra(miner, gen.hardfork(), lns_entry);
gen.add_tx(tx1, false /*can_be_added_to_blockchain*/, "Can not replay an older update mapping, should fail signature verification");
}
@ -2507,7 +2506,7 @@ bool loki_name_system_wrong_burn::generate(std::vector<test_event_entry> &events
}
lns_keys_t lns_keys = make_lns_keys(miner);
lns::mapping_type const types[] = {lns::mapping_type::session, lns::mapping_type::wallet, lns::mapping_type::lokinet_1year};
lns::mapping_type const types[] = {lns::mapping_type::session, lns::mapping_type::wallet, lns::mapping_type::lokinet};
for (int i = 0; i < 2; i++)
{
bool under_burn = (i == 0);
@ -2521,14 +2520,14 @@ bool loki_name_system_wrong_burn::generate(std::vector<test_event_entry> &events
if (type == lns::mapping_type::session)
{
value = lns_keys.session_value;
name = "My Friendly Session Name";
name = "My-Friendly-Session-Name";
}
else if (type == lns::mapping_type::wallet)
{
value = lns_keys.wallet_value;
name = "My Friendly Wallet Name";
name = "My-Friendly-Wallet-Name";
}
else if (type == lns::mapping_type::lokinet_1year)
else if (type == lns::mapping_type::lokinet)
{
value = lns_keys.lokinet_value;
name = "MyFriendlyLokinetName.loki";
@ -2542,7 +2541,7 @@ bool loki_name_system_wrong_burn::generate(std::vector<test_event_entry> &events
if (under_burn) burn -= 1;
else burn += 1;
cryptonote::transaction tx = gen.create_loki_name_system_tx(miner, type, name, value, nullptr /*owner*/, nullptr /*backup_owner*/, burn);
cryptonote::transaction tx = gen.create_loki_name_system_tx(miner, gen.hardfork(), type, name, value, nullptr /*owner*/, nullptr /*backup_owner*/, burn);
gen.add_tx(tx, false /*can_be_added_to_blockchain*/, "Wrong burn for a LNS tx", false /*kept_by_block*/);
}
}

View File

@ -15,8 +15,11 @@ TEST(loki_name_system, name_tests)
name_test const lokinet_names[] = {
{"a.loki", true},
{"domain.loki", true},
{"xn--tda.loki", true},
{"xn--Mchen-Ost-9db-u6b.loki", true},
{"xn--tda.loki", true}, // ü
{"xn--Mnchen-Ost-9db.loki", true}, // München-Ost
{"xn--fwg93vdaef749it128eiajklmnopqrstu7dwaxyz0a1a2a3a643qhok169a.loki", true}, // ⸘🌻‽💩🤣♠♡♢♣🂡🂢🂣🂤🂥🂦🂧🂨🂩🂪🂫🂬🂭🂮🂱🂲🂳🂴🂵🂶🂷🂸🂹
{"abcdefghijklmnopqrstuvwxyz123456.loki", true}, // Max length = 32 if no hyphen (so that it can't look like a raw address)
{"a-cdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789a.loki", true}, // Max length = 63 if there is at least one hyphen
{"abc.domain.loki", false},
{"a", false},
@ -29,6 +32,12 @@ TEST(loki_name_system, name_tests)
{" a.loki ", false},
{"localhost.loki", false},
{"localhost", false},
{"loki.loki", false},
{"snode.loki", false},
{"abcdefghijklmnopqrstuvwxyz1234567.loki", false}, // Too long (no hyphen)
{"a-cdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789ab.loki", false}, // Too long with hyphen
{"xn--fwg93vdaef749it128eiajklmnopqrstu7dwaxyz0a1a2a3a643qhok169ab.loki", false}, // invalid (punycode and DNS name parts max at 63)
{"ab--xyz.loki", false}, // Double-hyphen at chars 3&4 is reserved by DNS (currently only xn-- is used).
};
name_test const session_wallet_names[] = {
@ -40,7 +49,7 @@ TEST(loki_name_system, name_tests)
{"_Hello_", true},
{"999", true},
{"xn--tda", true},
{"xn--Mchen-Ost-9db-u6b", true},
{"xn--Mnchen-Ost-9db", true},
{"-", false},
{"@", false},
@ -62,6 +71,7 @@ TEST(loki_name_system, name_tests)
for (uint16_t type16 = 0; type16 < static_cast<uint16_t>(lns::mapping_type::_count); type16++)
{
auto type = static_cast<lns::mapping_type>(type16);
if (type == lns::mapping_type::wallet) continue; // Not yet supported
name_test const *names = lns::is_lokinet_type(type) ? lokinet_names : session_wallet_names;
size_t names_count = lns::is_lokinet_type(type) ? loki::char_count(lokinet_names) : loki::char_count(session_wallet_names);
@ -82,7 +92,7 @@ TEST(loki_name_system, value_encrypt_and_decrypt)
// The type here is not hugely important for decryption except that lokinet (as opposed to
// session) doesn't fall back to argon2 decryption if decryption fails.
constexpr auto type = lns::mapping_type::lokinet_1year;
constexpr auto type = lns::mapping_type::lokinet;
// Encryption and Decryption success
{