Remove owner signature from loki name service tx extra

We want to allow people to buy LNS entries on behalf of other users. If
this is the case we don't need signatures to verify that the purchaser
knows the secret key. What we actually want in this scenario is that,
there's a LNS entry, and people can voluntarily pay to renew/buy that.
This commit is contained in:
Doyle 2020-01-21 15:30:17 +11:00
parent a821bad551
commit 0b3df6c5ad
12 changed files with 68 additions and 89 deletions

View File

@ -913,40 +913,6 @@ namespace cryptonote
return winner.m_service_node_key;
}
//---------------------------------------------------------------
crypto::hash tx_extra_loki_name_system::make_signature_hash() const
{
char buf[sizeof(owner.data) + sizeof(type) + lns::GENERIC_NAME_MAX + lns::GENERIC_VALUE_MAX] = {};
char *buf_ptr = buf;
memcpy(buf_ptr, owner.data, sizeof(owner));
buf_ptr += sizeof(owner);
uint16_t type_le = boost::endian::native_to_little(type);
memcpy(buf_ptr, &type_le, sizeof(type_le));
buf_ptr += sizeof(type_le);
size_t bytes_to_copy = std::min(lns::GENERIC_NAME_MAX, name.size());
memcpy(buf_ptr, name.data(), bytes_to_copy);
buf_ptr += bytes_to_copy;
bytes_to_copy = std::min(lns::GENERIC_VALUE_MAX, value.size());
memcpy(buf_ptr, value.data(), bytes_to_copy);
buf_ptr += bytes_to_copy;
size_t buf_size = buf_ptr - buf;
crypto::hash result;
crypto::cn_fast_hash(buf, buf_size, result);
return result;
}
//---------------------------------------------------------------
crypto::ed25519_signature tx_extra_loki_name_system::make_signature(crypto::ed25519_secret_key const &key) const
{
crypto::hash hash = make_signature_hash();
crypto::ed25519_signature result = {};
crypto_sign_ed25519_detached(result.data, nullptr, reinterpret_cast<const unsigned char *>(hash.data), sizeof(hash.data), key.data);
return result;
}
//---------------------------------------------------------------
bool get_loki_name_system_from_tx_extra(const std::vector<uint8_t> &tx_extra, tx_extra_loki_name_system &entry)
{
std::vector<tx_extra_field> tx_extra_fields;

View File

@ -386,16 +386,12 @@ namespace cryptonote
uint16_t type;
std::string name;
std::string value;
crypto::ed25519_signature signature;
crypto::hash make_signature_hash() const;
crypto::ed25519_signature make_signature(crypto::ed25519_secret_key const &key) const;
BEGIN_SERIALIZE()
FIELD(owner);
FIELD(type);
FIELD(name);
FIELD(value);
FIELD(signature);
END_SERIALIZE()
};

View File

@ -3502,7 +3502,12 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
if (requester.id != owner.id)
{
MERROR_VER("TX: " << tx.type << " " << get_transaction_hash(tx) << ", owner = " << data.owner
<< ", type = " << (int)data.type << ", name = " << data.name << " actual owner user_id = "
<< mapping.user_id << ", does not match requester's id = " << requester.id);
return false;
}
}
else
{

View File

@ -341,14 +341,6 @@ bool validate_lns_tx(cryptonote::network_type nettype, cryptonote::transaction c
return false;
}
// TODO: Validate burn amount in the tx_extra
crypto::hash hash = entry->make_signature_hash();
if (crypto_sign_ed25519_verify_detached(entry->signature.data, reinterpret_cast<const unsigned char *>(hash.data), sizeof(hash.data), entry->owner.data) != 0)
{
LOG_PRINT_L1("LNS TX " << cryptonote::get_transaction_hash(tx) << " Failed signature validation");
return false;
}
return true;
}

View File

