Merge pull request #1044 from Doy-lee/LokiNameServiceUpdateTX

Loki Name Service - Update Mappings TX
This commit is contained in:
Doyle 2020-02-21 11:06:01 +11:00 committed by GitHub
commit 02a1f0741a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1041 additions and 297 deletions

View File

@ -284,6 +284,7 @@ EPEE_TYPE_IS_SPANNABLE(crypto::public_key)
EPEE_TYPE_IS_SPANNABLE(crypto::key_derivation)
EPEE_TYPE_IS_SPANNABLE(crypto::key_image)
EPEE_TYPE_IS_SPANNABLE(crypto::signature)
EPEE_TYPE_IS_SPANNABLE(crypto::ed25519_signature)
EPEE_TYPE_IS_SPANNABLE(crypto::ed25519_public_key)
EPEE_TYPE_IS_SPANNABLE(crypto::x25519_public_key)

View File

@ -582,12 +582,12 @@ namespace cryptonote
{
switch(type)
{
case txtype::standard: return "standard";
case txtype::state_change: return "state_change";
case txtype::key_image_unlock: return "key_image_unlock";
case txtype::stake: return "stake";
case txtype::loki_name_system: return "loki_name_system";
default: assert(false); return "xx_unhandled_type";
case txtype::standard: return "standard";
case txtype::state_change: return "state_change";
case txtype::key_image_unlock: return "key_image_unlock";
case txtype::stake: return "stake";
case txtype::loki_name_system: return "loki_name_system";
default: assert(false); return "xx_unhandled_type";
}
}

View File

@ -184,6 +184,13 @@ namespace cryptonote
std::string print_tx_verification_context (tx_verification_context const &tvc, transaction const *tx = nullptr);
std::string print_vote_verification_context(vote_verification_context const &vvc, service_nodes::quorum_vote_t const *vote = nullptr);
inline std::ostream &operator<<(std::ostream &stream, transaction const &tx)
{
stream << "tx={version=" << tx.version << ", type=" << tx.type << ", hash=" << get_transaction_hash(tx) << "}";
return stream;
}
//---------------------------------------------------------------
template<class t_object>
bool t_serializable_object_from_blob(t_object& to, const blobdata& b_blob)

View File

@ -62,6 +62,23 @@
#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
#define TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID 0x01
namespace lns
{
enum struct mapping_type : uint16_t
{
session = 0,
wallet = 1,
lokinet = 2,
_count,
};
enum struct tx_command_t : uint8_t
{
buy = 0,
update = 1,
_count,
};
};
namespace service_nodes {
enum class new_state : uint16_t
{
@ -385,29 +402,49 @@ namespace cryptonote
struct tx_extra_loki_name_system
{
uint8_t version = 0;
crypto::ed25519_public_key owner;
uint16_t type;
lns::tx_command_t command;
lns::mapping_type type; // alias to lns::mapping_type
crypto::ed25519_public_key owner; // only serialized if command == tx_command_t::buy
crypto::ed25519_signature signature; // only serialized if command == tx_command_t::update
std::string name;
std::string value; // binary format of the name->value mapping
crypto::hash prev_txid = crypto::null_hash; // previous txid that purchased the mapping
tx_extra_loki_name_system() = default;
tx_extra_loki_name_system(crypto::ed25519_public_key const &owner, uint16_t type, std::string const &name, std::string const &value, crypto::hash const &prev_txid)
: owner(owner)
, type(type)
, name(name)
, value(value)
, prev_txid(prev_txid)
static tx_extra_loki_name_system make_buy(crypto::ed25519_public_key const &owner, lns::mapping_type type, std::string const &name, std::string const &value, crypto::hash const &prev_txid)
{
tx_extra_loki_name_system result = {};
result.owner = owner;
result.type = type;
result.name = name;
result.value = value;
result.prev_txid = prev_txid;
result.command = lns::tx_command_t::buy;
return result;
}
static tx_extra_loki_name_system make_update(crypto::ed25519_signature const &signature, lns::mapping_type type, std::string const &name, std::string const &value, crypto::hash const &prev_txid)
{
tx_extra_loki_name_system result = {};
result.signature = signature;
result.type = type;
result.name = name;
result.value = value;
result.prev_txid = prev_txid;
result.command = lns::tx_command_t::update;
return result;
}
BEGIN_SERIALIZE()
FIELD(version);
FIELD(owner);
FIELD(type);
FIELD(name);
FIELD(value);
FIELD(prev_txid);
FIELD(version)
ENUM_FIELD(type, type < lns::mapping_type::_count)
ENUM_FIELD(command, command < lns::tx_command_t::_count)
if (command == lns::tx_command_t::buy)
FIELD(owner)
else
FIELD(signature)
FIELD(name)
FIELD(value)
FIELD(prev_txid)
END_SERIALIZE()
};

View File

@ -3356,7 +3356,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
std::string 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);
MERROR_VER("Failed to validate LNS TX reason: " << fail_reason);
return false;
}
}

View File

