Move RPC for ons resolve address into ons resolve and decrypt wallet side

This commit is contained in:
Sean Darcy 2021-04-09 15:48:38 +10:00
parent 52ecd26b0d
commit 3030277b6a
14 changed files with 115 additions and 103 deletions

View File

@ -1911,32 +1911,6 @@ namespace cryptonote
return m_blockchain_storage.get_total_transactions();
}
//-----------------------------------------------------------------------------------------------
bool core::get_account_address_from_str_or_ons(
cryptonote::address_parse_info& info
, cryptonote::network_type nettype
, std::string str,
uint64_t height
)
{
bool result = false;
if (cryptonote::get_account_address_from_str(info, nettype, str))
{
result = true;
} else {
std::string name = tools::lowercase_ascii_string(std::move(str));
std::string reason;
ons::name_system_db& db = m_blockchain_storage.name_system_db();
if (ons::validate_ons_name(ons::mapping_type::wallet, str, &reason) && db.get_wallet_mapping(name, height, info))
{
result = true;
LOG_PRINT_L2("Resolved ONS name: "<< str << " to address: " << get_account_address_as_str(nettype, info.is_subaddress, info.address));
} else {
LOG_PRINT_L2("Invalid address format, could not resolve " << str);
}
}
return result;
}
//-----------------------------------------------------------------------------------------------
bool core::relay_txpool_transactions()
{
// we attempt to relay txes that should be relayed, but were not

View File

@ -1042,13 +1042,6 @@ namespace cryptonote
*/
const fs::path& get_config_directory() const { return m_config_folder; }
/**
* @brief checks if an address is valid, can accept an address or a oxen name service wallet mapping
*
* @return true if valid address
*/
bool get_account_address_from_str_or_ons( address_parse_info& info , network_type nettype , std::string str_or_url, uint64_t height);
private:
/**

View File

@ -869,6 +869,31 @@ bool validate_ons_name(mapping_type type, std::string name, std::string *reason)
return true;
}
std::optional<cryptonote::address_parse_info> encrypted_wallet_value_to_info(std::string name, std::string encrypted_value, std::string nonce)
{
std::string lower_name = tools::lowercase_ascii_string(std::move(name));
mapping_value record(oxenmq::from_hex(encrypted_value), oxenmq::from_hex(nonce));
record.decrypt(lower_name, mapping_type::wallet);
auto iter = std::next(record.buffer.begin(),1);
cryptonote::address_parse_info addr_info{0};
addr_info.has_payment_id = false;
addr_info.is_subaddress = false;
std::memcpy(&addr_info.address.m_spend_public_key.data, &*iter, 32);
std::advance(iter,32);
std::memcpy(&addr_info.address.m_view_public_key.data, &*iter, 32);
if (record.buffer[0] == 0x2) {
std::advance(iter,32);
std::copy_n(iter,8,addr_info.payment_id.data);
addr_info.has_payment_id = true;
} else if (record.buffer[0] == 0x1) {
addr_info.is_subaddress = true;
}
return addr_info;
}
static bool check_lengths(mapping_type type, std::string_view value, size_t max, bool binary_val, std::string *reason)
{
bool result;
@ -1032,6 +1057,17 @@ bool mapping_value::validate_encrypted(mapping_type type, std::string_view value
return true;
}
mapping_value::mapping_value(std::string encrypted_value, std::string nonce): buffer{0}
{
auto it = std::copy(encrypted_value.begin(), encrypted_value.end(), buffer.begin());
std::copy(nonce.begin(), nonce.end(), it);
len = encrypted_value.size() + nonce.size();
encrypted = true;
}
mapping_value::mapping_value(){}
std::string name_hash_bytes_to_base64(std::string_view bytes)
{
if (bytes.size() != NAME_HASH_SIZE)
@ -2223,6 +2259,8 @@ bool name_system_db::get_wallet_mapping(std::string str, uint64_t blockchain_hei
return false;
}
mapping_record name_system_db::get_mapping(mapping_type type, std::string_view name_base64_hash, std::optional<uint64_t> blockchain_height)
{
assert(name_base64_hash.size() == 44 && name_base64_hash.back() == '=' && oxenmq::is_base64(name_base64_hash));

View File

@ -105,6 +105,9 @@ struct mapping_value
// blob: (optional) if function returns true then the value will be loaded into the given
// mapping_value, ready for decryption via decrypt().
static bool validate_encrypted(mapping_type type, std::string_view value, mapping_value *blob = nullptr, std::string *reason = nullptr);
mapping_value();
mapping_value(std::string encrypted_value, std::string nonce);
};
inline std::ostream &operator<<(std::ostream &os, mapping_value const &v) { return os << oxenmq::to_hex(v.to_view()); }
@ -165,6 +168,8 @@ std::optional<std::string> name_hash_input_to_base64(std::string_view input);
bool validate_ons_name(mapping_type type, std::string name, std::string *reason = nullptr);
std::optional<cryptonote::address_parse_info> encrypted_wallet_value_to_info(std::string name, std::string encrypted_value, std::string nonce);
generic_signature make_ed25519_signature(crypto::hash const &hash, crypto::ed25519_secret_key const &skey);
generic_owner make_monero_owner(cryptonote::account_public_address const &owner, bool is_subaddress);
generic_owner make_ed25519_owner(crypto::ed25519_public_key const &pkey);

View File

@ -3655,6 +3655,7 @@ namespace cryptonote { namespace rpc {
if (!name_hash)
throw rpc_error{ERROR_WRONG_PARAM, "Unable to resolve ONS address: invalid 'name_hash' value '" + req.name_hash + "'"};
uint8_t hf_version = m_core.get_hard_fork_version(m_core.get_current_blockchain_height());
auto type = static_cast<ons::mapping_type>(req.type);
if (!ons::mapping_type_allowed(hf_version, type))
@ -3670,26 +3671,5 @@ namespace cryptonote { namespace rpc {
}
return res;
}
//------------------------------------------------------------------------------------------------------------------------------
ONS_RESOLVE_ADDRESS::response core_rpc_server::invoke(ONS_RESOLVE_ADDRESS::request&& req, rpc_context context)
{
ONS_RESOLVE_ADDRESS::response res{};
uint64_t height;
height = (req.height) ? req.height : m_core.get_current_blockchain_height();
cryptonote::address_parse_info info;
cryptonote::network_type nettype = m_core.get_nettype();
bool success = m_core.get_account_address_from_str_or_ons(info, nettype, req.address, height);
if (success)
{
res.status = STATUS_OK;
res.address = get_account_address_as_str(nettype, 0, info.address);
} else {
res.status = "Failed";
}
return res;
}
} } // namespace cryptonote

View File

@ -271,7 +271,6 @@ namespace cryptonote::rpc {
ONS_NAMES_TO_OWNERS::response invoke(ONS_NAMES_TO_OWNERS::request&& req, rpc_context context);
ONS_OWNERS_TO_NAMES::response invoke(ONS_OWNERS_TO_NAMES::request&& req, rpc_context context);
ONS_RESOLVE::response invoke(ONS_RESOLVE::request&& req, rpc_context context);
ONS_RESOLVE_ADDRESS::response invoke(ONS_RESOLVE_ADDRESS::request&& req, rpc_context context);
FLUSH_CACHE::response invoke(FLUSH_CACHE::request&& req, rpc_context);
#if defined(OXEN_ENABLE_INTEGRATION_TEST_HOOKS)

View File

@ -1340,16 +1340,6 @@ KV_SERIALIZE_MAP_CODE_BEGIN(ONS_NAMES_TO_OWNERS::response)
KV_SERIALIZE(status)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(ONS_RESOLVE_ADDRESS::request)
KV_SERIALIZE(address)
KV_SERIALIZE(height)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(ONS_RESOLVE_ADDRESS::response)
KV_SERIALIZE(address)
KV_SERIALIZE(status)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(ONS_OWNERS_TO_NAMES::request)
KV_SERIALIZE(entries)
KV_SERIALIZE(include_expired)

View File

@ -2481,28 +2481,6 @@ namespace rpc {
};
};
// Resolves a provided address, will return the address if valid or the the resolved ONS wallet mapping if valid ONS Name
struct ONS_RESOLVE_ADDRESS : PUBLIC
{
static constexpr auto names() { return NAMES("ons_resolve_address"); }
struct request
{
std::string address; // The address or name to resolve to a public key via Oxen Name Service.
uint64_t height; // Optional: if provided and true, the result will be provided for a Oxen Name Resolved at this height
KV_MAP_SERIALIZABLE
};
struct response
{
std::optional<std::string> address; // The value that the address maps to.
std::string status; // Generic RPC error code. "OK" is the success value.
KV_MAP_SERIALIZABLE
};
};
OXEN_RPC_DOC_INTROSPECT
// Get all the name mappings for the queried owner. The owner can be either a ed25519 public key or Monero style
// public key; by default purchases are owned by the spend public key of the purchasing wallet.
@ -2568,7 +2546,7 @@ namespace rpc {
struct request
{
uint16_t type; // The ONS type (mandatory); currently supported values are: 0 = session, 2 = lokinet.
uint16_t type; // The ONS type (mandatory); currently supported values are: 0 = session, 1 = wallet, 2 = lokinet.
std::string name_hash; // The 32-byte BLAKE2b hash of the name to look up, encoded as 64 hex digits or 44/43 base64 characters (with/without padding).
KV_MAP_SERIALIZABLE
@ -2685,7 +2663,6 @@ namespace rpc {
ONS_NAMES_TO_OWNERS,
ONS_OWNERS_TO_NAMES,
ONS_RESOLVE,
ONS_RESOLVE_ADDRESS,
FLUSH_CACHE
>;

View File

@ -5909,11 +5909,9 @@ bool simple_wallet::transfer_main(Transfer transfer_type, const std::vector<std:
r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), local_args[i], oa_prompter);
if (!r && m_wallet->is_trusted_daemon())
{
rpc::ONS_RESOLVE_ADDRESS::request lookup_req{};
lookup_req.address = local_args[i];
auto [success, addr_response] = m_wallet->resolve_address(lookup_req);
if (success)
r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), *addr_response.address, oa_prompter);
std::optional<std::string> address = m_wallet->resolve_address(local_args[i]);
if (address)
r = cryptonote::get_account_address_from_str_or_url(info, m_wallet->nettype(), *address, oa_prompter);
}
if(!r)
{

View File

@ -344,10 +344,28 @@ std::pair<bool, std::vector<cryptonote::rpc::ONS_NAMES_TO_OWNERS::response_entry
{
return get_result_pair<rpc::ONS_NAMES_TO_OWNERS>(request, [](auto&& res) { return std::move(res.entries); });
}
std::pair<bool, cryptonote::rpc::ONS_RESOLVE_ADDRESS::response> NodeRPCProxy::ons_resolve_address(cryptonote::rpc::ONS_RESOLVE_ADDRESS::request const &request) const
std::pair<bool,cryptonote::rpc::ONS_RESOLVE::response> NodeRPCProxy::ons_resolve(cryptonote::rpc::ONS_RESOLVE::request const &request) const
{
return get_result_pair<rpc::ONS_RESOLVE_ADDRESS>(request, [](auto&& res) { return std::move(res); });
std::pair<bool, cryptonote::rpc::ONS_RESOLVE::response> result;
auto& [success, resolved] = result;
success = false;
uint64_t height;
if (m_offline || !get_height(height))
return result;
{
try {
auto res = m_http_client.json_rpc<rpc::ONS_RESOLVE>(rpc::ONS_RESOLVE::names().front(), request);
resolved = res;
} catch (...) {
return result;
}
}
success = true;
return result;
}
}

View File

@ -62,7 +62,8 @@ public:
std::pair<bool, std::vector<cryptonote::rpc::GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::entry>> get_service_node_blacklisted_key_images() const;
std::pair<bool, std::vector<cryptonote::rpc::ONS_OWNERS_TO_NAMES::response_entry>> ons_owners_to_names(cryptonote::rpc::ONS_OWNERS_TO_NAMES::request const &request) const;
std::pair<bool, std::vector<cryptonote::rpc::ONS_NAMES_TO_OWNERS::response_entry>> ons_names_to_owners(cryptonote::rpc::ONS_NAMES_TO_OWNERS::request const &request) const;
std::pair<bool, cryptonote::rpc::ONS_RESOLVE_ADDRESS::response> ons_resolve_address(cryptonote::rpc::ONS_RESOLVE_ADDRESS::request const &request) const;
std::pair<bool, cryptonote::rpc::ONS_RESOLVE::response>
ons_resolve(cryptonote::rpc::ONS_RESOLVE::request const &request) const;
private:
bool get_info() const;

View File

@ -6519,6 +6519,45 @@ void wallet2::get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wall
}
}
//----------------------------------------------------------------------------------------------------
std::optional<std::string> wallet2::resolve_address(std::string address, uint64_t height)
{
// addr_response will have an encrypted value
cryptonote::address_parse_info info;
bool result = false;
if (cryptonote::get_account_address_from_str(info, m_nettype, address))
{
result = true;
} else {
std::string name = tools::lowercase_ascii_string(std::move(address));
std::string reason;
if (ons::validate_ons_name(ons::mapping_type::wallet, name, &reason))
{
std::string b64_hashed_name = ons::name_to_base64_hash(name);
rpc::ONS_RESOLVE::request lookup_req{1, b64_hashed_name};
auto [success, addr_response] = resolve(lookup_req);
if (success && addr_response.encrypted_value)
{
std::optional<cryptonote::address_parse_info> addr_info = ons::encrypted_wallet_value_to_info(name, *addr_response.encrypted_value, *addr_response.nonce);
if (addr_info)
{
info = std::move(*addr_info);
result = true;
LOG_PRINT_L2("Resolved ONS name: "<< address << " to address: " << get_account_address_as_str(m_nettype, info.is_subaddress, info.address));
}
}
} else {
LOG_PRINT_L2("Invalid address format, could not resolve " << address);
}
}
if (result)
return get_account_address_as_str(m_nettype, info.is_subaddress, info.address);
else
return std::nullopt;
}
//----------------------------------------------------------------------------------------------------
void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const std::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const
{
for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) {

View File

@ -806,6 +806,7 @@ private:
uint64_t min_height, uint64_t max_height = (uint64_t)-1, const std::optional<uint32_t>& subaddr_account = std::nullopt, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments, const std::optional<uint32_t>& subaddr_account = std::nullopt, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const std::optional<uint32_t>& subaddr_account = std::nullopt, const std::set<uint32_t>& subaddr_indices = {}) const;
std::optional<std::string> resolve_address(std::string address, uint64_t height = 0);
// These return pairs where .first == true if the request was successful, and .second is a
// vector of the requested entries.
@ -817,7 +818,7 @@ private:
std::vector<cryptonote::rpc::GET_SERVICE_NODES::response::entry> list_current_stakes();
auto ons_owners_to_names(cryptonote::rpc::ONS_OWNERS_TO_NAMES::request const &request) const { return m_node_rpc_proxy.ons_owners_to_names(request); }
auto ons_names_to_owners(cryptonote::rpc::ONS_NAMES_TO_OWNERS::request const &request) const { return m_node_rpc_proxy.ons_names_to_owners(request); }
auto resolve_address(cryptonote::rpc::ONS_RESOLVE_ADDRESS::request const &request) const { return m_node_rpc_proxy.ons_resolve_address(request); }
auto resolve(cryptonote::rpc::ONS_RESOLVE::request const &request) const { return m_node_rpc_proxy.ons_resolve(request); }
struct ons_detail
{

View File

@ -871,14 +871,13 @@ namespace tools
cryptonote::network_type nettype,
std::string_view addr_or_url)
{
rpc::ONS_RESOLVE_ADDRESS::request lookup_req{};
lookup_req.address = addr_or_url;
if (m_wallet->is_trusted_daemon())
{
if (auto [success, response] = m_wallet->resolve_address(lookup_req); success)
std::optional<std::string> address = m_wallet->resolve_address(std::string{addr_or_url});
if (address)
{
cryptonote::address_parse_info info;
if (!get_account_address_from_str_or_url(info, nettype, *response.address,
if (!get_account_address_from_str_or_url(info, nettype, *address,
[](const std::string_view url, const std::vector<std::string> &addresses, bool dnssec_valid) {
if (!dnssec_valid)
throw wallet_rpc_error{error_code::WRONG_ADDRESS, "Invalid DNSSEC for "s + std::string{url}};