@ -262,7 +262,7 @@ namespace
const char* USAGE_STAKE("stake [index=<N1>[,<N2>,...]] [<priority>] <service node pubkey> <amount|percent%>");
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>] [blockchain|lokinet|messenger|<custom_type_as_number>] \"<name>\" <value>");
const char* USAGE_BUY_LNS_MAPPING("buy_lns_mapping [index=<N1>[,<N2>,...]] [<priority>] [owner] (blockchain|lokinet|messenger|<custom_type_as_number>) \"<name>\" <value>");
#if defined (LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
std::string input_line(const std::string &prompt, bool yesno = false)
@ -3210,7 +3210,7 @@ simple_wallet::simple_wallet()
tr(USAGE_PRINT_LOCKED_STAKES),
tr("Print stakes currently locked on the Service Node network"));
std::stringstream stream;
stream << "Buy a Loki Name Service mapping. You are able to purchase the following 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` is a ED25519 public key, derived from the wallets spend key. You are able to purchase the following mappings\n\n";
stream << "Blockchain: (max: " << lns::BLOCKCHAIN_NAME_MAX << " bytes) map a human readable name to a wallet address\n";
stream << "Lokinet: (max: " << lns::LOKINET_DOMAIN_NAME_MAX << " bytes) map a human readable domain name to a <public_key>.loki address on Lokinet\n";
@ -6513,7 +6513,14 @@ bool simple_wallet::buy_lns_mapping(const std::vector<std::string>& args)
std::set<uint32_t> subaddr_indices = {};
if (!parse_subaddr_indices_and_priority(*m_wallet, local_args, subaddr_indices, priority)) return false;
if (args.size() < 3)
std::string owner;
if (bool has_owner = (local_args.size() == 4))
{
owner = local_args[0];
local_args.erase(local_args.begin());
}
if (local_args.size() < 3)
{
PRINT_USAGE(USAGE_BUY_LNS_MAPPING);
return true;
@ -6558,7 +6565,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(type, name, value, &reason, priority, m_current_subaddress_account, subaddr_indices);
ptx_vector = m_wallet->create_buy_lns_mapping_tx(type, owner, name, value, &reason, priority, m_current_subaddress_account, subaddr_indices);
if (ptx_vector.empty())
{
tools::fail_msg_writer() << reason;

View File

@ -8544,6 +8544,7 @@ wallet2::request_stake_unlock_result wallet2::can_request_stake_unlock(const cry
}
std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(uint16_t type,
std::string const &owner,
std::string const &name,
std::string const &value,
std::string *reason,
@ -8564,26 +8565,26 @@ std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(uint16_t typ
return {};
}
// TODO(loki): The wallet does something funky with the key storage that the
// values have changed, even after decrypting the wallet keys. The ed keys are
// different from when we originally derived them, so for now, just re-derive
// them every-time.
crypto::ed25519_public_key pkey;
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.data));
if (owner.size())
{
if (!epee::string_tools::hex_to_pod(owner, pkey))
{
if (reason) *reason = "Failed to convert owner to a ed25519 key, owner = " + owner;
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.data));
}
tx_extra_loki_name_system entry = {};
entry.owner = pkey;
entry.type = type;
entry.name = name;
entry.value = value;
entry.signature = entry.make_signature(skey);
crypto::hash hash = entry.make_signature_hash();
assert(crypto_sign_ed25519_verify_detached(entry.signature.data,
reinterpret_cast<const unsigned char *>(hash.data),
sizeof(hash.data),
entry.owner.data) == 0);
std::vector<uint8_t> extra;
add_loki_name_system_to_tx_extra(extra, entry);
@ -8599,6 +8600,7 @@ std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(uint16_t typ
}
std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(std::string const &type,
std::string const &owner,
std::string const &name,
std::string const &value,
std::string *reason,
@ -8610,7 +8612,7 @@ std::vector<wallet2::pending_tx> wallet2::create_buy_lns_mapping_tx(std::string
if (!lns::validate_mapping_type(type, &mapping_type, reason))
return {};
std::vector<wallet2::pending_tx> result = create_buy_lns_mapping_tx(mapping_type, name, value, reason, priority, account_index, subaddr_indices);
std::vector<wallet2::pending_tx> result = create_buy_lns_mapping_tx(mapping_type, owner, name, value, reason, priority, account_index, subaddr_indices);
return result;
}

View File

@ -1541,8 +1541,8 @@ 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 &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 &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(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(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 = {});
void freeze(size_t idx);
void thaw(size_t idx);

View File

@ -4265,7 +4265,7 @@ namespace tools
}
std::string reason;
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_buy_lns_mapping_tx(req.type, req.name, req.value, &reason, req.priority, req.account_index, req.subaddr_indices);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_buy_lns_mapping_tx(req.type, req.owner, req.name, req.value, &reason, req.priority, req.account_index, req.subaddr_indices);
if (ptx_vector.empty())
{
er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE;

View File

@ -2863,14 +2863,15 @@ namespace wallet_rpc
};
LOKI_RPC_DOC_INTROSPECT
// Purchase a Loki Name Service mapping
// Buy a Loki Name Service mapping. Specifying `owner` is optional and defaults to the purchasing wallet if empty or not specified. The `owner` is a ED25519 public key, derived from the wallets spend key.
struct COMMAND_RPC_BUY_LNS_MAPPING
{
struct request_t
{
std::string type; // The mapping type, either "blockchain", "lokinet", "messenger" or if custom, a value between 0-65536 (0-2 map to the predefined mappings listed earlier).
std::string owner; // (Optional): The owner of the mapping (wallet spend key as ed25519 public key in hex). For Lokinet, the owner has the ability to renew the Loki Name Service entry. By default/if field is empty, it is derived from the wallet purchasing the LNS mapping.
std::string name; // The name to purchase via Loki Name Service
std::string value; // The value that the name maps to via Loki Name Service
std::string value; // The value that the name maps to via Loki Name Service, (i.e. For wallets: name -> wallet address. For messenger: display name -> messenger public key. For Lokinet: name -> domain name).
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)
@ -2881,6 +2882,10 @@ namespace wallet_rpc
bool get_tx_metadata; // Return the metadata needed to relay the transaction. (Defaults to false)
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE (type);
KV_SERIALIZE_OPT(owner, std::string(""));
KV_SERIALIZE (name);
KV_SERIALIZE (value);
KV_SERIALIZE_OPT(account_index, (uint32_t)0);
KV_SERIALIZE_OPT(subaddr_indices, {});
KV_SERIALIZE_OPT(priority, (uint32_t)0);

View File

@ -300,9 +300,10 @@ loki_chain_generator::create_and_add_loki_name_system_tx(cryptonote::account_bas
void const *value,
size_t value_len,
std::string const &name,
crypto::ed25519_public_key const *owner,
bool kept_by_block)
{
cryptonote::transaction t = create_loki_name_system_tx(src, type, value, value_len, name);
cryptonote::transaction t = create_loki_name_system_tx(src, type, value, value_len, name, owner);
add_tx(t, true /*can_be_added_to_blockchain*/, ""/*fail_msg*/, kept_by_block);
return t;
}
@ -517,11 +518,19 @@ cryptonote::transaction loki_chain_generator::create_loki_name_system_tx(crypton
uint16_t type,
void const *value,
size_t value_len,
std::string const &name) const
std::string const &name,
crypto::ed25519_public_key const *owner) const
{
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.data));
if (owner)
{
pkey = *owner;
}
else
{
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.data));
}
std::vector<uint8_t> extra;
cryptonote::tx_extra_loki_name_system data = {};
@ -529,7 +538,6 @@ cryptonote::transaction loki_chain_generator::create_loki_name_system_tx(crypton
data.type = type;
data.value = std::string(reinterpret_cast<char const *>(value), value_len);
data.name = name;
data.signature = data.make_signature(skey);
cryptonote::add_loki_name_system_to_tx_extra(extra, data);
cryptonote::add_burned_amount_to_tx_extra(extra, lns::BURN_REQUIREMENT);

View File

@ -1420,7 +1420,7 @@ 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, void const *value, size_t value_len, std::string const &name, bool kept_by_block = false);
cryptonote::transaction create_and_add_loki_name_system_tx(cryptonote::account_base const &src, uint16_t type, void const *value, size_t value_len, std::string const &name, crypto::ed25519_public_key const *owner = nullptr, 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);
@ -1438,7 +1438,7 @@ struct loki_chain_generator
cryptonote::transaction create_staking_tx (const crypto::public_key& pub_key, const cryptonote::account_base &src, uint64_t amount) const;
cryptonote::transaction create_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) const;
cryptonote::checkpoint_t create_service_node_checkpoint(uint64_t block_height, size_t num_votes) const;
cryptonote::transaction create_loki_name_system_tx (cryptonote::account_base const &src, uint16_t type, void const *value, size_t value_len, std::string const &name) const;
cryptonote::transaction create_loki_name_system_tx (cryptonote::account_base const &src, uint16_t type, void const *value, size_t value_len, std::string const &name, crypto::ed25519_public_key const *owner = nullptr) 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

