Accept wallet address for LNS buy/querying for type safety

We still pull out the spend public key, in a future coming PR we will
take the entire wallet address as to improve the usability of LNS.
Said PR will also accept subaddresses which should work out of the box
as long as we use the correct secret key of said subaddress to generate
the required signature.
This commit is contained in:
Doyle 2020-03-12 11:48:06 +11:00
parent c5b8988057
commit 558a5d53c3
7 changed files with 42 additions and 32 deletions

View File

@ -7,6 +7,7 @@
#include "common/base32z.h"
#include "crypto/hash.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "cryptonote_basic/tx_extra.h"
@ -401,6 +402,34 @@ crypto::generic_public_key make_ed25519_public_key(crypto::ed25519_public_key co
return result;
}
bool parse_owner_to_generic_key(cryptonote::network_type nettype, std::string const &owner, crypto::generic_public_key &key, std::string *reason)
{
cryptonote::address_parse_info parsed_addr;
crypto::ed25519_public_key ed_key;
if (cryptonote::get_account_address_from_str(parsed_addr, nettype, owner))
{
key = lns::make_monero_public_key(parsed_addr.address.m_spend_public_key);
}
else if (epee::string_tools::hex_to_pod(owner, key))
{
key = lns::make_ed25519_public_key(ed_key);
}
else
{
if (reason)
{
char const *type_heuristic =
(owner.size() == sizeof(crypto::ed25519_public_key) * 2) ? "ED25519 Key" : "Wallet address";
*reason = type_heuristic;
*reason += " provided could not be parsed owner=";
*reason += key;
}
return false;
}
return true;
}
static bool char_is_num(char c)
{
bool result = c >= '0' && c <= '9';

View File

@ -71,6 +71,7 @@ crypto::generic_signature make_monero_signature(crypto::hash const &hash, crypt
crypto::generic_signature make_ed25519_signature(crypto::hash const &hash, crypto::ed25519_secret_key const &skey);
crypto::generic_public_key make_monero_public_key(crypto::public_key const &pkey);
crypto::generic_public_key make_ed25519_public_key(crypto::ed25519_public_key const &pkey);
bool parse_owner_to_generic_key(cryptonote::network_type nettype, std::string const &owner, crypto::generic_public_key &key, std::string *reason);
crypto::hash tx_extra_signature_hash(epee::span<const uint8_t> value, crypto::generic_public_key const *owner, crypto::generic_public_key const *backup_owner, crypto::hash const &prev_txid);
// Validate a human readable mapping value representation in 'value' and write the binary form into 'blob'.

View File

@ -3420,20 +3420,17 @@ namespace cryptonote
if (exceeds_quantity_limit(ctx, error_resp, m_restricted, req.entries.size(), COMMAND_RPC_LNS_OWNERS_TO_NAMES::MAX_REQUEST_ENTRIES))
return false;
// TODO(doyle): Currently we assume monero keys, make code handle wallet addresses + ed keys to detect the type of key.
std::map<crypto::generic_public_key, size_t> key_to_request_index;
std::vector<crypto::generic_public_key> keys;
keys.reserve(req.entries.size());
for (size_t request_index = 0; request_index < req.entries.size(); request_index++)
{
std::string const &owner = req.entries[request_index];
std::string const &owner = req.entries[request_index];
crypto::generic_public_key pkey = {};
pkey.type = crypto::generic_key_sig_type::monero;
if (!epee::string_tools::hex_to_pod(owner, pkey))
if (!lns::parse_owner_to_generic_key(m_core.get_nettype(), owner, pkey, &error_resp.message))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
error_resp.message = "Public key=" + owner + ", could not be converted to a ed25519 key, expected 32 char hex string";
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
return false;
}

View File

@ -271,13 +271,12 @@ namespace
const char* USAGE_REQUEST_STAKE_UNLOCK("request_stake_unlock <service_node_pubkey>");
const char* USAGE_PRINT_LOCKED_STAKES("print_locked_stakes");
const char* USAGE_LNS_BUY_MAPPING("lns_buy_mapping [index=<N1>[,<N2>,...]] [<priority>] [owner=<pub_key>] [backup_owner=<pub_key>] \"<name>\" <value>");
const char* USAGE_LNS_UPDATE_MAPPING(
"lns_update_mapping [index=<N1>[,<N2>,...]] [<priority>] [owner=<pub_key>] [backup_owner=<pub_key>] [value=<lns_value>] [signature=<hex_signature>] \"<name>\"");
const char* USAGE_LNS_BUY_MAPPING("lns_buy_mapping [index=<N1>[,<N2>,...]] [<priority>] [owner=<value>] [backup_owner=<value>] \"<name>\" <value>");
const char* USAGE_LNS_UPDATE_MAPPING("lns_update_mapping [index=<N1>[,<N2>,...]] [<priority>] [owner=<value>] [backup_owner=<value>] [value=<lns_value>] [signature=<hex_signature>] \"<name>\"");
// TODO(loki): Currently defaults to session, in future allow specifying Lokinet and Wallet when they are enabled
const char* USAGE_LNS_MAKE_UPDATE_MAPPING_SIGNATURE("lns_make_update_mapping_signature [owner=<pub_key>] [backup_owner=<pub_key>] [value=<lns_value>] \"<name>\"");
const char* USAGE_LNS_PRINT_OWNERS_TO_NAMES("lns_print_owners_to_names [<64 hex character ed25519 public key>]");
const char* USAGE_LNS_MAKE_UPDATE_MAPPING_SIGNATURE("lns_make_update_mapping_signature [owner=<value>] [backup_owner=<value>] [value=<lns_value>] \"<name>\"");
const char* USAGE_LNS_PRINT_OWNERS_TO_NAMES("lns_print_owners_to_names [<owner>, ...]");
const char* USAGE_LNS_PRINT_NAME_TO_OWNERS("lns_print_name_to_owners [type=<N1|all>[,<N2>...]] \"name\"");
#if defined (LOKI_ENABLE_INTEGRATION_TEST_HOOKS)
@ -6658,7 +6657,7 @@ bool simple_wallet::lns_print_owners_to_names(const std::vector<std::string>& ar
cryptonote::COMMAND_RPC_LNS_OWNERS_TO_NAMES::request request = {};
if (args.size() == 0)
{
my_key = epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key);
my_key = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0});
request.entries.push_back(my_key);
}
else