@ -3,7 +3,9 @@
#include "checkpoints/checkpoints.h"
#include "common/loki.h"
#include "common/hex.h"
#include "common/util.h"
#include "common/base32z.h"
#include "crypto/hash.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
@ -130,7 +132,11 @@ static bool sql_run_statement(cryptonote::network_type nettype, lns_sql_type typ
case lns_sql_type::get_mapping:
{
mapping_record tmp_entry = {};
tmp_entry.type = static_cast<uint16_t>(sqlite3_column_int(statement, static_cast<int>(mapping_record_row::type)));
int type_int = static_cast<uint16_t>(sqlite3_column_int(statement, static_cast<int>(mapping_record_row::type)));
if (type_int >= tools::enum_count<mapping_type>)
return false;
tmp_entry.type = static_cast<mapping_type>(type_int);
tmp_entry.register_height = static_cast<uint16_t>(sqlite3_column_int(statement, static_cast<int>(mapping_record_row::register_height)));
tmp_entry.owner_id = sqlite3_column_int(statement, static_cast<int>(mapping_record_row::owner_id));
@ -195,7 +201,7 @@ static bool sql_run_statement(cryptonote::network_type nettype, lns_sql_type typ
bool mapping_record::active(cryptonote::network_type nettype, uint64_t blockchain_height) const
{
if (!loaded) return false;
if (type != static_cast<uint16_t>(mapping_type::lokinet)) return true;
if (type != mapping_type::lokinet) return true;
uint64_t expiry_blocks = lns::lokinet_expiry_blocks(nettype);
uint64_t const last_active_height = register_height + expiry_blocks;
return last_active_height >= (blockchain_height - 1);
@ -232,6 +238,10 @@ uint64_t burn_requirement_in_atomic_loki(uint8_t /*hf_version*/, burn_type type)
uint64_t result = 0;
switch (type)
{
case burn_type::update_record:
result = 0;
break;
case burn_type::lokinet_1year: /* FALLTHRU */
case burn_type::session: /* FALLTHRU */
case burn_type::wallet: /* FALLTHRU */
@ -281,6 +291,26 @@ uint64_t lokinet_expiry_blocks(cryptonote::network_type nettype, uint64_t *renew
return result;
}
crypto::hash tx_extra_signature_hash(epee::span<const uint8_t> blob, crypto::hash const &prev_txid)
{
static_assert(sizeof(crypto::hash) == crypto_generichash_BYTES, "Using libsodium generichash for signature hash, require we fit into crypto::hash");
crypto::hash result = {};
if (blob.size() <= lns::GENERIC_VALUE_MAX)
{
unsigned char buffer[lns::GENERIC_VALUE_MAX + sizeof(prev_txid)] = {};
size_t buffer_len = blob.size() + sizeof(prev_txid);
memcpy(buffer, blob.data(), blob.size());
memcpy(buffer + blob.size(), prev_txid.data, sizeof(prev_txid));
crypto_generichash(reinterpret_cast<unsigned char *>(result.data), sizeof(result), buffer, buffer_len, NULL /*key*/, 0 /*key_len*/);
}
else
{
MERROR("Unexpected blob len=" << blob.size() << " greater than the blob backing buffer capacity=" << lns::GENERIC_VALUE_MAX);
}
return result;
}
static bool char_is_num(char c)
{
bool result = c >= '0' && c <= '9';
@ -299,15 +329,15 @@ static bool char_is_alphanum(char c)
return result;
}
bool validate_lns_name(uint16_t type, std::string const &name, std::string *reason)
bool validate_lns_name(mapping_type type, std::string const &name, std::string *reason)
{
std::stringstream err_stream;
LOKI_DEFER { if (reason) *reason = err_stream.str(); };
size_t max_name_len = lns::GENERIC_NAME_MAX;
if (type == static_cast<uint16_t>(mapping_type::session)) max_name_len = lns::SESSION_DISPLAY_NAME_MAX;
else if (type == static_cast<uint16_t>(mapping_type::wallet)) max_name_len = lns::WALLET_NAME_MAX;
else if (type == static_cast<uint16_t>(mapping_type::lokinet)) max_name_len = lns::LOKINET_DOMAIN_NAME_MAX;
if (type == mapping_type::session) max_name_len = lns::SESSION_DISPLAY_NAME_MAX;
else if (type == mapping_type::wallet) max_name_len = lns::WALLET_NAME_MAX;
else if (type == mapping_type::lokinet) max_name_len = lns::LOKINET_DOMAIN_NAME_MAX;
// NOTE: Validate name length
if (name.empty() || name.size() > max_name_len)
@ -320,10 +350,10 @@ bool validate_lns_name(uint16_t type, std::string const &name, std::string *reas
}
// NOTE: Validate domain specific requirements
if (type == static_cast<uint16_t>(mapping_type::session))
if (type == mapping_type::session)
{
}
else if (type == static_cast<uint16_t>(mapping_type::lokinet))
else if (type == mapping_type::lokinet)
{
// Domain has to start with a letter or digit, and can have letters, digits, or hyphens in between and must end with a .loki
// ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.loki$
@ -388,7 +418,7 @@ bool validate_lns_name(uint16_t type, std::string const &name, std::string *reas
return true;
}
static bool check_lengths(uint16_t type, std::string const &value, size_t max, bool require_exact_len, std::string *reason)
static bool check_lengths(mapping_type type, std::string const &value, size_t max, bool require_exact_len, std::string *reason)
{
bool result = true;
if (require_exact_len)
@ -420,7 +450,7 @@ static bool check_lengths(uint16_t type, std::string const &value, size_t max, b
return result;
}
bool validate_lns_value(cryptonote::network_type nettype, uint16_t type, std::string const &value, lns_value *blob, std::string *reason)
bool validate_lns_value(cryptonote::network_type nettype, mapping_type type, std::string const &value, lns_value *blob, std::string *reason)
{
if (blob) *blob = {};
std::stringstream err_stream;
@ -430,7 +460,7 @@ bool validate_lns_value(cryptonote::network_type nettype, uint16_t type, std::st
static_assert(GENERIC_VALUE_MAX >= SESSION_PUBLIC_KEY_BINARY_LENGTH, "lns_value assumes the largest blob size required, all other values should be able to fit into this buffer");
static_assert(GENERIC_VALUE_MAX >= LOKINET_ADDRESS_BINARY_LENGTH, "lns_value assumes the largest blob size required, all other values should be able to fit into this buffer");
static_assert(GENERIC_VALUE_MAX >= sizeof(addr_info.address), "lns_value assumes the largest blob size required, all other values should be able to fit into this buffer");
if (type == static_cast<uint16_t>(mapping_type::wallet))
if (type == mapping_type::wallet)
{
if (value.empty() || !get_account_address_from_str(addr_info, nettype, value))
{
@ -454,15 +484,15 @@ bool validate_lns_value(cryptonote::network_type nettype, uint16_t type, std::st
{
int max_value_len = lns::GENERIC_VALUE_MAX;
bool value_require_exact_len = true;
if (type == static_cast<uint16_t>(mapping_type::lokinet)) max_value_len = (LOKINET_ADDRESS_BINARY_LENGTH * 2);
else if (type == static_cast<uint16_t>(mapping_type::session)) max_value_len = (SESSION_PUBLIC_KEY_BINARY_LENGTH * 2);
if (type == mapping_type::lokinet) max_value_len = (LOKINET_ADDRESS_BINARY_LENGTH * 2);
else if (type == mapping_type::session) max_value_len = (SESSION_PUBLIC_KEY_BINARY_LENGTH * 2);
else value_require_exact_len = false;
if (!check_lengths(type, value, max_value_len, value_require_exact_len, reason))
return false;
}
if (type == static_cast<uint16_t>(mapping_type::wallet))
if (type == mapping_type::wallet)
{
if (blob)
{
@ -470,7 +500,7 @@ bool validate_lns_value(cryptonote::network_type nettype, uint16_t type, std::st
memcpy(blob->buffer.data(), &addr_info.address, blob->len);
}
}
else if (type == static_cast<uint16_t>(mapping_type::lokinet))
else if (type == mapping_type::lokinet)
{
if (value.size() != 52)
{
@ -532,7 +562,7 @@ bool validate_lns_value(cryptonote::network_type nettype, uint16_t type, std::st
}
}
if (type == static_cast<uint16_t>(mapping_type::session))
if (type == mapping_type::session)
{
if (!(value[0] == '0' && value[1] == '5')) // NOTE: Session public keys are 33 bytes, with the first byte being 0x05 and the remaining 32 being the public key.
{
@ -548,19 +578,19 @@ bool validate_lns_value(cryptonote::network_type nettype, uint16_t type, std::st
return true;
}
bool validate_lns_value_binary(uint16_t type, std::string const &value, std::string *reason)
bool validate_lns_value_binary(mapping_type type, std::string const &value, std::string *reason)
{
int max_value_len = lns::GENERIC_VALUE_MAX;
bool value_require_exact_len = true;
if (type == static_cast<uint16_t>(mapping_type::lokinet)) max_value_len = LOKINET_ADDRESS_BINARY_LENGTH;
else if (type == static_cast<uint16_t>(mapping_type::session)) max_value_len = SESSION_PUBLIC_KEY_BINARY_LENGTH;
else if (type == static_cast<uint16_t>(mapping_type::wallet)) max_value_len = sizeof(cryptonote::account_public_address);
if (type == mapping_type::lokinet) max_value_len = LOKINET_ADDRESS_BINARY_LENGTH;
else if (type == mapping_type::session) max_value_len = SESSION_PUBLIC_KEY_BINARY_LENGTH;
else if (type == mapping_type::wallet) max_value_len = sizeof(cryptonote::account_public_address);
else value_require_exact_len = false;
if (!check_lengths(type, value, max_value_len, value_require_exact_len, reason))
return false;
if (type == static_cast<uint16_t>(lns::mapping_type::wallet))
if (type == lns::mapping_type::wallet)
{
// TODO(doyle): Better address validation? Is it a valid address, is it a valid nettype address?
cryptonote::account_public_address address;
@ -579,76 +609,139 @@ bool validate_lns_value_binary(uint16_t type, std::string const &value, std::str
return true;
}
static std::stringstream &error_stream_append_tx_msg(std::stringstream &err_stream, cryptonote::transaction const &tx, cryptonote::tx_extra_loki_name_system const &data)
static std::ostream &operator<<(std::ostream &stream, cryptonote::tx_extra_loki_name_system const &data)
{
err_stream << "TX type=" << tx.type << ", tx=" << get_transaction_hash(tx) << ", owner=" << data.owner << ", type=" << (int)data.type << ", name=" << data.name << ", ";
return err_stream;
stream << "LNS Extra={";
if (data.command == lns::tx_command_t::buy)
stream << "owner=" << data.owner;
else
stream << "signature=" << epee::string_tools::pod_to_hex(data.signature);
stream << ", type=" << data.type << ", name=" << data.name << "}";
return stream;
}
static bool validate_against_previous_mapping(lns::name_system_db const &lns_db, uint64_t blockchain_height, cryptonote::transaction const &tx, cryptonote::tx_extra_loki_name_system const &data, std::string *reason = nullptr)
static bool validate_against_previous_mapping(lns::name_system_db const &lns_db, uint64_t blockchain_height, cryptonote::transaction const &tx, cryptonote::tx_extra_loki_name_system &data, std::string *reason = nullptr)
{
std::stringstream err_stream;
crypto::hash expected_prev_txid = crypto::null_hash;
if (lns::mapping_record mapping = lns_db.get_mapping(data.type, data.name))
lns::mapping_record mapping = lns_db.get_mapping(data.type, data.name);
const bool updating = data.command == lns::tx_command_t::update;
if (updating && !mapping)
{
if (data.type != static_cast<uint16_t>(lns::mapping_type::lokinet))
if (reason)
{
if (reason)
{
lns::owner_record owner = lns_db.get_owner_by_id(mapping.owner_id);
error_stream_append_tx_msg(err_stream, tx, data) << "non-lokinet entries can NOT be renewed, mapping already exists with name=" << mapping.name << ", owner=" << owner.key << ", type=" << mapping.type;
*reason = err_stream.str();
}
return false;
}
if (!mapping.active(lns_db.network_type(), blockchain_height))
return true;
expected_prev_txid = mapping.txid;
uint64_t renew_window = 0;
uint64_t expiry_blocks = lns::lokinet_expiry_blocks(lns_db.network_type(), &renew_window);
uint64_t const renew_window_offset = expiry_blocks - renew_window;
uint64_t const min_renew_height = mapping.register_height + renew_window_offset;
if (min_renew_height >= blockchain_height)
{
error_stream_append_tx_msg(err_stream, tx, data) << "trying to renew too early, the earliest renew height=" << min_renew_height << ", urrent height=" << blockchain_height;
err_stream << tx << ", " << data << ", update requested but mapping does not exist.";
*reason = err_stream.str();
return false; // Trying to renew too early
}
return false;
}
// LNS entry can be renewed, check that the request to renew originates from the owner of this mapping
lns::owner_record requester = lns_db.get_owner_by_key(data.owner);
if (!requester)
if (mapping)
{
expected_prev_txid = mapping.txid;
if (updating)
{
if (reason)
if (data.type == lns::mapping_type::lokinet && !mapping.active(lns_db.network_type(), blockchain_height))
{
error_stream_append_tx_msg(err_stream, tx, data) << "trying to renew existing mapping but owner specified in LNS extra does not exist, rejected";
*reason = err_stream.str();
// Updating, we can always update unless the mapping has expired
if (reason)
{
err_stream << tx << ", " << data << ", TX requested to update mapping that has already expired";
*reason = err_stream.str();
}
return false;
}
return false;
if (data.value == mapping.value)
{
if (reason)
{
err_stream << tx << ", " << data << ", value to update to is already the same as the mapping value";
*reason = err_stream.str();
}
return false;
}
// Validate signature
{
crypto::hash hash = tx_extra_signature_hash(epee::span<const uint8_t>(reinterpret_cast<const uint8_t *>(data.value.data()), data.value.size()), expected_prev_txid);
if (crypto_sign_verify_detached(data.signature.data, reinterpret_cast<unsigned char *>(hash.data), sizeof(hash.data), mapping.owner.data) != 0)
{
if (reason)
{
err_stream << tx << ", " << data << ", failed to verify signature for LNS update";
*reason = err_stream.str();
}
return false;
}
}
data.owner = mapping.owner;
}
lns::owner_record owner = lns_db.get_owner_by_id(mapping.owner_id);
if (!owner)
else
{
if (reason)
if (data.type != lns::mapping_type::lokinet)
{
error_stream_append_tx_msg(err_stream, tx, data) << "unexpected owner_id=" << mapping.owner_id << " does not exist";
*reason = err_stream.str();
if (reason)
{
lns::owner_record owner = lns_db.get_owner_by_id(mapping.owner_id);
err_stream << tx << ", " << data << ", non-lokinet entries can NOT be renewed, mapping already exists with name=" << mapping.name << ", owner=" << owner.key << ", type=" << mapping.type;
*reason = err_stream.str();
}
return false;
}
return false;
}
if (requester.id != owner.id)
{
if (reason)
uint64_t renew_window = 0;
uint64_t expiry_blocks = lns::lokinet_expiry_blocks(lns_db.network_type(), &renew_window);
uint64_t const renew_window_offset = expiry_blocks - renew_window;
uint64_t const min_renew_height = mapping.register_height + renew_window_offset;
if (min_renew_height >= blockchain_height)
{
error_stream_append_tx_msg(err_stream, tx, data) << " actual owner=" << owner.key << ", with owner_id=" << mapping.owner_id << ", does not match requester=" << requester.key << ", with id=" << requester.id;
err_stream << tx << ", " << data << ", trying to renew too early, the earliest renew height=" << min_renew_height << ", urrent height=" << blockchain_height;
*reason = err_stream.str();
return false; // Trying to renew too early
}
return false;
if (mapping.active(lns_db.network_type(), blockchain_height))
{
// Lokinet entry expired i.e. it's no longer active. A purchase for this name is valid
// Check that the request originates from the owner of this mapping
lns::owner_record const requester = lns_db.get_owner_by_key(data.owner);
if (!requester)
{
if (reason)
{
err_stream << tx << ", " << data << ", trying to renew existing mapping but owner specified in LNS extra does not exist, rejected";
*reason = err_stream.str();
}
return false;
}
lns::owner_record const owner = lns_db.get_owner_by_id(mapping.owner_id);
if (!owner)
{
if (reason)
{
err_stream << tx << ", " << data << ", unexpected owner_id=" << mapping.owner_id << " does not exist";
*reason = err_stream.str();
}
return false;
}
if (requester.id != owner.id)
{
if (reason)
{
err_stream << tx << ", " << data << ", actual owner=" << owner.key << ", with owner_id=" << mapping.owner_id << ", does not match requester=" << requester.key << ", with id=" << requester.id;
*reason = err_stream.str();
}
return false;
}
}
// else mapping has expired, new purchase is valid
}
}
@ -656,7 +749,7 @@ static bool validate_against_previous_mapping(lns::name_system_db const &lns_db,
{
if (reason)
{
error_stream_append_tx_msg(err_stream, tx, data) << " specified prior owner txid=" << data.prev_txid << ", but LNS DB reports=" << expected_prev_txid << ", possible competing TX was submitted and accepted before this TX was processed";
err_stream << tx << ", " << data << ", specified prior owner txid=" << data.prev_txid << ", but LNS DB reports=" << expected_prev_txid << ", possible competing TX was submitted and accepted before this TX was processed";
*reason = err_stream.str();
}
return false;
@ -671,11 +764,21 @@ bool name_system_db::validate_lns_tx(uint8_t hf_version, uint64_t blockchain_hei
if (!entry) entry = &entry_;
std::stringstream err_stream;
if (tx.type != cryptonote::txtype::loki_name_system)
{
if (reason)
{
err_stream << tx << ", uses wrong tx type, expected=" << cryptonote::txtype::loki_name_system;
*reason = err_stream.str();
}
return false;
}
if (!cryptonote::get_loki_name_system_from_tx_extra(tx.extra, *entry))
{
if (reason)
{
err_stream << "TX: " << tx.type << " " << get_transaction_hash(tx) << ", didn't have loki name service in the tx_extra";
err_stream << tx << ", didn't have loki name service in the tx_extra";
*reason = err_stream.str();
}
return false;
@ -685,7 +788,7 @@ bool name_system_db::validate_lns_tx(uint8_t hf_version, uint64_t blockchain_hei
{
if (reason)
{
err_stream << "TX: " << tx.type << " " << get_transaction_hash(tx) << " unexpected version=" << std::to_string(entry->version) << ", expected=0";
err_stream << tx << ", " << *entry << ", unexpected version=" << std::to_string(entry->version) << ", expected=0";
*reason = err_stream.str();
}
return false;
@ -695,12 +798,11 @@ bool name_system_db::validate_lns_tx(uint8_t hf_version, uint64_t blockchain_hei
{
if (reason)
{
err_stream << "TX: " << tx.type << " " << get_transaction_hash(tx) << " specifying type=" << static_cast<uint16_t>(entry->type) << " that is disallowed";
err_stream << tx << ", " << *entry << ", specifying type=" << entry->type << " that is disallowed";
*reason = err_stream.str();
}
return false;
}
uint64_t burn = cryptonote::get_burned_amount_from_tx_extra(tx.extra);
if (!validate_lns_name(entry->type, entry->name, reason))
return false;
@ -710,14 +812,16 @@ bool name_system_db::validate_lns_tx(uint8_t hf_version, uint64_t blockchain_hei
if (!validate_against_previous_mapping(*this, blockchain_height, tx, *entry, reason))
return false;
auto lns_type = static_cast<mapping_type>(entry->type);
uint64_t const burn_required = burn_requirement_in_atomic_loki(hf_version, mapping_type_to_burn_type(lns_type));
uint64_t burn = cryptonote::get_burned_amount_from_tx_extra(tx.extra);
uint64_t const burn_required = entry->command == lns::tx_command_t::buy
? burn_requirement_in_atomic_loki(hf_version, mapping_type_to_burn_type(entry->type))
: 0;
if (burn != burn_required)
{
if (reason)
{
char const *over_or_under = burn > burn_required ? "too much " : "insufficient ";
err_stream << "LNS TX=" << cryptonote::get_transaction_hash(tx) << ", burned " << over_or_under << "loki=" << burn << ", require=" << burn_required;
err_stream << tx << ", " << *entry << ", burned " << over_or_under << "loki=" << burn << ", require=" << burn_required;
*reason = err_stream.str();
}
return false;
@ -726,7 +830,7 @@ bool name_system_db::validate_lns_tx(uint8_t hf_version, uint64_t blockchain_hei
return true;
}
bool validate_mapping_type(std::string const &type, uint16_t *mapping_type, std::string *reason)
bool validate_mapping_type(std::string const &type, lns::mapping_type *mapping_type, std::string *reason)
{
std::string type_lowered = type;
for (char &ch : type_lowered)
@ -735,23 +839,35 @@ bool validate_mapping_type(std::string const &type, uint16_t *mapping_type, std:
ch = ch + ('a' - 'A');
}
uint16_t mapping_type_ = 0;
if (type_lowered == "session") mapping_type_ = static_cast<uint16_t>(lns::mapping_type::session);
lns::mapping_type mapping_type_ = lns::mapping_type::session;
if (type_lowered == "session") mapping_type_ = lns::mapping_type::session;
else
{
try
{
size_t value = std::stoul(type_lowered);
if (value > std::numeric_limits<uint16_t>::max())
if (value > tools::enum_count<lns::mapping_type>)
{
if (reason) *reason = "LNS type specifies value too large, must be from 0-65535: " + std::to_string(value);
if (reason) *reason = "LNS type specifies value too large, must be from [0-" + std::to_string(tools::enum_count<lns::mapping_type>) + "): " + std::to_string(value);
return false;
}
mapping_type_ = static_cast<uint16_t>(value);
mapping_type_ = static_cast<lns::mapping_type>(value);
}
catch (std::exception const &)
{
if (reason) *reason = "Failed to convert lns mapping (was not proper integer, or not one of the recognised: \"session\"), string was=" + type;
if (reason)
{
*reason = "Failed to convert lns mapping (was not proper integer, or not one of the recognised: \"session\"), string was";
if (type.empty())
{
*reason += " empty.";
}
else
{
*reason += "=";
*reason += type;
}
}
return false;
}
}
@ -917,9 +1033,15 @@ static bool add_lns_entry(lns::name_system_db &lns_db, uint64_t height, cryptono
if (owner_record owner = lns_db.get_owner_by_key(entry.owner)) owner_id = owner.id;
if (owner_id == 0)
{
if (entry.command == lns::tx_command_t::update)
{
MERROR("Owner does not exist but TX received is trying to update an existing mapping (i.e. owner should already exist). TX=" << tx_hash << " should have failed validation prior.");
return false;
}
if (!lns_db.save_owner(entry.owner, &owner_id))
{
LOG_PRINT_L1("Failed to save LNS owner to DB tx: " << tx_hash << ", type: " << (uint16_t)entry.type << ", name: " << entry.name << ", owner: " << entry.owner);
LOG_PRINT_L1("Failed to save LNS owner to DB tx: " << tx_hash << ", type: " << entry.type << ", name: " << entry.name << ", owner: " << entry.owner);
return false;
}
}
@ -927,7 +1049,7 @@ static bool add_lns_entry(lns::name_system_db &lns_db, uint64_t height, cryptono
if (!lns_db.save_mapping(tx_hash, entry, height, owner_id))
{
LOG_PRINT_L1("Failed to save LNS entry to DB tx: " << tx_hash << ", type: " << (uint16_t)entry.type << ", name: " << entry.name << ", owner: " << entry.owner);
LOG_PRINT_L1("Failed to save LNS entry to DB tx: " << tx_hash << ", type: " << entry.type << ", name: " << entry.name << ", owner: " << entry.owner);
return false;
}
@ -1137,11 +1259,11 @@ owner_record name_system_db::get_owner_by_id(int64_t owner_id) const
return result;
}
mapping_record name_system_db::get_mapping(uint16_t type, std::string const &name) const
mapping_record name_system_db::get_mapping(mapping_type type, std::string const &name) const
{
sqlite3_stmt *statement = get_mapping_sql;
sqlite3_clear_bindings(statement);
sqlite3_bind_int(statement, 1 /*sql param index*/, type);
sqlite3_bind_int(statement, 1 /*sql param index*/, static_cast<int>(type));
sqlite3_bind_text(statement, 2 /*sql param index*/, name.data(), name.size(), nullptr /*destructor*/);
mapping_record result = {};

View File

@ -3,6 +3,8 @@
#include "crypto/crypto.h"
#include "cryptonote_config.h"
#include "span.h"
#include "cryptonote_basic/tx_extra.h"
#include <string>
@ -12,7 +14,7 @@ namespace cryptonote
{
struct checkpoint_t;
struct block;
struct transaction;
class transaction;
struct account_address;
struct tx_extra_loki_name_system;
class Blockchain;
@ -35,35 +37,40 @@ struct lns_value
size_t len;
};
enum struct mapping_type : uint16_t
{
session = 0,
wallet = 1,
lokinet = 2,
};
constexpr bool mapping_type_allowed(uint8_t /*hf_version*/, uint16_t type) { return type == static_cast<uint16_t>(mapping_type::session); }
constexpr bool mapping_type_allowed(uint8_t hf_version, mapping_type type) { return mapping_type_allowed(hf_version, static_cast<uint16_t>(type)); }
enum struct burn_type
{
none,
update_record,
lokinet_1year,
session,
wallet,
custom,
};
inline std::ostream &operator<<(std::ostream &os, mapping_type type)
{
switch(type)
{
case mapping_type::lokinet: os << "lokinet"; break;
case mapping_type::session: os << "session"; break;
case mapping_type::wallet: os << "wallet"; break;
default: assert(false); os << "xx_unhandled_type"; break;
}
return os;
}
constexpr bool mapping_type_allowed(uint8_t hf_version, mapping_type type) { return type == mapping_type::session; }
burn_type mapping_type_to_burn_type(mapping_type in);
uint64_t burn_requirement_in_atomic_loki(uint8_t hf_version, burn_type type);
sqlite3 *init_loki_name_system(char const *file_path);
uint64_t lokinet_expiry_blocks(cryptonote::network_type nettype, uint64_t *renew_window = nullptr);
bool validate_lns_name(uint16_t type, std::string const &name, std::string *reason = nullptr);
crypto::hash tx_extra_signature_hash(epee::span<const uint8_t> blob, crypto::hash const &prev_txid);
bool validate_lns_name(mapping_type type, std::string const &name, std::string *reason = nullptr);
// blob: if set, validate_lns_value will convert the value into the binary format suitable for storing into the LNS DB.
bool validate_lns_value(cryptonote::network_type nettype, uint16_t type, std::string const &value, lns_value *blob = nullptr, std::string *reason = nullptr);
bool validate_lns_value_binary(uint16_t type, std::string const &value, std::string *reason = nullptr);
bool validate_mapping_type(std::string const &type, uint16_t *mapping_type, std::string *reason);
bool validate_lns_value(cryptonote::network_type nettype, mapping_type type, std::string const &value, lns_value *blob = nullptr, std::string *reason = nullptr);
bool validate_lns_value_binary(mapping_type type, std::string const &value, std::string *reason = nullptr);
bool validate_mapping_type(std::string const &type, lns::mapping_type *mapping_type, std::string *reason);
struct owner_record
{
@ -95,7 +102,7 @@ struct mapping_record
operator bool() const { return loaded; }
bool loaded;
uint16_t type; // alias to lns::mapping_type
mapping_type type; // alias to lns::mapping_type
std::string name;
std::string value;
uint64_t register_height;
@ -121,7 +128,7 @@ struct name_system_db
owner_record get_owner_by_key (crypto::ed25519_public_key const &key) const;
owner_record get_owner_by_id (int64_t owner_id) const;
mapping_record get_mapping (uint16_t type, std::string const &name) const;
mapping_record get_mapping (mapping_type type, std::string const &name) const;
std::vector<mapping_record> get_mappings (std::vector<uint16_t> const &types, std::string const &name) const;
std::vector<mapping_record> get_mappings_by_owner (crypto::ed25519_public_key const &key) const;
std::vector<mapping_record> get_mappings_by_owners(std::vector<crypto::ed25519_public_key> const &keys) const;

View File

@ -3378,7 +3378,7 @@ namespace cryptonote
res.entries.emplace_back();
COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry &entry = res.entries.back();
entry.entry_index = request_index;
entry.type = record.type;
entry.type = static_cast<uint16_t>(record.type);
entry.owner = epee::string_tools::pod_to_hex(record.owner);
entry.value = extract_lns_mapping_value(record);
entry.register_height = record.register_height;
@ -3431,7 +3431,7 @@ namespace cryptonote
}
entry.request_index = it->second;
entry.type = mapping.type;
entry.type = static_cast<uint16_t>(mapping.type);
entry.name = mapping.name;
entry.value = extract_lns_mapping_value(mapping);
entry.register_height = mapping.register_height;

View File

@ -86,6 +86,7 @@ BLOB_SERIALIZER(crypto::key_derivation);
BLOB_SERIALIZER(crypto::key_image);
BLOB_SERIALIZER(crypto::signature);
BLOB_SERIALIZER(crypto::ed25519_public_key);
BLOB_SERIALIZER(crypto::ed25519_signature);
VARIANT_TAG(debug_archive, crypto::hash, "hash");
VARIANT_TAG(debug_archive, crypto::hash8, "hash8");
VARIANT_TAG(debug_archive, crypto::public_key, "public_key");
@ -94,4 +95,5 @@ VARIANT_TAG(debug_archive, crypto::key_derivation, "key_derivation");
VARIANT_TAG(debug_archive, crypto::key_image, "key_image");
VARIANT_TAG(debug_archive, crypto::signature, "signature");
VARIANT_TAG(debug_archive, crypto::ed25519_public_key, "ed25519_public_key");
VARIANT_TAG(debug_archive, crypto::ed25519_signature, "ed25519_signature");

View File

@ -269,6 +269,7 @@ 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_BUY_LNS_MAPPING("buy_lns_mapping [index=<N1>[,<N2>,...]] [<priority>] [owner] \"<name>\" <value>");
const char* USAGE_UPDATE_LNS_MAPPING("update_lns_mapping [index=<N1>[,<N2>,...]] [<priority>] \"<name>\" <value> [<signature>]");
const char* USAGE_PRINT_LNS_OWNERS_TO_NAMES("print_lns_owners_to_names [<64 hex character ed25519 public key>]");
const char* USAGE_PRINT_LNS_NAME_TO_OWNERS("print_lns_name_to_owners [type=<N1|all>[,<N2>...]] \"name\"");
@ -3096,11 +3097,13 @@ Pending or Failed: "failed"|"pending", "out", Time, Amount*, Transaction Hash,
boost::bind(&simple_wallet::print_locked_stakes, this, _1),
tr(USAGE_PRINT_LOCKED_STAKES),
tr("Print stakes currently locked on the Service Node network"));
char const OWNER_KEY_EXPLANATION[] = "By default this is the public key of an ed25519 keypair derived using the wallet's secret spend key as the seed value. ";
char const AVAILABLE_LNS_RECORDS[] = "You are currenly only able to purchase Session mappings. ";
std::stringstream stream;
stream << "Buy a Loki Name Service mapping. Specifying `owner` is optional and defaults to the purchasing wallet if "
"empty or not specified. The `owner` should be a ed25519 public key; by default this is the public key of "
"an ed25519 keypair derived using the wallet's secret spend key as the seed value. You are currently only "
"able to purchase Session mappings\n\n";
stream << "Buy a Loki Name Service mapping. Specifying `owner` is optional and defaults to the purchasing wallet if empty or not specified. The `owner` should be a ed25519 public key. "
<< OWNER_KEY_EXPLANATION
<< AVAILABLE_LNS_RECORDS << "\n\n";
stream << "Session: (max: " << lns::SESSION_DISPLAY_NAME_MAX << " bytes) map a human readable name to a Session public key.\n";
m_cmd_binder.set_handler("buy_lns_mapping",
@ -3108,6 +3111,16 @@ Pending or Failed: "failed"|"pending", "out", Time, Amount*, Transaction Hash,
tr(USAGE_BUY_LNS_MAPPING),
tr(stream.str().c_str()));
stream.clear();
stream << "Update a Loki Name Service mapping's value field in the name->value mapping, you must be the owner of the the mapping by providing a signature that can be verified by the owner's public key."
<< OWNER_KEY_EXPLANATION
<< AVAILABLE_LNS_RECORDS
<< "The signature is derived using libsodium generichash on the {current txid blob, new value blob} of the mapping to update. Signature is an optional field and is signed using the wallet's spend key as an ed25519 keypair if it is not specified.";
m_cmd_binder.set_handler("update_lns_mapping",
boost::bind(&simple_wallet::update_lns_mapping, this, _1),
tr(USAGE_UPDATE_LNS_MAPPING),
tr(stream.str().c_str()));
m_cmd_binder.set_handler("print_lns_owners_to_names",
boost::bind(&simple_wallet::print_lns_owners_to_names, this, _1),
tr(USAGE_PRINT_LNS_OWNERS_TO_NAMES),
@ -6407,7 +6420,7 @@ bool simple_wallet::buy_lns_mapping(const std::vector<std::string>& args)
std::vector<tools::wallet2::pending_tx> ptx_vector;
try
{
ptx_vector = m_wallet->create_buy_lns_mapping_tx(static_cast<uint16_t>(lns::mapping_type::session),
ptx_vector = m_wallet->create_buy_lns_mapping_tx(lns::mapping_type::session,
owner,
name,
value,
@ -6444,6 +6457,91 @@ bool simple_wallet::buy_lns_mapping(const std::vector<std::string>& args)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::update_lns_mapping(const 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)) return false;
if (local_args.size() < 2)
{
PRINT_USAGE(USAGE_UPDATE_LNS_MAPPING);
return true;
}
std::string const *signature = nullptr;
std::string const *value = nullptr;
std::string const &last_word = local_args[local_args.size() - 1];
if (last_word.size() == (sizeof(crypto::signature) * 2))
{
if (local_args.size() == 2)
{
PRINT_USAGE(USAGE_UPDATE_LNS_MAPPING);
fail_msg_writer() << "Detected signature=" << last_word << ", but only 2 arguments given- missing either <value> or \"<name>\".";
return true;
}
signature = &last_word;
value = &local_args[local_args.size() - 2];
}
else
{
value = &last_word;
}
std::string name;
size_t first_word_index = 0;
size_t last_word_index = local_args.size() - 2;
if (!parse_lns_name_string(local_args, first_word_index, last_word_index, name))
{
PRINT_USAGE(USAGE_UPDATE_LNS_MAPPING);
fail_msg_writer() << "lns name didn't start or end with quotation marks (')";
return false;
}
SCOPED_WALLET_UNLOCK();
std::string reason;
std::vector<tools::wallet2::pending_tx> ptx_vector;
try
{
ptx_vector = m_wallet->create_update_lns_mapping_tx(lns::mapping_type::session,
name,
*value,
signature,
&reason,
priority,
m_current_subaddress_account,
subaddr_indices);
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);
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::print_lns_name_to_owners(const std::vector<std::string>& args)
{
if (!try_connect_to_daemon())
@ -6475,14 +6573,14 @@ bool simple_wallet::print_lns_name_to_owners(const std::vector<std::string>& arg
for (std::string const &type : split_types)
{
uint16_t mapping_type = 0;
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(mapping_type);
requested_types.push_back(static_cast<uint16_t>(mapping_type));
}
}
}
@ -6496,12 +6594,10 @@ bool simple_wallet::print_lns_name_to_owners(const std::vector<std::string>& arg
return false;
}
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request_entry> names;
names.emplace_back();
cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request_entry &entry = names.back();
entry.name = lns_name;
entry.types = std::move(requested_types);
cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request request = {};
request.entries.push_back({lns_name, std::move(requested_types)});
cryptonote::COMMAND_RPC_GET_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::wallet));
@ -6510,15 +6606,15 @@ bool simple_wallet::print_lns_name_to_owners(const std::vector<std::string>& arg
}
boost::optional<std::string> failed;
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> entries = m_wallet->get_lns_names_to_owners(names, failed);
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> response = m_wallet->get_lns_names_to_owners(request, failed);
if (failed)
{
fail_msg_writer() << *failed;
return false;
}
for (auto const &mapping : entries)
tools::msg_writer() << "name=\"" << entry.name << "\", owner=" << mapping.owner << ", type=" << mapping.type << ", height=" << mapping.register_height;
for (auto const &mapping : response)
tools::msg_writer() << "name=\"" << entry.name << "\", owner=" << mapping.owner << ", type=" << static_cast<lns::mapping_type>(mapping.type) << ", height=" << mapping.register_height;
return true;
}
@ -6528,10 +6624,10 @@ bool simple_wallet::print_lns_owners_to_names(const std::vector<std::string>& ar
if (!try_connect_to_daemon())
return false;
std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> entries;
boost::optional<std::string> failed;
std::string my_own_ed25519_key;
cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::request request = {};
if (args.size() == 0)
{
// TODO(doyle): I need to make this address visible easily for people, prior
@ -6541,7 +6637,7 @@ bool simple_wallet::print_lns_owners_to_names(const std::vector<std::string>& ar
crypto::ed25519_secret_key skey;
crypto_sign_ed25519_seed_keypair(pkey.data, skey.data, reinterpret_cast<const unsigned char *>(m_wallet->get_account().get_keys().m_spend_secret_key.data));
my_own_ed25519_key = epee::string_tools::pod_to_hex(pkey);
entries = m_wallet->get_lns_owners_to_names({my_own_ed25519_key}, failed);
request.entries.push_back(my_own_ed25519_key);
}
else
{
@ -6561,10 +6657,11 @@ bool simple_wallet::print_lns_owners_to_names(const std::vector<std::string>& ar
return false;
}
}
request.entries.push_back(arg);
}
entries = m_wallet->get_lns_owners_to_names(args, failed);
}
std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> entries = m_wallet->get_lns_owners_to_names(request, failed);
if (failed)
{
fail_msg_writer() << *failed;
@ -6587,7 +6684,7 @@ bool simple_wallet::print_lns_owners_to_names(const std::vector<std::string>& ar
}
}
tools::msg_writer() << "owner=" << *owner << ", height=" << entry.register_height << ", name=\"" << entry.name << "\", value=" << entry.value << ", prev_txid=" << entry.prev_txid;
tools::msg_writer() << "owner=" << *owner << ", type=" << static_cast<lns::mapping_type>(entry.type) << ", height=" << entry.register_height << ", name=\"" << entry.name << "\", value=" << entry.value << ", prev_txid=" << entry.prev_txid;
}
return true;
}

View File

@ -175,6 +175,7 @@ namespace cryptonote
bool print_locked_stakes(const std::vector<std::string>& /*args*/);
bool query_locked_stakes(bool print_result);
bool buy_lns_mapping(const std::vector<std::string> &args);
bool update_lns_mapping(const std::vector<std::string> &args);
bool print_lns_owners_to_names(const std::vector<std::string> &args);
bool print_lns_name_to_owners(const std::vector<std::string> &args);

View File

@ -406,7 +406,7 @@ std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::ent
return result;
}
std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> NodeRPCProxy::get_lns_owners_to_names(std::vector<std::string> const &owners, boost::optional<std::string> &failed) const
std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> NodeRPCProxy::get_lns_owners_to_names(cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::request const &request, boost::optional<std::string> &failed) const
{
if (m_offline)
{
@ -414,12 +414,10 @@ std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> Nod
return {};
}
cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::request req = {};
cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response res = {};
req.entries = owners;
{
std::lock_guard<std::recursive_mutex> lock(m_daemon_rpc_mutex);
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "get_lns_owners_to_names", req, res, m_http_client, rpc_timeout);
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "get_lns_owners_to_names", request, res, m_http_client, rpc_timeout);
if (!check_invoke(r, res, failed))
return {};
}
@ -427,7 +425,7 @@ std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> Nod
return res.entries;
}
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> NodeRPCProxy::get_lns_names_to_owners(std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request_entry> const &names, boost::optional<std::string> &failed) const
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> NodeRPCProxy::get_lns_names_to_owners(cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request const &request, boost::optional<std::string> &failed) const
{
if (m_offline)
{
@ -435,12 +433,10 @@ std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> Nod
return {};
}
cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request req = {};
cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response res = {};
req.entries = names;
{
std::lock_guard<std::recursive_mutex> lock(m_daemon_rpc_mutex);
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "get_lns_names_to_owners", req, res, m_http_client, rpc_timeout);
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "get_lns_names_to_owners", request, res, m_http_client, rpc_timeout);
if (!check_invoke(r, res, failed))
return {};
}

View File

@ -60,8 +60,8 @@ public:
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> get_all_service_nodes(boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> get_contributed_service_nodes(const std::string &contributor, boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> get_service_node_blacklisted_key_images(boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> get_lns_owners_to_names(std::vector<std::string> const &owners, boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> get_lns_names_to_owners(std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request_entry> const &names, boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> get_lns_owners_to_names(cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::request const &request, boost::optional<std::string> &failed) const;
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> get_lns_names_to_owners(cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request const &request, boost::optional<std::string> &failed) const;
private:
boost::optional<std::string> get_info() const;

View File

@ -8426,7 +8426,65 @@ wallet2::request_stake_unlock_result wallet2::can_request_stake_unlock(const cry
return result;
}
std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(uint16_t type,
static bool prepare_tx_extra_loki_name_system_values(cryptonote::network_type nettype,
lns::mapping_type type,
uint32_t priority,
std::string const &name,
std::string const &value,
wallet2 const &wallet,
crypto::hash &prev_txid,
lns::lns_value &value_blob,
std::string *reason)
{
if (priority == tools::tx_priority_blink)
{
if (reason) *reason = "Can not request a blink TX for Loki Name Service transactions";
return false;
}
if (!lns::validate_lns_name(type, name, reason))
return false;
if (!lns::validate_lns_value(nettype, type, value, &value_blob, reason))
return false;
prev_txid = crypto::null_hash;
{
cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request request = {};
{
request.entries.emplace_back();
auto &request_entry = request.entries.back();
request_entry.name = name;
request_entry.types.push_back(static_cast<uint16_t>(type));
}
boost::optional<std::string> failed;
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> response = wallet.get_lns_names_to_owners(request, failed);
if (failed)
{
if (reason) *reason = "Failed to query previous owner for LNS entry, reason=" + *failed;
return false;
}
if (response.size())
{
crypto::hash txid_hash;
if (epee::string_tools::hex_to_pod(response[0].txid, txid_hash))
{
prev_txid = txid_hash;
}
else
{
if (reason) *reason = "Failed to convert response txid=" + response[0].txid + " from the daemon into a 32 byte hash, it must be a 64 char hex string";
return false;
}
}
}
return true;
}
std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(lns::mapping_type type,
std::string const &owner,
std::string const &name,
std::string const &value,
@ -8435,19 +8493,6 @@ std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(uint16_t typ
uint32_t account_index,
std::set<uint32_t> subaddr_indices)
{
if (!lns::validate_lns_name(type, name, reason))
return {};
lns::lns_value value_blob;
if (!lns::validate_lns_value(nettype(), type, value, &value_blob, reason))
return {};
if (priority == tools::tx_priority_blink)
{
if (reason) *reason = "Can not request a blink TX for Loki Name Service transactions";
return {};
}
crypto::ed25519_public_key pkey;
if (owner.size())
{
@ -8463,38 +8508,14 @@ std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(uint16_t typ
crypto_sign_ed25519_seed_keypair(pkey.data, skey.data, reinterpret_cast<const unsigned char *>(&m_account.get_keys().m_spend_secret_key));
}
crypto::hash prev_txid = crypto::null_hash;
{
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request_entry> request = {};
{
request.emplace_back();
auto request_entry = request.back();
request_entry.name = name;
request_entry.types.push_back(type);
}
lns::lns_value value_blob;
crypto::hash prev_txid;
if (!prepare_tx_extra_loki_name_system_values(nettype(), type, priority, name, value, *this, prev_txid, value_blob, reason))
return {};
boost::optional<std::string> failed;
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> response = get_lns_names_to_owners(request, failed);
if (failed)
{
if (reason) *reason = "Failed to query previous owner for LNS entry, reason=" + *failed;
return {};
}
if (response.size())
{
crypto::hash txid_hash;
if (epee::string_tools::hex_to_pod(response[0].txid, txid_hash))
{
prev_txid = txid_hash;
}
else
{
if (reason) *reason = "Failed to convert response txid=" + response[0].txid + " from the daemon into a 32 byte hash, it must be a 64 char hex string";
return {};
}
}
}
std::vector<uint8_t> extra;
auto entry = cryptonote::tx_extra_loki_name_system::make_buy(pkey, type, name, std::string(reinterpret_cast<char const *>(value_blob.buffer.data()), value_blob.len), prev_txid);
add_loki_name_system_to_tx_extra(extra, entry);
boost::optional<uint8_t> hf_version = get_hard_fork_version();
if (!hf_version)
@ -8502,16 +8523,8 @@ std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(uint16_t typ
if (reason) *reason = ERR_MSG_NETWORK_VERSION_QUERY_FAILED;
return {};
}
lns::lns_value blob;
if (!lns::validate_lns_value(nettype(), type, value, &blob, reason))
return {};
std::vector<uint8_t> extra;
tx_extra_loki_name_system entry(pkey, type, name, std::string(reinterpret_cast<char const *>(blob.buffer.data()), blob.len), prev_txid);
add_loki_name_system_to_tx_extra(extra, entry);
loki_construct_tx_params tx_params = wallet2::construct_params(*hf_version, txtype::loki_name_system, priority, lns::mapping_type_to_burn_type(static_cast<lns::mapping_type>(type)));
auto result = create_transactions_2({} /*dests*/,
CRYPTONOTE_DEFAULT_TX_MIXIN,
0 /*unlock_at_block*/,
@ -8532,7 +8545,7 @@ std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(std::string
uint32_t account_index,
std::set<uint32_t> subaddr_indices)
{
uint16_t mapping_type = 0;
lns::mapping_type mapping_type = lns::mapping_type::session;
if (!lns::validate_mapping_type(type, &mapping_type, reason))
return {};
@ -8540,6 +8553,79 @@ std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(std::string
return result;
}
std::vector<wallet2::pending_tx> wallet2::create_update_lns_mapping_tx(lns::mapping_type type,
std::string const &name,
std::string const &value,
std::string const *signature,
std::string *reason,
uint32_t priority,
uint32_t account_index,
std::set<uint32_t> subaddr_indices)
{
crypto::hash prev_txid;
lns::lns_value value_blob;
if (!prepare_tx_extra_loki_name_system_values(nettype(), type, priority, name, value, *this, prev_txid, value_blob, reason))
return {};
crypto::ed25519_public_key pkey;
crypto::ed25519_signature signature_binary;
if (signature)
{
if (!epee::string_tools::hex_to_pod(*signature, signature_binary))
{
if (reason) *reason = "Hex signature provided failed to convert to a ed25519_signature, signature=" + *signature;
return {};
}
}
else
{
crypto::ed25519_secret_key skey;
crypto_sign_ed25519_seed_keypair(pkey.data, skey.data, reinterpret_cast<const unsigned char *>(&m_account.get_keys().m_spend_secret_key));
crypto::hash hash = lns::tx_extra_signature_hash(epee::span<const uint8_t>(value_blob.buffer.data(), value_blob.len), prev_txid);
crypto_sign_detached(signature_binary.data, NULL, reinterpret_cast<unsigned char *>(hash.data), sizeof(hash.data), skey.data);
}
std::vector<uint8_t> extra;
auto entry = cryptonote::tx_extra_loki_name_system::make_update(signature_binary, type, name, std::string(reinterpret_cast<char const *>(value_blob.buffer.data()), value_blob.len), prev_txid);
add_loki_name_system_to_tx_extra(extra, entry);
boost::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, lns::burn_type::update_record);
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::create_update_lns_mapping_tx(std::string const &type,
std::string const &name,
std::string const &value,
std::string const *signature,
std::string *reason,
uint32_t priority,
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))
return {};
std::vector<wallet2::pending_tx> result = create_update_lns_mapping_tx(mapping_type, name, value, signature, reason, priority, account_index, subaddr_indices);
return result;
}
bool wallet2::lock_keys_file()
{
if (m_keys_file_locker)
@ -10259,8 +10345,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
boost::unique_lock<hw::device> hwdev_lock (hwdev);
hw::reset_mode rst(hwdev);
bool const is_lns_tx = (tx_params.tx_type == txtype::loki_name_system);
auto original_dsts = dsts;
if (tx_params.tx_type == txtype::loki_name_system)
if (is_lns_tx)
{
THROW_WALLET_EXCEPTION_IF(dsts.size() != 0, error::wallet_internal_error, "loki name system txs must not have any destinations set, has: " + std::to_string(dsts.size()));
dsts.emplace_back(0, account_public_address{} /*address*/, false /*is_subaddress*/); // NOTE: Create a dummy dest that gets repurposed into the change output.
@ -10349,14 +10436,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
needed_money = 0;
for(auto& dt: dsts)
{
THROW_WALLET_EXCEPTION_IF(0 == dt.amount && (tx_params.tx_type != txtype::loki_name_system), error::zero_destination);
THROW_WALLET_EXCEPTION_IF(0 == dt.amount && !is_lns_tx, error::zero_destination);
needed_money += dt.amount;
LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money));
THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, 0, m_nettype);
}
// throw if attempting a transaction with no money
THROW_WALLET_EXCEPTION_IF(needed_money == 0 && (tx_params.tx_type != txtype::loki_name_system), error::zero_destination);
THROW_WALLET_EXCEPTION_IF(needed_money == 0 && !is_lns_tx, error::zero_destination);
std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account);
@ -10547,7 +10634,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
idx = pop_back(preferred_inputs);
pop_if_present(*unused_transfers_indices, idx);
pop_if_present(*unused_dust_indices, idx);
} else if ((dsts.empty() || (dsts[0].amount == 0 && tx_params.tx_type != txtype::loki_name_system)) && !adding_fee) {
} else if ((dsts.empty() || (dsts[0].amount == 0 && !is_lns_tx)) && !adding_fee) {
// NOTE: A LNS tx sets dsts[0].amount to 0, but this branch is for the
// 2 inputs/2 outputs. We only have 1 output as LNS transactions are
// distinguishable, so we actually want the last branch which uses unused

View File

@ -1023,8 +1023,8 @@ private:
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> get_all_service_nodes(boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_all_service_nodes(failed); }
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODES::response::entry> get_service_nodes (std::vector<std::string> const &pubkeys, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_service_nodes(pubkeys, failed); }
std::vector<cryptonote::COMMAND_RPC_GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry> get_service_node_blacklisted_key_images(boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_service_node_blacklisted_key_images(failed); }
std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> get_lns_owners_to_names(std::vector<std::string> const &owners, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_lns_owners_to_names(owners, failed); }
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> get_lns_names_to_owners(std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request_entry> const &names, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_lns_names_to_owners(names, failed); }
std::vector<cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::response_entry> get_lns_owners_to_names(cryptonote::COMMAND_RPC_GET_LNS_OWNERS_TO_NAMES::request const &request, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_lns_owners_to_names(request, failed); }
std::vector<cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::response_entry> get_lns_names_to_owners(cryptonote::COMMAND_RPC_GET_LNS_NAMES_TO_OWNERS::request const &request, boost::optional<std::string> &failed) const { return m_node_rpc_proxy.get_lns_names_to_owners(request, failed); }
uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); }
void rescan_spent();
@ -1538,9 +1538,14 @@ private:
pending_tx ptx;
};
request_stake_unlock_result can_request_stake_unlock(const crypto::public_key &sn_key);
std::vector<wallet2::pending_tx> create_buy_lns_mapping_tx(uint16_t type, std::string const &owner, std::string const &name, std::string const &value, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {});
std::vector<wallet2::pending_tx> create_buy_lns_mapping_tx(lns::mapping_type type, std::string const &owner, std::string const &name, std::string const &value, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {});
std::vector<wallet2::pending_tx> create_buy_lns_mapping_tx(std::string const &type, std::string const &owner, std::string const &name, std::string const &value, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {});
// signature: (Optional) If set, use the signature given, otherwise by default derive the signature from the wallet spend key as an ed25519 key.
// The signature is derived from the hash of the previous txid blob and previous value blob of the mapping. By default this is signed using the wallet's spend key as an ed25519 keypair.
std::vector<wallet2::pending_tx> create_update_lns_mapping_tx(lns::mapping_type type, std::string const &name, std::string const &value, std::string const *signature, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {});
std::vector<wallet2::pending_tx> create_update_lns_mapping_tx(std::string const &type, std::string const &name, std::string const &value, std::string const *signature, std::string *reason, uint32_t priority = 0, uint32_t account_index = 0, std::set<uint32_t> subaddr_indices = {});
void freeze(size_t idx);
void thaw(size_t idx);
bool frozen(size_t idx) const;

View File

@ -4315,6 +4315,51 @@ namespace tools
res.tx_metadata,
er);
}
bool wallet_rpc_server::on_update_lns_mapping(const wallet_rpc::COMMAND_RPC_UPDATE_LNS_MAPPING::request &req, wallet_rpc::COMMAND_RPC_UPDATE_LNS_MAPPING::response &res, epee::json_rpc::error &er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Updating lns mappings is unavailable in restricted mode.";
return false;
}
std::string reason;
std::vector<wallet2::pending_tx> ptx_vector =
m_wallet->create_update_lns_mapping_tx(req.type,
req.name,
req.value,
req.signature.empty() ? nullptr : &req.signature,
&reason,
req.priority,
req.account_index,
req.subaddr_indices);
if (ptx_vector.empty())
{
er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE;
er.message = "Failed to create LNS update transaction: " + reason;
return false;
}
return 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,
er);
}
}
class t_daemon

View File

@ -165,6 +165,7 @@ namespace tools
MAP_JON_RPC_WE("can_request_stake_unlock", on_can_request_stake_unlock, wallet_rpc::COMMAND_RPC_CAN_REQUEST_STAKE_UNLOCK)
MAP_JON_RPC_WE("request_stake_unlock", on_request_stake_unlock, wallet_rpc::COMMAND_RPC_REQUEST_STAKE_UNLOCK)
MAP_JON_RPC_WE("buy_lns_mapping", on_buy_lns_mapping, wallet_rpc::COMMAND_RPC_BUY_LNS_MAPPING)
MAP_JON_RPC_WE("update_lns_mapping", on_update_lns_mapping, wallet_rpc::COMMAND_RPC_UPDATE_LNS_MAPPING)
END_JSON_RPC_MAP()
END_URI_MAP2()
@ -256,6 +257,7 @@ namespace tools
bool on_can_request_stake_unlock(const wallet_rpc::COMMAND_RPC_CAN_REQUEST_STAKE_UNLOCK::request& req, wallet_rpc::COMMAND_RPC_CAN_REQUEST_STAKE_UNLOCK::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_request_stake_unlock(const wallet_rpc::COMMAND_RPC_REQUEST_STAKE_UNLOCK::request& req, wallet_rpc::COMMAND_RPC_REQUEST_STAKE_UNLOCK::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_buy_lns_mapping(const wallet_rpc::COMMAND_RPC_BUY_LNS_MAPPING::request& req, wallet_rpc::COMMAND_RPC_BUY_LNS_MAPPING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_update_lns_mapping(const wallet_rpc::COMMAND_RPC_UPDATE_LNS_MAPPING::request& req, wallet_rpc::COMMAND_RPC_UPDATE_LNS_MAPPING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
//json rpc v2
bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);

View File

@ -2869,7 +2869,7 @@ namespace wallet_rpc
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-3 for: default, unimportant, normal, elevated, priority.
uint32_t priority; // Set a priority for the transaction. Accepted values are: or 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)
@ -2916,5 +2916,66 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
LOKI_RPC_DOC_INTROSPECT
// Update the underlying value in the name->value mapping via Loki Name Service.
struct COMMAND_RPC_UPDATE_LNS_MAPPING
{
struct request_t
{
std::string type; // The mapping type, currently only "session" is supported.
std::string name; // The name to update via Loki Name Service
std::string value; // The new value that the name maps to via Loki Name Service, (i.e. For session: display name -> session public key).
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.
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)
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE (type);
KV_SERIALIZE (name);
KV_SERIALIZE (value);
KV_SERIALIZE_OPT(signature, std::string(""));
KV_SERIALIZE_OPT(account_index, (uint32_t)0);
KV_SERIALIZE_OPT(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)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
std::string tx_hash; // Publically 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.
std::string tx_blob; // Raw transaction represented as hex string, if get_tx_hex is true.
std::string tx_metadata; // Set of transaction metadata needed to relay this transfer later, if `get_tx_metadata` is `true`.
std::string multisig_txset; // Set of multisig transactions in the process of being signed (empty for non-multisig).
std::string unsigned_txset; // Set of unsigned tx for cold-signing purposes.
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
KV_SERIALIZE(multisig_txset)
KV_SERIALIZE(unsigned_txset)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
}
}

View File

@ -283,7 +283,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,
uint16_t type,
lns::mapping_type type,
std::string const &value,
std::string const &name,
crypto::ed25519_public_key const *owner,
@ -294,6 +294,17 @@ loki_chain_generator::create_and_add_loki_name_system_tx(cryptonote::account_bas
return t;
}
cryptonote::transaction loki_chain_generator::create_and_add_loki_name_system_tx_update(cryptonote::account_base const &src,
lns::mapping_type type,
std::string const &value,
std::string const &name,
bool kept_by_block)
{
cryptonote::transaction t = create_loki_name_system_tx_update(src, type, value, 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,
@ -501,7 +512,7 @@ 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,
uint16_t type,
lns::mapping_type type,
std::string const &value,
std::string const &name,
crypto::ed25519_public_key const *owner,
@ -522,15 +533,14 @@ cryptonote::transaction loki_chain_generator::create_loki_name_system_tx(crypton
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_requirement_in_atomic_loki(new_hf_version,
lns::mapping_type_to_burn_type(static_cast<lns::mapping_type>(type)));
burn = lns::burn_requirement_in_atomic_loki(new_hf_version, lns::mapping_type_to_burn_type(type));
crypto::hash prev_txid = crypto::null_hash;
if (lns::mapping_record mapping = lns_db_.get_mapping(type, name))
prev_txid = mapping.txid;
std::vector<uint8_t> extra;
cryptonote::tx_extra_loki_name_system data(pkey, type, name, value, prev_txid);
cryptonote::tx_extra_loki_name_system data = cryptonote::tx_extra_loki_name_system::make_buy(pkey, type, name, value, prev_txid);
cryptonote::add_loki_name_system_to_tx_extra(extra, data);
cryptonote::add_burned_amount_to_tx_extra(extra, burn);
cryptonote::transaction result = {};
@ -543,6 +553,66 @@ cryptonote::transaction loki_chain_generator::create_loki_name_system_tx(crypton
return result;
}
cryptonote::transaction loki_chain_generator::create_loki_name_system_tx_update(cryptonote::account_base const &src,
lns::mapping_type type,
std::string const &value,
std::string const &name,
crypto::ed25519_signature *signature,
bool use_asserts) const
{
lns::mapping_record mapping = lns_db_.get_mapping(type, name);
crypto::hash prev_txid = mapping.txid;
if (use_asserts) assert(mapping);
crypto::ed25519_public_key pkey;
crypto::ed25519_secret_key skey;
crypto_sign_ed25519_seed_keypair(pkey.data, skey.data, reinterpret_cast<const unsigned char *>(&src.get_keys().m_spend_secret_key));
crypto::ed25519_signature signature_ = {};
if (!signature)
{
signature = &signature_;
crypto::hash hash = lns::tx_extra_signature_hash(epee::span<const uint8_t>(reinterpret_cast<const uint8_t *>(value.data()), value.size()), prev_txid);
crypto_sign_detached(signature->data, NULL, reinterpret_cast<unsigned char *>(hash.data), sizeof(hash.data), skey.data);
}
std::vector<uint8_t> extra;
cryptonote::tx_extra_loki_name_system data = cryptonote::tx_extra_loki_name_system::make_update(*signature, type, name, value, prev_txid);
cryptonote::add_loki_name_system_to_tx_extra(extra, data);
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);
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(TESTS_DEFAULT_FEE)
.build();
return result;
}
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
{
std::vector<uint8_t> extra;
cryptonote::add_loki_name_system_to_tx_extra(extra, lns_extra);
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);
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(TESTS_DEFAULT_FEE)
.build();
return result;
}
static void fill_nonce(cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height)
{
blk.nonce = 0;

View File

@ -1421,7 +1421,8 @@ 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, uint16_t type, std::string const &value, std::string const &name, crypto::ed25519_public_key const *owner = nullptr, bool kept_by_block = false);
cryptonote::transaction create_and_add_loki_name_system_tx(cryptonote::account_base const &src, lns::mapping_type type, std::string const &value, std::string const &name, crypto::ed25519_public_key const *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 &value, 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);
@ -1442,7 +1443,9 @@ 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, uint16_t type, std::string const &value, std::string const &name, crypto::ed25519_public_key const *owner = nullptr, uint64_t burn = LNS_AUTO_BURN) const;
cryptonote::transaction create_loki_name_system_tx (cryptonote::account_base const &src, lns::mapping_type type, std::string const &value, std::string const &name, crypto::ed25519_public_key const *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 &value, std::string const &name, crypto::ed25519_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;
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

@ -134,6 +134,11 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(loki_name_system_large_reorg);
GENERATE_AND_PLAY(loki_name_system_name_renewal);
GENERATE_AND_PLAY(loki_name_system_name_value_max_lengths);
GENERATE_AND_PLAY(loki_name_system_update_mapping_after_expiry_fails);
GENERATE_AND_PLAY(loki_name_system_update_mapping);
GENERATE_AND_PLAY(loki_name_system_update_mapping_non_existent_name_fails);
GENERATE_AND_PLAY(loki_name_system_update_mapping_invalid_signature);
GENERATE_AND_PLAY(loki_name_system_update_mapping_replay);
GENERATE_AND_PLAY(loki_name_system_wrong_burn);
GENERATE_AND_PLAY(loki_name_system_wrong_version);
GENERATE_AND_PLAY(loki_service_nodes_alt_quorums);

View File

@ -1004,7 +1004,7 @@ bool loki_name_system_disallow_reserved_type::generate(std::vector<test_event_en
std::string mapping_value = "asdf";
auto unusable_type = static_cast<uint16_t>(-1);
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, mapping_value, "FriendlyName");
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");
@ -1058,7 +1058,7 @@ bool loki_name_system_expiration::generate(std::vector<test_event_entry> &events
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, static_cast<uint16_t>(lns::mapping_type::lokinet), miner_key.lokinet_value, name);
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet, miner_key.lokinet_value, name);
gen.create_and_add_next_block({tx});
uint64_t height_of_lns_entry = gen.height();
@ -1074,9 +1074,9 @@ bool loki_name_system_expiration::generate(std::vector<test_event_entry> &events
CHECK_EQ(owner.id, 1);
CHECK_EQ(miner_key.ed_key, owner.key);
lns::mapping_record mappings = lns_db.get_mapping(static_cast<uint16_t>(lns::mapping_type::lokinet), name);
lns::mapping_record mappings = lns_db.get_mapping(lns::mapping_type::lokinet, name);
CHECK_EQ(mappings.loaded, true);
CHECK_EQ(mappings.type, static_cast<uint16_t>(lns::mapping_type::lokinet));
CHECK_EQ(mappings.type, lns::mapping_type::lokinet);
CHECK_EQ(mappings.name, name);
CHECK_EQ(mappings.value, miner_key.lokinet_value);
CHECK_EQ(mappings.register_height, height_of_lns_entry);
@ -1098,10 +1098,10 @@ bool loki_name_system_expiration::generate(std::vector<test_event_entry> &events
CHECK_EQ(owner.id, 1);
CHECK_EQ(miner_key.ed_key, owner.key);
lns::mapping_record mappings = lns_db.get_mapping(static_cast<uint16_t>(lns::mapping_type::lokinet), name);
lns::mapping_record mappings = lns_db.get_mapping(lns::mapping_type::lokinet, name);
CHECK_EQ(mappings.loaded, true);
CHECK_EQ(mappings.active(cryptonote::FAKECHAIN, blockchain_height), false);
CHECK_EQ(mappings.type, static_cast<uint16_t>(lns::mapping_type::lokinet));
CHECK_EQ(mappings.type, lns::mapping_type::lokinet);
CHECK_EQ(mappings.name, name);
CHECK_EQ(mappings.value, miner_key.lokinet_value);
CHECK_EQ(mappings.register_height, height_of_lns_entry);
@ -1135,8 +1135,8 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
std::string session_name1 = "MyName";
std::string session_name2 = "AnotherName";
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, session_name1);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, session_name2, &bob_key.ed_key);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, bob_key.session_value, session_name1);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, bob_key.session_value, session_name2, &bob_key.ed_key);
gen.create_and_add_next_block({tx1, tx2});
}
uint64_t session_height = gen.height();
@ -1147,8 +1147,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::lokinet))
{
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, static_cast<uint16_t>(lns::mapping_type::lokinet), bob_key.lokinet_value, lokinet_name1);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::lokinet), bob_key.lokinet_value, lokinet_name2, &bob_key.ed_key);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::lokinet, bob_key.lokinet_value, lokinet_name1);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet, bob_key.lokinet_value, lokinet_name2, &bob_key.ed_key);
gen.create_and_add_next_block({tx1, tx2});
}
}
@ -1161,8 +1161,8 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
{
{
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, static_cast<uint16_t>(lns::mapping_type::wallet), bob_key.wallet_value, wallet_name1);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::wallet), bob_key.wallet_value, wallet_name2, &bob_key.ed_key);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::wallet, bob_key.wallet_value, wallet_name1);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::wallet, bob_key.wallet_value, wallet_name2, &bob_key.ed_key);
gen.create_and_add_next_block({tx1, tx2});
}
}
@ -1192,8 +1192,8 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
CHECK_EQ(records[1].register_height, session_height);
CHECK_EQ(records[0].value, bob_key.session_value);
CHECK_EQ(records[1].value, bob_key.session_value);
CHECK_EQ(records[0].type, static_cast<uint16_t>(lns::mapping_type::session));
CHECK_EQ(records[1].type, static_cast<uint16_t>(lns::mapping_type::session));
CHECK_EQ(records[0].type, lns::mapping_type::session);
CHECK_EQ(records[1].type, lns::mapping_type::session);
}
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet))
@ -1204,8 +1204,8 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
CHECK_EQ(records[3].register_height, lokinet_height);
CHECK_EQ(records[2].value, bob_key.lokinet_value);
CHECK_EQ(records[3].value, bob_key.lokinet_value);
CHECK_EQ(records[2].type, static_cast<uint16_t>(lns::mapping_type::lokinet));
CHECK_EQ(records[3].type, static_cast<uint16_t>(lns::mapping_type::lokinet));
CHECK_EQ(records[2].type, lns::mapping_type::lokinet);
CHECK_EQ(records[3].type, lns::mapping_type::lokinet);
}
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::wallet))
@ -1216,8 +1216,8 @@ bool loki_name_system_get_mappings_by_owner::generate(std::vector<test_event_ent
CHECK_EQ(records[5].register_height, wallet_height);
CHECK_EQ(records[4].value, bob_key.wallet_value);
CHECK_EQ(records[5].value, bob_key.wallet_value);
CHECK_EQ(records[4].type, static_cast<uint16_t>(lns::mapping_type::wallet));
CHECK_EQ(records[5].type, static_cast<uint16_t>(lns::mapping_type::wallet));
CHECK_EQ(records[4].type, lns::mapping_type::wallet);
CHECK_EQ(records[5].type, lns::mapping_type::wallet);
}
return true;
});
@ -1249,7 +1249,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, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, session_name1);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, bob_key.session_value, session_name1);
session_tx_hash1 = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
@ -1258,7 +1258,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, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, session_name2);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, bob_key.session_value, session_name2);
session_tx_hash2 = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
@ -1267,7 +1267,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, static_cast<uint16_t>(lns::mapping_type::session), miner_key.session_value, session_name3);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, miner_key.session_value, session_name3);
session_tx_hash3 = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
@ -1286,7 +1286,7 @@ bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_en
std::sort(records.begin(), records.end(), [](lns::mapping_record const &lhs, lns::mapping_record const &rhs) {
return lhs.register_height < rhs.register_height;
});
CHECK_EQ(records[0].type, static_cast<uint16_t>(lns::mapping_type::session));
CHECK_EQ(records[0].type, lns::mapping_type::session);
CHECK_EQ(records[0].name, session_name1);
CHECK_EQ(records[0].value, bob_key.session_value);
CHECK_EQ(records[0].register_height, session_height1);
@ -1294,7 +1294,7 @@ bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_en
CHECK_EQ(records[0].txid, session_tx_hash1);
CHECK_EQ(records[0].owner, bob_key.ed_key);
CHECK_EQ(records[1].type, static_cast<uint16_t>(lns::mapping_type::session));
CHECK_EQ(records[1].type, lns::mapping_type::session);
CHECK_EQ(records[1].name, session_name2);
CHECK_EQ(records[1].value, bob_key.session_value);
CHECK_EQ(records[1].register_height, session_height2);
@ -1302,7 +1302,7 @@ bool loki_name_system_get_mappings_by_owners::generate(std::vector<test_event_en
CHECK_EQ(records[1].txid, session_tx_hash2);
CHECK_EQ(records[1].owner, bob_key.ed_key);
CHECK_EQ(records[2].type, static_cast<uint16_t>(lns::mapping_type::session));
CHECK_EQ(records[2].type, lns::mapping_type::session);
CHECK_EQ(records[2].name, session_name3);
CHECK_EQ(records[2].value, miner_key.session_value);
CHECK_EQ(records[2].register_height, session_height3);
@ -1338,7 +1338,7 @@ bool loki_name_system_get_mappings::generate(std::vector<test_event_entry> &even
std::string session_name1 = "MyName";
crypto::hash session_tx_hash;
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, session_name1);
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, bob_key.session_value, session_name1);
session_tx_hash = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
@ -1350,7 +1350,7 @@ bool loki_name_system_get_mappings::generate(std::vector<test_event_entry> &even
lns::name_system_db const &lns_db = c.get_blockchain_storage().name_system_db();
std::vector<lns::mapping_record> records = lns_db.get_mappings({static_cast<uint16_t>(lns::mapping_type::session)}, session_name1);
CHECK_EQ(records.size(), 1);
CHECK_EQ(records[0].type, static_cast<uint16_t>(lns::mapping_type::session));
CHECK_EQ(records[0].type, lns::mapping_type::session);
CHECK_EQ(records[0].name, session_name1);
CHECK_EQ(records[0].value, bob_key.session_value);
CHECK_EQ(records[0].register_height, session_height);
@ -1382,10 +1382,10 @@ bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_eve
lns_keys_t miner_key = make_lns_keys(miner);
lns_keys_t bob_key = make_lns_keys(bob);
std::string session_name = "myfriendlydisplayname.loki";
uint16_t custom_type = 3928;
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, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, session_name);
cryptonote::transaction bar = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, bob_key.session_value, session_name);
std::vector<cryptonote::transaction> txs;
txs.push_back(bar);
@ -1398,7 +1398,7 @@ bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_eve
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
cryptonote::transaction bar3 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::lokinet), miner_key.lokinet_value, session_name);
cryptonote::transaction bar3 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet, miner_key.lokinet_value, session_name);
txs.push_back(bar3);
}
@ -1407,7 +1407,7 @@ 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, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, session_name);
cryptonote::transaction bar6 = gen.create_loki_name_system_tx(bob, lns::mapping_type::session, bob_key.session_value, session_name);
gen.add_tx(bar6, false /*can_be_added_to_blockchain*/, "Duplicate name requested by new owner: original already exists in lns db");
}
@ -1421,9 +1421,9 @@ bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_eve
CHECK_EQ(owner.id, 1);
CHECK_EQ(miner_key.ed_key, owner.key);
lns::mapping_record mappings = lns_db.get_mapping(static_cast<uint16_t>(lns::mapping_type::session), session_name);
lns::mapping_record mappings = lns_db.get_mapping(lns::mapping_type::session, session_name);
CHECK_EQ(mappings.loaded, true);
CHECK_EQ(mappings.type, static_cast<uint16_t>(lns::mapping_type::session));
CHECK_EQ(mappings.type, lns::mapping_type::session);
CHECK_EQ(mappings.name, session_name);
CHECK_EQ(mappings.value, bob_key.session_value);
CHECK_EQ(mappings.register_height, height_of_lns_entry);
@ -1431,9 +1431,9 @@ bool loki_name_system_handles_duplicate_in_lns_db::generate(std::vector<test_eve
if (lns::mapping_type_allowed(c.get_blockchain_storage().get_current_hard_fork_version(), lns::mapping_type::lokinet))
{
lns::mapping_record mappings2 = lns_db.get_mapping(static_cast<uint16_t>(lns::mapping_type::lokinet), session_name);
lns::mapping_record mappings2 = lns_db.get_mapping(lns::mapping_type::lokinet, session_name);
CHECK_EQ(mappings2.loaded, true);
CHECK_EQ(mappings2.type, static_cast<uint16_t>(lns::mapping_type::lokinet));
CHECK_EQ(mappings2.type, lns::mapping_type::lokinet);
CHECK_EQ(mappings2.name, session_name);
CHECK_EQ(mappings2.value, miner_key.lokinet_value);
CHECK_EQ(mappings2.register_height, height_of_lns_entry);
@ -1479,16 +1479,16 @@ bool loki_name_system_handles_duplicate_in_tx_pool::generate(std::vector<test_ev
lns_keys_t bob_key = make_lns_keys(bob);
std::string session_name = "myfriendlydisplayname.loki";
uint16_t custom_type = 3928;
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, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, session_name);
cryptonote::transaction bar = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, bob_key.session_value, session_name);
if (lns::mapping_type_allowed(gen.hardfork(), custom_type))
cryptonote::transaction bar2 = gen.create_and_add_loki_name_system_tx(miner, custom_type, bob_key.session_value, session_name);
// NOTE: Make duplicate in the TX pool, this should be rejected
cryptonote::transaction bar4 = gen.create_loki_name_system_tx(bob, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, session_name);
cryptonote::transaction bar4 = gen.create_loki_name_system_tx(bob, lns::mapping_type::session, bob_key.session_value, session_name);
gen.add_tx(bar4, false /*can_be_added_to_blockchain*/, "Duplicate name requested by new owner: original already exists in tx pool");
}
return true;
@ -1535,7 +1535,7 @@ bool loki_name_system_invalid_tx_extra_params::generate(std::vector<test_event_e
cryptonote::tx_extra_loki_name_system valid_data = {};
valid_data.owner = miner_key.ed_key;
valid_data.type = static_cast<uint16_t>(lns::mapping_type::wallet);
valid_data.type = lns::mapping_type::wallet;
valid_data.value = miner_key.wallet_value;
valid_data.name = "my_lns_name";
@ -1565,7 +1565,7 @@ 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))
{
valid_data.type = static_cast<uint16_t>(lns::mapping_type::lokinet);
valid_data.type = lns::mapping_type::lokinet;
// Lokinet domain name too long
{
cryptonote::tx_extra_loki_name_system data = valid_data;
@ -1605,7 +1605,7 @@ bool loki_name_system_invalid_tx_extra_params::generate(std::vector<test_event_e
// Session value invalid, session keys require a 33 byte key where the first byte is a 0x05
std::stringstream stream;
valid_data.type = static_cast<uint16_t>(lns::mapping_type::session);
valid_data.type = lns::mapping_type::session;
{
cryptonote::tx_extra_loki_name_system data = valid_data;
data.value[0] = 'a';
@ -1653,7 +1653,7 @@ bool loki_name_system_invalid_tx_extra_params::generate(std::vector<test_event_e
make_lns_tx_with_custom_extra(gen, events, miner, data, false, "(Session) User id, name too long");
}
uint16_t custom_type = 1111;
auto custom_type = static_cast<lns::mapping_type>(1111);
if (lns::mapping_type_allowed(gen.hardfork(), custom_type))
{
cryptonote::tx_extra_loki_name_system data = valid_data;
@ -1720,18 +1720,18 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
// 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, static_cast<uint16_t>(lns::mapping_type::session), miner_key.session_value, miner_session_name1);
cryptonote::transaction session_tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, miner_key.session_value, miner_session_name1);
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, static_cast<uint16_t>(lns::mapping_type::wallet), miner_key.wallet_value, miner_wallet_name1);
cryptonote::transaction wallet_tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::wallet, miner_key.wallet_value, miner_wallet_name1);
txs.push_back(wallet_tx);
}
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
cryptonote::transaction lokinet_tx = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::lokinet), miner_key.lokinet_value, miner_lokinet_domain1);
cryptonote::transaction lokinet_tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet, miner_key.lokinet_value, miner_lokinet_domain1);
txs.push_back(lokinet_tx);
}
gen.create_and_add_next_block(txs);
@ -1759,19 +1759,19 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
for (lns::mapping_record const &record : records)
{
if (record.type == static_cast<uint16_t>(lns::mapping_type::session))
if (record.type == lns::mapping_type::session)
{
CHECK_EQ(record.name, miner_session_name1);
CHECK_EQ(record.register_height, first_lns_height);
CHECK_EQ(record.value, miner_key.session_value);
}
else if (record.type == static_cast<uint16_t>(lns::mapping_type::lokinet))
else if (record.type == lns::mapping_type::lokinet)
{
CHECK_EQ(record.name, miner_lokinet_domain1);
CHECK_EQ(record.register_height, first_lns_height);
CHECK_EQ(record.value, miner_key.lokinet_value);
}
else if (record.type == static_cast<uint16_t>(lns::mapping_type::wallet))
else if (record.type == lns::mapping_type::wallet)
{
CHECK_EQ(record.name, miner_wallet_name1);
CHECK_EQ(record.register_height, first_lns_height);
@ -1795,11 +1795,11 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
std::string const bob_session_name1 = "I Like Session";
{
std::vector<cryptonote::transaction> txs;
txs.push_back(gen.create_and_add_loki_name_system_tx(bob, static_cast<uint16_t>(lns::mapping_type::session), bob_key.session_value, bob_session_name1));
txs.push_back(gen.create_and_add_loki_name_system_tx(bob, lns::mapping_type::session, bob_key.session_value, bob_session_name1));
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
txs.push_back(gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::lokinet), miner_key.lokinet_value, miner_lokinet_domain1));
txs.push_back(gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet, miner_key.lokinet_value, miner_lokinet_domain1));
}
gen.create_and_add_next_block(txs);
}
@ -1817,19 +1817,19 @@ 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.ed_key);
for (lns::mapping_record const &record : records)
{
if (record.type == static_cast<uint16_t>(lns::mapping_type::session))
if (record.type == lns::mapping_type::session)
{
CHECK_EQ(record.name, miner_session_name1);
CHECK_EQ(record.register_height, first_lns_height);
CHECK_EQ(record.value, miner_key.session_value);
}
else if (record.type == static_cast<uint16_t>(lns::mapping_type::lokinet))
else if (record.type == lns::mapping_type::lokinet)
{
CHECK_EQ(record.name, miner_lokinet_domain1);
CHECK_EQ(record.register_height, second_lns_height);
CHECK_EQ(record.value, miner_key.lokinet_value);
}
else if (record.type == static_cast<uint16_t>(lns::mapping_type::wallet))
else if (record.type == lns::mapping_type::wallet)
{
CHECK_EQ(record.name, miner_wallet_name1);
CHECK_EQ(record.register_height, first_lns_height);
@ -1849,7 +1849,7 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
CHECK_EQ(records[0].name, bob_session_name1);
CHECK_EQ(records[0].register_height, second_lns_height);
CHECK_EQ(records[0].value, bob_key.session_value);
CHECK_EQ(records[0].type, static_cast<uint16_t>(lns::mapping_type::session));
CHECK_EQ(records[0].type, lns::mapping_type::session);
}
return true;
@ -1886,19 +1886,19 @@ bool loki_name_system_large_reorg::generate(std::vector<test_event_entry> &event
for (lns::mapping_record const &record : records)
{
if (record.type == static_cast<uint16_t>(lns::mapping_type::session))
if (record.type == lns::mapping_type::session)
{
CHECK_EQ(record.name, miner_session_name1);
CHECK_EQ(record.register_height, first_lns_height);
CHECK_EQ(record.value, miner_key.session_value);
}
else if (record.type == static_cast<uint16_t>(lns::mapping_type::lokinet))
else if (record.type == lns::mapping_type::lokinet)
{
CHECK_EQ(record.name, miner_lokinet_domain1);
CHECK_EQ(record.register_height, first_lns_height);
CHECK_EQ(record.value, miner_key.lokinet_value);
}
else if (record.type == static_cast<uint16_t>(lns::mapping_type::wallet))
else if (record.type == lns::mapping_type::wallet)
{
CHECK_EQ(record.name, miner_wallet_name1);
CHECK_EQ(record.register_height, first_lns_height);
@ -1955,7 +1955,7 @@ 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";
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::lokinet), miner_key.lokinet_value, name);
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet, miner_key.lokinet_value, name);
gen.create_and_add_next_block({tx});
crypto::hash prev_txid = get_transaction_hash(tx);
@ -1974,9 +1974,9 @@ bool loki_name_system_name_renewal::generate(std::vector<test_event_entry> &even
CHECK_EQ(owner.id, 1);
CHECK_EQ(miner_key.ed_key, owner.key);
lns::mapping_record mappings = lns_db.get_mapping(static_cast<uint16_t>(lns::mapping_type::lokinet), name);
lns::mapping_record mappings = lns_db.get_mapping(lns::mapping_type::lokinet, name);
CHECK_EQ(mappings.loaded, true);
CHECK_EQ(mappings.type, static_cast<uint16_t>(lns::mapping_type::lokinet));
CHECK_EQ(mappings.type, lns::mapping_type::lokinet);
CHECK_EQ(mappings.name, name);
CHECK_EQ(mappings.value, miner_key.lokinet_value);
CHECK_EQ(mappings.register_height, height_of_lns_entry);
@ -1989,7 +1989,7 @@ bool loki_name_system_name_renewal::generate(std::vector<test_event_entry> &even
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, static_cast<uint16_t>(lns::mapping_type::lokinet), miner_key.lokinet_value, name);
cryptonote::transaction renew_tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet, miner_key.lokinet_value, name);
gen.create_and_add_next_block({renew_tx});
uint64_t renewal_height = gen.height();
@ -2003,9 +2003,9 @@ bool loki_name_system_name_renewal::generate(std::vector<test_event_entry> &even
CHECK_EQ(owner.id, 1);
CHECK_EQ(miner_key.ed_key, owner.key);
lns::mapping_record mappings = lns_db.get_mapping(static_cast<uint16_t>(lns::mapping_type::lokinet), name);
lns::mapping_record mappings = lns_db.get_mapping(lns::mapping_type::lokinet, name);
CHECK_EQ(mappings.loaded, true);
CHECK_EQ(mappings.type, static_cast<uint16_t>(lns::mapping_type::lokinet));
CHECK_EQ(mappings.type, lns::mapping_type::lokinet);
CHECK_EQ(mappings.name, name);
CHECK_EQ(mappings.value, miner_key.lokinet_value);
CHECK_EQ(mappings.register_height, renewal_height);
@ -2058,7 +2058,7 @@ bool loki_name_system_name_value_max_lengths::generate(std::vector<test_event_en
// Blockchain
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::wallet))
{
data.type = static_cast<uint16_t>(lns::mapping_type::wallet);
data.type = lns::mapping_type::wallet;
data.name = std::string(lns::WALLET_NAME_MAX, 'A');
data.value = miner_key.wallet_value;
make_lns_tx_with_custom_extra(gen, events, miner, data);
@ -2067,7 +2067,7 @@ 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))
{
data.type = static_cast<uint16_t>(lns::mapping_type::lokinet);
data.type = lns::mapping_type::lokinet;
data.name = "doyle.loki";
data.value = std::string(lns::LOKINET_ADDRESS_BINARY_LENGTH, 'a');
make_lns_tx_with_custom_extra(gen, events, miner, data);
@ -2075,7 +2075,7 @@ bool loki_name_system_name_value_max_lengths::generate(std::vector<test_event_en
// Session
{
data.type = static_cast<uint16_t>(lns::mapping_type::session);
data.type = lns::mapping_type::session;
data.name = std::string(lns::SESSION_DISPLAY_NAME_MAX, 'A');
data.value = std::string(lns::SESSION_PUBLIC_KEY_BINARY_LENGTH, 'a');
data.value[0] = '0';
@ -2086,7 +2086,7 @@ bool loki_name_system_name_value_max_lengths::generate(std::vector<test_event_en
// Generic
if (lns::mapping_type_allowed(gen.hardfork(), lns::mapping_type::lokinet))
{
data.type = 1111;
data.type = static_cast<lns::mapping_type>(1111);
data.name = std::string(lns::GENERIC_NAME_MAX, 'A');
data.value = std::string(lns::GENERIC_VALUE_MAX, 'a');
make_lns_tx_with_custom_extra(gen, events, miner, data);
@ -2095,6 +2095,197 @@ bool loki_name_system_name_value_max_lengths::generate(std::vector<test_event_en
return true;
}
bool loki_name_system_update_mapping_after_expiry_fails::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
cryptonote::account_base miner = gen.first_miner_;
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(10); /// generate some outputs and unlock them
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))
{
std::string const name = "mydomain.loki";
cryptonote::transaction tx = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::lokinet, miner_key.lokinet_value, name);
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::lokinet_expiry_blocks(cryptonote::FAKECHAIN, nullptr);
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, bob_key.lokinet_value, name);
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", [&events, height_of_lns_entry, miner_key, name](cryptonote::core &c, size_t ev_index)
{
DEFINE_TESTS_ERROR_CONTEXT("check_still_expired");
lns::name_system_db const &lns_db = c.get_blockchain_storage().name_system_db();
lns::owner_record owner = lns_db.get_owner_by_key(miner_key.ed_key);
CHECK_EQ(owner.loaded, true);
CHECK_EQ(owner.id, 1);
CHECK_EQ(miner_key.ed_key, owner.key);
lns::mapping_record mappings = lns_db.get_mapping(lns::mapping_type::lokinet, name);
CHECK_EQ(mappings.loaded, true);
CHECK_EQ(mappings.type, lns::mapping_type::lokinet);
CHECK_EQ(mappings.name, name);
CHECK_EQ(mappings.value, miner_key.lokinet_value);
CHECK_EQ(mappings.register_height, height_of_lns_entry);
CHECK_EQ(mappings.owner_id, owner.id);
return true;
});
}
return true;
}
bool loki_name_system_update_mapping::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(10); /// generate some outputs and unlock them
gen.add_mined_money_unlock_blocks();
cryptonote::account_base miner = gen.first_miner_;
cryptonote::account_base const bob = gen.add_account();
lns_keys_t miner_key = make_lns_keys(miner);
lns_keys_t bob_key = make_lns_keys(bob);
crypto::hash session_tx_hash1;
std::string session_name1 = "MyName";
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, miner_key.session_value, session_name1);
session_tx_hash1 = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
// Test update mapping with same name fails
{
crypto::hash session_tx_hash2;
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update(miner, lns::mapping_type::session, miner_key.session_value, session_name1);
gen.add_tx(tx1, false /*can_be_added_to_blockchain*/, "Can not add a LNS TX that re-updates the underlying value to same value");
}
crypto::hash session_tx_hash2;
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, bob_key.session_value, session_name1);
session_tx_hash2 = cryptonote::get_transaction_hash(tx1);
gen.create_and_add_next_block({tx1});
}
uint64_t session_height2 = gen.height();
loki_register_callback(events, "check_updated", [&events, miner_key, bob_key, session_height2, session_name1, session_tx_hash1, session_tx_hash2](cryptonote::core &c, size_t ev_index)
{
DEFINE_TESTS_ERROR_CONTEXT("check_updated");
lns::name_system_db const &lns_db = c.get_blockchain_storage().name_system_db();
std::vector<lns::mapping_record> records = lns_db.get_mappings({static_cast<uint16_t>(lns::mapping_type::session)}, session_name1);
CHECK_EQ(records.size(), 1);
CHECK_EQ(records[0].type, lns::mapping_type::session);
CHECK_EQ(records[0].name, session_name1);
CHECK_EQ(records[0].value, bob_key.session_value);
CHECK_EQ(records[0].register_height, session_height2);
CHECK_EQ(records[0].prev_txid, session_tx_hash1);
CHECK_EQ(records[0].txid, session_tx_hash2);
CHECK_EQ(records[0].owner, miner_key.ed_key);
return true;
});
return true;
}
bool loki_name_system_update_mapping_non_existent_name_fails::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(5); /// generate some outputs and unlock them
gen.add_mined_money_unlock_blocks();
cryptonote::account_base miner = gen.first_miner_;
lns_keys_t miner_key = make_lns_keys(miner);
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update(miner, lns::mapping_type::session, miner_key.session_value, "Hello World", 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;
}
bool loki_name_system_update_mapping_invalid_signature::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(5); /// generate some outputs and unlock them
gen.add_mined_money_unlock_blocks();
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, miner_key.session_value, name);
gen.create_and_add_next_block({tx1});
lns_keys_t bob_key = make_lns_keys(gen.add_account());
crypto::ed25519_signature invalid_signature = {};
cryptonote::transaction tx2 = gen.create_loki_name_system_tx_update(miner, lns::mapping_type::session, bob_key.session_value, name, &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;
}
bool loki_name_system_update_mapping_replay::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
loki_chain_generator gen(events, hard_forks);
gen.add_blocks_until_version(hard_forks.back().first);
gen.add_n_blocks(5); /// generate some outputs and unlock them
gen.add_mined_money_unlock_blocks();
cryptonote::account_base miner = gen.first_miner_;
lns_keys_t miner_key = make_lns_keys(miner);
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";
// Make LNS Mapping
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(miner, lns::mapping_type::session, miner_key.session_value, name);
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, bob_key.session_value, name);
gen.create_and_add_next_block({tx1});
assert(cryptonote::get_loki_name_system_from_tx_extra(tx1.extra, lns_entry));
}
// 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);
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
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx_update(miner, lns::mapping_type::session, alice_key.session_value, name);
gen.create_and_add_next_block({tx1});
}
// 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.
{
cryptonote::transaction tx1 = gen.create_loki_name_system_tx_update_w_extra(miner, lns_entry);
gen.add_tx(tx1, false /*can_be_added_to_blockchain*/, "Can not replay an older update mapping, should fail signature verification");
}
return true;
}
bool loki_name_system_wrong_burn::generate(std::vector<test_event_entry> &events)
{
std::vector<std::pair<uint8_t, uint64_t>> hard_forks = loki_generate_sequential_hard_fork_table();
@ -2145,7 +2336,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, static_cast<uint16_t>(type), value, name, nullptr /*owner*/, burn);
cryptonote::transaction tx = gen.create_loki_name_system_tx(miner, type, value, name, nullptr /*owner*/, burn);
gen.add_tx(tx, false /*can_be_added_to_blockchain*/, "Wrong burn for a LNS tx", false /*kept_by_block*/);
}
}
@ -2167,7 +2358,7 @@ bool loki_name_system_wrong_version::generate(std::vector<test_event_entry> &eve
cryptonote::tx_extra_loki_name_system data = {};
data.version = 0xFF;
data.owner = miner_key.ed_key;
data.type = static_cast<uint16_t>(lns::mapping_type::session);
data.type = lns::mapping_type::session;
data.value = miner_key.session_value;
data.name = "my_lns_name";

View File

@ -62,6 +62,11 @@ struct loki_name_system_invalid_tx_extra_params
struct loki_name_system_large_reorg : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_name_system_name_renewal : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_name_system_name_value_max_lengths : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_name_system_update_mapping_after_expiry_fails : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_name_system_update_mapping : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_name_system_update_mapping_non_existent_name_fails : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_name_system_update_mapping_invalid_signature : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_name_system_update_mapping_replay : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_name_system_wrong_burn : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_name_system_wrong_version : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };
struct loki_service_nodes_alt_quorums : public test_chain_unit_base { bool generate(std::vector<test_event_entry>& events); };

View File

@ -8,7 +8,7 @@ TEST(loki_name_system, lokinet_domain_names)
char domain_edkeys[lns::LOKINET_ADDRESS_BINARY_LENGTH * 2] = {};
memset(domain_edkeys, 'a', sizeof(domain_edkeys));
uint16_t const lokinet = static_cast<uint16_t>(lns::mapping_type::lokinet);
lns::mapping_type const lokinet = lns::mapping_type::lokinet;
// Should work
{