@ -1141,7 +1141,7 @@ bool loki_name_system_get_mappings_by_user::generate(std::vector<test_event_entr
messenger_key[0] = 5;
memcpy(messenger_key + 1, bob_key.data, sizeof(bob_key));
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, static_cast<uint16_t>(lns::mapping_type::messenger), messenger_key, sizeof(messenger_key), messenger_name1);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::messenger), messenger_key, sizeof(messenger_key), messenger_name2);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::messenger), messenger_key, sizeof(messenger_key), messenger_name2, &bob_key);
gen.create_and_add_next_block({tx1, tx2});
}
uint64_t messenger_height = gen.height();
@ -1160,7 +1160,7 @@ bool loki_name_system_get_mappings_by_user::generate(std::vector<test_event_entr
std::string lokinet_name2 = "ipsum.loki";
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, static_cast<uint16_t>(lns::mapping_type::lokinet), bob_key.data, sizeof(bob_key), lokinet_name1);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::lokinet), bob_key.data, sizeof(bob_key), lokinet_name2);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::lokinet), bob_key.data, sizeof(bob_key), lokinet_name2, &bob_key);
gen.create_and_add_next_block({tx1, tx2});
}
uint64_t lokinet_height = gen.height();
@ -1171,7 +1171,7 @@ bool loki_name_system_get_mappings_by_user::generate(std::vector<test_event_entr
std::string wallet_name2 = "Wallet2";
{
cryptonote::transaction tx1 = gen.create_and_add_loki_name_system_tx(bob, static_cast<uint16_t>(lns::mapping_type::blockchain), bob_addr.data(), bob_addr.size(), wallet_name1);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::blockchain), bob_addr.data(), bob_addr.size(), wallet_name2);
cryptonote::transaction tx2 = gen.create_and_add_loki_name_system_tx(miner, static_cast<uint16_t>(lns::mapping_type::blockchain), bob_addr.data(), bob_addr.size(), wallet_name2, &bob_key);
gen.create_and_add_next_block({tx1, tx2});
}
uint64_t wallet_height = gen.height();
@ -1198,19 +1198,19 @@ bool loki_name_system_get_mappings_by_user::generate(std::vector<test_event_entr
std::vector<lns::mapping_record> records = lns_db.get_mappings_by_user(bob_pkey);
CHECK_EQ(records.size(), 6);
CHECK_EQ(records[0].name, wallet_name2); // NOTE: Sorted order, ties dealt by name sorted order
CHECK_EQ(records[1].name, wallet_name1);
CHECK_EQ(records[0].name, messenger_name1);
CHECK_EQ(records[1].name, messenger_name2);
CHECK_EQ(records[2].name, lokinet_name1);
CHECK_EQ(records[3].name, lokinet_name2);
CHECK_EQ(records[4].name, messenger_name1);
CHECK_EQ(records[5].name, messenger_name2);
CHECK_EQ(records[4].name, wallet_name1);
CHECK_EQ(records[5].name, wallet_name2);
CHECK_EQ(records[0].register_height, wallet_height);
CHECK_EQ(records[1].register_height, wallet_height);
CHECK_EQ(records[0].register_height, messenger_height);
CHECK_EQ(records[1].register_height, messenger_height);
CHECK_EQ(records[2].register_height, lokinet_height);
CHECK_EQ(records[3].register_height, lokinet_height);
CHECK_EQ(records[4].register_height, messenger_height);
CHECK_EQ(records[5].register_height, messenger_height);
CHECK_EQ(records[4].register_height, wallet_height);
CHECK_EQ(records[5].register_height, wallet_height);
auto messenger_value_str = std::string(messenger_key, sizeof(messenger_key));
auto lokinet_value_str = std::string((char *)bob_key.data, sizeof(bob_key));
@ -1665,7 +1665,6 @@ bool loki_name_system_invalid_tx_extra_params::generate(std::vector<test_event_e
cryptonote::tx_extra_loki_name_system &data,
bool valid,
char const *reason) -> void {
data.signature = data.make_signature(miner_skey);
std::vector<uint8_t> extra;
cryptonote::add_loki_name_system_to_tx_extra(extra, data);
cryptonote::add_burned_amount_to_tx_extra(extra, lns::BURN_REQUIREMENT);
@ -1963,7 +1962,6 @@ bool loki_name_system_name_value_max_lengths::generate(std::vector<test_event_en
cryptonote::account_base const &src,
cryptonote::tx_extra_loki_name_system &data) -> void {
data.signature = data.make_signature(skey);
std::vector<uint8_t> extra;
cryptonote::add_loki_name_system_to_tx_extra(extra, data);
cryptonote::add_burned_amount_to_tx_extra(extra, lns::BURN_REQUIREMENT);