View File

@ -8473,26 +8473,11 @@ static lns_prepared_args prepare_tx_extra_loki_name_system_values(wallet2 const
}
}
if (owner)
{
if (!epee::string_tools::hex_to_pod(*owner, result.owner))
{
if (reason) *reason = "Hex owner key provided failed to convert to public_key, owner=" + *owner;
if (owner && !lns::parse_owner_to_generic_key(wallet.nettype(), *owner, result.owner, reason))
return {};
}
}
if (backup_owner)
{
if (!epee::string_tools::hex_to_pod(*backup_owner, result.backup_owner))
{
if (reason) *reason = "Hex backup_owner key provided failed to convert to public_key, backup_owner=" + *backup_owner;
if (backup_owner && !lns::parse_owner_to_generic_key(wallet.nettype(), *backup_owner, result.backup_owner, reason))
return {};
}
}
if (!owner && !backup_owner)
result.owner = lns::make_monero_public_key(wallet.get_account().get_keys().m_account_address.m_spend_public_key);
{
cryptonote::COMMAND_RPC_LNS_NAMES_TO_OWNERS::request request = {};

View File

@ -2857,7 +2857,7 @@ namespace wallet_rpc
struct COMMAND_RPC_LNS_BUY_MAPPING
{
static constexpr const char *description =
R"(Buy a Loki Name System mapping. Loki Name System allows multiple owners that are authorised to update the underlying mapping. By default if no owner is specified, the purchasing wallet's public spend key is set as the owner.
R"(Buy a Loki Name System mapping. Loki Name System allows multiple owners that are authorised to update the underlying mapping. An owner can be either a ed25519 public key or a wallet address. By default if no owner is specified, the purchasing wallet's public spend key is used from the wallet as the owner.
- For Session, the recommended owner is the ed25519 public key of the user's Session ID set to owner
In future, support for mappings on Blockchain wallets and Lokinet addresses will be available. The recommended owner is the monero ed25519 public key of the user's wallet spend key set to owner.
@ -2929,7 +2929,7 @@ For information on updating & signing, refer to COMMAND_RPC_LNS_UPDATE_MAPPING)"
struct COMMAND_RPC_LNS_UPDATE_MAPPING
{
static constexpr const char *description =
R"(Update a Loki Name System mapping's underlying value. The owner (public key) of the mapping must be able to validate.
R"(Update a Loki Name System mapping's underlying value. The owner (wallet address/ed25519 public key) of the mapping must be able to validate.
The signature is generated from signing a hash generated by using Libsodium's generichash on the {prev_txid field (in the current mapping to update), value (new value to update to)} in binary.
Depending on the owner's public key(s) associated with the mapping the signature can sign the hash in 2 ways. If the owner public key associated with the mapping is-

View File

@ -603,7 +603,6 @@ cryptonote::transaction loki_chain_generator::create_loki_name_system_tx_update(
crypto::generic_signature signature_ = {};
if (!signature)
{
// TODO(doyle): We need to detct the key types, this will be possible to detect when we pass in the wallet address as the owner
signature = &signature_;
crypto::hash hash = lns::tx_extra_signature_hash(encrypted_value.to_span(), owner, backup_owner, prev_txid);
*signature = lns::make_monero_signature(hash, src.get_keys().m_account_address.m_spend_public_key, src.get_keys().m_spend_secret_key);