mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
ons buy and ons update mapping
This commit is contained in:
parent
31dd71ce6f
commit
2e6b96afbc
17 changed files with 518 additions and 24 deletions
|
@ -136,6 +136,13 @@ get_hard_fork_heights(network_type nettype, hf version) {
|
|||
return found;
|
||||
}
|
||||
|
||||
hard_fork get_latest_hard_fork(network_type nettype) {
|
||||
if (nettype == network_type::MAINNET) return mainnet_hard_forks.back();
|
||||
if (nettype == network_type::TESTNET) return testnet_hard_forks.back();
|
||||
if (nettype == network_type::FAKECHAIN) return fakechain_hardforks.back();
|
||||
return devnet_hard_forks.back();
|
||||
}
|
||||
|
||||
hf hard_fork_ceil(network_type nettype, hf version) {
|
||||
auto [it, end] = get_hard_forks(nettype);
|
||||
for (; it != end; it++)
|
||||
|
|
|
@ -55,6 +55,9 @@ namespace cryptonote
|
|||
std::pair<std::optional<uint64_t>, std::optional<uint64_t>>
|
||||
get_hard_fork_heights(network_type type, hf version);
|
||||
|
||||
// Returns the latest hardfork
|
||||
hard_fork get_latest_hard_fork(network_type type);
|
||||
|
||||
// Returns the lowest network version >= the given version, that is, it rounds up missing hf table
|
||||
// entries to the next largest entry. Typically this returns the network version itself, but if
|
||||
// some versions are skipped (particularly on testnet/devnet/fakechain) then this will return the
|
||||
|
|
|
@ -523,6 +523,58 @@ bool bind_and_run(ons_sql_type type, sql_compiled_statement& statement, void *co
|
|||
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
using stringtypemap = std::pair<std::string_view, mapping_type>;
|
||||
static constexpr std::array ons_str_type_mappings = {
|
||||
stringtypemap{"5"sv, mapping_type::lokinet_10years},
|
||||
stringtypemap{"4"sv, mapping_type::lokinet_5years},
|
||||
stringtypemap{"3"sv, mapping_type::lokinet_2years},
|
||||
stringtypemap{"2"sv, mapping_type::lokinet},
|
||||
stringtypemap{"1"sv, mapping_type::wallet},
|
||||
stringtypemap{"0"sv, mapping_type::session},
|
||||
stringtypemap{"session"sv, mapping_type::session},
|
||||
stringtypemap{"wallet"sv, mapping_type::wallet},
|
||||
stringtypemap{"lokinet"sv, mapping_type::lokinet},
|
||||
stringtypemap{"lokinet_2years"sv, mapping_type::lokinet_2years},
|
||||
stringtypemap{"lokinet_5years"sv, mapping_type::lokinet_5years},
|
||||
stringtypemap{"lokinet_10years"sv, mapping_type::lokinet_10years}
|
||||
};
|
||||
|
||||
std::optional<mapping_type>
|
||||
parse_ons_type(std::string input)
|
||||
{
|
||||
// Lower-case the input:
|
||||
for (auto& c : input)
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c += ('A' - 'a');
|
||||
|
||||
for (const auto& [str, map] : ons_str_type_mappings)
|
||||
if (str == input)
|
||||
return map;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
using inttypemap = std::pair<uint16_t, mapping_type>;
|
||||
static constexpr std::array ons_int_type_mappings = {
|
||||
inttypemap{5, mapping_type::lokinet_10years},
|
||||
inttypemap{4, mapping_type::lokinet_5years},
|
||||
inttypemap{3, mapping_type::lokinet_2years},
|
||||
inttypemap{2, mapping_type::lokinet},
|
||||
inttypemap{1, mapping_type::wallet},
|
||||
inttypemap{0, mapping_type::session}
|
||||
};
|
||||
std::optional<mapping_type>
|
||||
parse_ons_type(uint16_t input)
|
||||
{
|
||||
for (const auto& [inttype, map] : ons_int_type_mappings)
|
||||
if (inttype == input)
|
||||
return map;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
bool mapping_record::active(uint64_t blockchain_height) const
|
||||
|
|
|
@ -210,6 +210,12 @@ struct settings_record
|
|||
int version;
|
||||
};
|
||||
|
||||
std::optional<mapping_type>
|
||||
parse_ons_type(std::string input);
|
||||
|
||||
std::optional<mapping_type>
|
||||
parse_ons_type(uint16_t input);
|
||||
|
||||
struct mapping_record
|
||||
{
|
||||
// NOTE: We keep expired entries in the DB indefinitely because we need to
|
||||
|
|
|
@ -116,11 +116,14 @@ def balance():
|
|||
if context.wallet is None:
|
||||
click.echo("Wallet not loaded")
|
||||
return
|
||||
click.echo("Balance: {}".format(context.wallet.get_balance()))
|
||||
click.echo("Balance: {:.2f} Oxen".format(context.wallet.get_balance()/1e9))
|
||||
|
||||
@walletcli.command()
|
||||
def unlocked_balance():
|
||||
click.echo("Unlocked Balance: {}".format(context.wallet.get_unlocked_balance()))
|
||||
if context.wallet is None:
|
||||
click.echo("Wallet not loaded")
|
||||
return
|
||||
click.echo("Unlocked Balance: {:.2f} Oxen".format(context.wallet.get_unlocked_balance()/1e9))
|
||||
|
||||
@walletcli.command()
|
||||
def height():
|
||||
|
@ -135,13 +138,51 @@ def transfer():
|
|||
if address == "" or amount == 0.0:
|
||||
click.prompt("Invalid address/amount entered")
|
||||
return
|
||||
amount_in_atomic_units = round(amount * 10e9, 0);
|
||||
amount_in_atomic_units = round(amount * 1e9, 0);
|
||||
destination = {"address": address, "amount": amount_in_atomic_units}
|
||||
transfer_params = {"destinations": [destination]}
|
||||
transfer_future = context.rpc_future("restricted.transfer", args=transfer_params);
|
||||
transfer_response = transfer_future.get();
|
||||
click.echo("Transfer Response: {}".format(transfer_response))
|
||||
|
||||
@walletcli.command()
|
||||
def ons_buy_mapping():
|
||||
ons_type = click.prompt("What type of mapping would you like", type=click.Choice(['session', 'wallet', 'lokinet', 'lokinet_2years', 'lokinet_5years', 'lokinet_10years']), default="session").strip()
|
||||
ons_name = click.prompt("Please enter the ons name you would like to register", default="").strip()
|
||||
ons_value = click.prompt("Please enter the value for the ons mapping", default="").strip()
|
||||
ons_owner = click.prompt("Optional: Enter the address of a different owner", default="").strip()
|
||||
ons_backup_owner = click.prompt("Optional: Enter the address of a backup owner", default="").strip()
|
||||
|
||||
ons_buy_params = {
|
||||
"name": ons_name,
|
||||
"value": ons_value,
|
||||
"owner": ons_owner,
|
||||
"backup_owner": ons_backup_owner,
|
||||
"type": ons_type,
|
||||
}
|
||||
transfer_future = context.rpc_future("restricted.ons_buy_mapping", args=ons_buy_params);
|
||||
transfer_response = transfer_future.get();
|
||||
click.echo("ONS Buy Mapping Response: {}".format(transfer_response))
|
||||
|
||||
@walletcli.command()
|
||||
def ons_update_mapping():
|
||||
ons_name = click.prompt("Please enter the ons name you would like to update", default="").strip()
|
||||
ons_type = click.prompt("Please enter the type of ONS mapping this is", type=click.Choice(['session', 'wallet', 'lokinet', 'lokinet_2years', 'lokinet_5years', 'lokinet_10years']), default="session").strip()
|
||||
ons_value = click.prompt("Optional: Please enter a value to modify the ons mapping", default="").strip()
|
||||
ons_owner = click.prompt("Optional: Please enter an address to modify the owner", default="").strip()
|
||||
ons_backup_owner = click.prompt("Optional: Please enter an address to modify the backup owner", default="").strip()
|
||||
|
||||
ons_update_params = {
|
||||
"name": ons_name,
|
||||
"value": ons_value,
|
||||
"owner": ons_owner,
|
||||
"backup_owner": ons_backup_owner,
|
||||
"type": ons_type,
|
||||
}
|
||||
transfer_future = context.rpc_future("restricted.ons_update_mapping", args=ons_update_params);
|
||||
transfer_response = transfer_future.get();
|
||||
click.echo("ONS Update Mapping Response: {}".format(transfer_response))
|
||||
|
||||
@walletcli.command()
|
||||
def quit():
|
||||
if context.wallet:
|
||||
|
|
|
@ -56,6 +56,9 @@ namespace wallet
|
|||
|
||||
virtual std::future<std::string>
|
||||
submit_transaction(const cryptonote::transaction& tx, bool blink) = 0;
|
||||
|
||||
virtual std::future<std::string>
|
||||
ons_names_to_owners(const std::string& name_hash, uint16_t type) = 0;
|
||||
};
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -467,6 +467,44 @@ namespace wallet
|
|||
return fut;
|
||||
}
|
||||
|
||||
std::future<std::string>
|
||||
DefaultDaemonComms::ons_names_to_owners(const std::string& name_hash, const uint16_t type)
|
||||
{
|
||||
auto p = std::make_shared<std::promise<std::string> >();
|
||||
auto fut = p->get_future();
|
||||
auto req_cb = [p=std::move(p)](bool ok, std::vector<std::string> response)
|
||||
{
|
||||
|
||||
oxenc::bt_dict_consumer dc{response[1]};
|
||||
|
||||
if (not dc.skip_until("result"))
|
||||
{
|
||||
auto reason = dc.consume_string();
|
||||
p->set_value(std::string("ONS names to owners rejected, reason: ") + reason);
|
||||
return;
|
||||
}
|
||||
auto result_list = dc.consume_list_consumer();
|
||||
const auto result = result_list.consume_dict_data();
|
||||
|
||||
p->set_value(std::string(result));
|
||||
return;
|
||||
};
|
||||
|
||||
oxenc::bt_dict req_params_dict;
|
||||
|
||||
oxenc::bt_list name_hash_list;
|
||||
name_hash_list.push_back(name_hash);
|
||||
oxenc::bt_list type_list;
|
||||
type_list.push_back(type);
|
||||
|
||||
req_params_dict["name_hash"] = name_hash_list;
|
||||
req_params_dict["type"] = type_list;
|
||||
|
||||
omq->request(conn, "rpc.ons_names_to_owners", req_cb, oxenc::bt_serialize(req_params_dict));
|
||||
|
||||
return fut;
|
||||
}
|
||||
|
||||
void
|
||||
DefaultDaemonComms::register_wallet(wallet::Wallet& wallet, int64_t height, bool check_sync_height, bool new_wallet)
|
||||
{
|
||||
|
|
|
@ -57,6 +57,9 @@ namespace wallet
|
|||
std::future<std::string>
|
||||
submit_transaction(const cryptonote::transaction& tx, bool blink);
|
||||
|
||||
std::future<std::string>
|
||||
ons_names_to_owners(const std::string& name_hash, const uint16_t type);
|
||||
|
||||
private:
|
||||
|
||||
void
|
||||
|
|
|
@ -453,4 +453,36 @@ namespace wallet
|
|||
return returned_keys;
|
||||
}
|
||||
|
||||
ons::generic_signature
|
||||
Keyring::generate_ons_signature(const std::string& curr_owner, const ons::generic_owner* new_owner, const ons::generic_owner* new_backup_owner, const ons::mapping_value& encrypted_value, const crypto::hash& prev_txid, const cryptonote::network_type& nettype)
|
||||
{
|
||||
ons::generic_signature result;
|
||||
cryptonote::address_parse_info curr_owner_parsed = {};
|
||||
if (!cryptonote::get_account_address_from_str(curr_owner_parsed, nettype, curr_owner))
|
||||
throw std::runtime_error("Could not parse address");
|
||||
|
||||
//TODO sean this should actually get it from the db
|
||||
cryptonote::subaddress_index index = {0,0};
|
||||
|
||||
//std::optional<cryptonote::subaddress_index> index = get_subaddress_index(curr_owner_parsed.address);
|
||||
//if (!index) return false;
|
||||
|
||||
auto sig_data = ons::tx_extra_signature(
|
||||
encrypted_value.to_view(),
|
||||
new_owner,
|
||||
new_backup_owner,
|
||||
prev_txid);
|
||||
if (sig_data.empty())
|
||||
throw std::runtime_error("Could not generate signature");
|
||||
|
||||
cryptonote::account_base account;
|
||||
account.create_from_keys(cryptonote::account_public_address{spend_public_key, view_public_key}, spend_private_key, view_private_key);
|
||||
auto& hwdev = account.get_device();
|
||||
hw::mode_resetter rst{key_device};
|
||||
key_device.generate_ons_signature(sig_data, account.get_keys(), index, result.monero);
|
||||
result.type = ons::generic_owner_sig_type::monero;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <crypto/crypto.h>
|
||||
#include <cryptonote_basic/subaddress_index.h>
|
||||
#include <cryptonote_basic/cryptonote_basic.h>
|
||||
#include <cryptonote_core/oxen_name_system.h>
|
||||
#include <device/device_default.hpp>
|
||||
#include <ringct/rctSigs.h>
|
||||
|
||||
|
@ -118,6 +119,9 @@ namespace wallet
|
|||
virtual cryptonote::account_keys
|
||||
export_keys();
|
||||
|
||||
virtual ons::generic_signature
|
||||
generate_ons_signature(const std::string& curr_owner, const ons::generic_owner* new_owner, const ons::generic_owner* new_backup_owner, const ons::mapping_value& encrypted_value, const crypto::hash& prev_txid, const cryptonote::network_type& nettype);
|
||||
|
||||
cryptonote::network_type nettype;
|
||||
|
||||
private:
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace wallet
|
|||
throw std::runtime_error("Transaction amounts must be positive");
|
||||
sum_recipient_amounts += recipient.amount;
|
||||
}
|
||||
if (new_recipients.empty() || sum_recipient_amounts < 0)
|
||||
if (sum_recipient_amounts < 0)
|
||||
throw std::runtime_error("Transaction amounts must be positive");
|
||||
}
|
||||
|
||||
|
@ -55,8 +55,7 @@ namespace wallet
|
|||
int64_t
|
||||
PendingTransaction::get_fee(int64_t n_inputs) const
|
||||
{
|
||||
// TODO sean add this
|
||||
int64_t fixed_fee = 0;
|
||||
int64_t fixed_fee = burn_fixed;
|
||||
// TODO sean add this
|
||||
int64_t burn_pct = 0;
|
||||
int64_t fee_percent = oxen::BLINK_BURN_TX_FEE_PERCENT_V18; // 100%
|
||||
|
@ -121,6 +120,8 @@ namespace wallet
|
|||
tx.output_unlock_times.push_back(unlock_time);
|
||||
tx.output_unlock_times.push_back(change_unlock_time);
|
||||
|
||||
tx.extra = std::move(extra);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,11 @@ namespace wallet
|
|||
uint64_t fee_per_byte = cryptonote::FEE_PER_BYTE_V13;
|
||||
uint64_t fee_per_output = cryptonote::FEE_PER_OUTPUT_V18;
|
||||
size_t mixin_count = cryptonote::TX_OUTPUT_DECOYS;
|
||||
size_t extra_size() const {return 0;};
|
||||
|
||||
uint64_t burn_fixed = 0;
|
||||
|
||||
std::vector<uint8_t> extra = {};
|
||||
size_t extra_size() const {return extra.size();};
|
||||
|
||||
PendingTransaction() = default;
|
||||
|
||||
|
|
|
@ -360,12 +360,41 @@ void parse_request(SET_LOG_CATEGORIES& req, rpc_input in) {
|
|||
}
|
||||
|
||||
void parse_request(ONS_BUY_MAPPING& req, rpc_input in) {
|
||||
get_values(in,
|
||||
"account_index", req.request.account_index,
|
||||
"backup_owner", req.request.backup_owner,
|
||||
"do_not_relay", req.request.do_not_relay,
|
||||
"get_tx_hex", req.request.get_tx_hex,
|
||||
"get_tx_key", req.request.get_tx_key,
|
||||
"get_tx_metadata", req.request.get_tx_metadata,
|
||||
"name", req.request.name,
|
||||
"owner", req.request.owner,
|
||||
"priority", req.request.priority,
|
||||
"subaddr_indices", req.request.subaddr_indices,
|
||||
"type", req.request.type,
|
||||
"value", req.request.value
|
||||
);
|
||||
}
|
||||
|
||||
void parse_request(ONS_RENEW_MAPPING& req, rpc_input in) {
|
||||
}
|
||||
|
||||
void parse_request(ONS_UPDATE_MAPPING& req, rpc_input in) {
|
||||
get_values(in,
|
||||
"account_index", req.request.account_index,
|
||||
"backup_owner", req.request.backup_owner,
|
||||
"do_not_relay", req.request.do_not_relay,
|
||||
"get_tx_hex", req.request.get_tx_hex,
|
||||
"get_tx_key", req.request.get_tx_key,
|
||||
"get_tx_metadata", req.request.get_tx_metadata,
|
||||
"name", req.request.name,
|
||||
"owner", req.request.owner,
|
||||
"priority", req.request.priority,
|
||||
"signature", req.request.signature,
|
||||
"subaddr_indices", req.request.subaddr_indices,
|
||||
"type", req.request.type,
|
||||
"value", req.request.value
|
||||
);
|
||||
}
|
||||
|
||||
void parse_request(ONS_MAKE_UPDATE_SIGNATURE& req, rpc_input in) {
|
||||
|
|
|
@ -2481,33 +2481,31 @@ namespace wallet::rpc {
|
|||
static constexpr auto names() { return NAMES("ons_buy_mapping"); }
|
||||
|
||||
static constexpr const char *description =
|
||||
R"(Buy a Loki Name System (ONS) mapping that maps a unique name to a Session ID or Lokinet address.
|
||||
R"(Buy an Oxen Name System (ONS) mapping that maps a unique name to a Session ID, Oxen Address or Lokinet address.
|
||||
|
||||
Currently supports Session, Lokinet and Wallet registrations. Lokinet registrations can be for 1, 2, 5, or 10 years by specifying a type value of "lokinet", "lokinet_2y", "lokinet_5y", "lokinet_10y". Session registrations do not expire.
|
||||
Currently supports Session, Wallet and Lokinet registrations. Lokinet registrations can be for 1, 2, 5, or 10 years by specifying a type value of "lokinet", "lokinet_2y", "lokinet_5y", "lokinet_10y". Session and Wallet registrations do not expire.
|
||||
|
||||
The owner of the ONS entry (by default, the purchasing wallet) will be permitted to submit ONS update transactions to the Loki blockchain (for example to update a Session pubkey or the target Lokinet address). You may change the primary owner or add a backup owner in the registration and can change them later with update transactions. Owner addresses can be either Loki wallets, or generic ed25519 pubkeys (for advanced uses).
|
||||
|
||||
For Session, the recommended owner or backup owner is the ed25519 public key of the user's Session ID.
|
||||
|
||||
When specifying owners, either a wallet (sub)address or standard ed25519 public key is supported per mapping. Updating the value that a name maps to requires one of the owners to sign the update transaction. For wallets, this is signed using the (sub)address's spend key.
|
||||
|
||||
For more information on updating and signing see the ONS_UPDATE_MAPPING documentation.)";
|
||||
|
||||
struct REQUEST
|
||||
{
|
||||
std::string type; // The mapping type: "session", "lokinet", "lokinet_2y", "lokinet_5y", "lokinet_10y", "wallet".
|
||||
std::string owner; // (Optional): The ed25519 public key or wallet address that has authority to update the mapping.
|
||||
std::string backup_owner; // (Optional): The secondary, backup public key that has authority to update the mapping.
|
||||
std::string name; // The name to purchase via Oxen Name Service
|
||||
std::string value; // The value that the name maps to via Oxen Name Service, (i.e. For Session: [display name->session public key], for wallets: [name->wallet address], for Lokinet: [name->domain name]).
|
||||
std::string type; // The mapping type: "session", "wallet", "lokinet", "lokinet_2y", "lokinet_5y", "lokinet_10y".
|
||||
std::string owner; // (Optional): The ed25519 public key or wallet address that has authority to update the mapping.
|
||||
std::string backup_owner; // (Optional): The secondary, backup public key that has authority to update the mapping.
|
||||
std::string name; // The name to purchase via Oxen Name Service
|
||||
std::string value; // The value that the name maps to via Oxen Name Service, (i.e. For Session: [display name->session public key], for wallets: [name->wallet address], 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)
|
||||
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 oxen 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)
|
||||
uint32_t account_index; // (Optional) Transfer from this account index. (Defaults to 0)
|
||||
std::vector<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: 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 oxen 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)
|
||||
} request;
|
||||
};
|
||||
|
||||
|
@ -2619,7 +2617,7 @@ If signing is performed externally then you must first encrypt the `value` (if b
|
|||
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)
|
||||
std::vector<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 oxen network. (Defaults to false)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include <common/hex.h>
|
||||
#include <oxenc/base64.h>
|
||||
#include "mnemonics/electrum-words.h"
|
||||
|
||||
|
||||
|
@ -24,6 +25,8 @@ using cryptonote::rpc::rpc_error;
|
|||
|
||||
namespace {
|
||||
|
||||
static auto logcat = oxen::log::Cat("wallet");
|
||||
|
||||
template <typename RPC>
|
||||
void register_rpc_command(std::unordered_map<std::string, std::shared_ptr<const rpc_command>>& regs)
|
||||
{
|
||||
|
@ -471,12 +474,83 @@ void RequestHandler::invoke(SET_LOG_CATEGORIES& command, rpc_context context) {
|
|||
}
|
||||
|
||||
void RequestHandler::invoke(ONS_BUY_MAPPING& command, rpc_context context) {
|
||||
//TODO sean these params need to be accounted for
|
||||
// "do_not_relay", req.request.do_not_relay.
|
||||
// "get_tx_hex", req.request.get_tx_hex.
|
||||
// "get_tx_key", req.request.get_tx_key.
|
||||
// "get_tx_metadata", req.request.get_tx_metadata.
|
||||
// "priority", req.request.priority,
|
||||
// "subaddr_indices", req.request.subaddr_indices,
|
||||
|
||||
oxen::log::info(logcat, "RPC Handler received ONS_BUY_MAPPING command");
|
||||
if (auto w = wallet.lock())
|
||||
{
|
||||
cryptonote::tx_destination_entry change_dest;
|
||||
change_dest.original = w->keys->get_main_address();
|
||||
cryptonote::address_parse_info change_addr_info;
|
||||
cryptonote::get_account_address_from_str(change_addr_info, w->nettype, change_dest.original);
|
||||
change_dest.amount = 0;
|
||||
change_dest.addr = change_addr_info.address;
|
||||
change_dest.is_subaddress = change_addr_info.is_subaddress;
|
||||
change_dest.is_integrated = change_addr_info.has_payment_id;
|
||||
|
||||
auto ptx = w->tx_constructor->create_ons_buy_transaction(
|
||||
change_dest,
|
||||
command.request.type,
|
||||
command.request.owner,
|
||||
command.request.backup_owner,
|
||||
command.request.name,
|
||||
command.request.value
|
||||
);
|
||||
|
||||
w->keys->sign_transaction(ptx);
|
||||
|
||||
auto submit_future = w->daemon_comms->submit_transaction(ptx.tx, false);
|
||||
|
||||
if (submit_future.wait_for(5s) != std::future_status::ready)
|
||||
throw rpc_error(500, "request to daemon timed out");
|
||||
|
||||
command.response["status"] = "200";
|
||||
command.response["result"] = submit_future.get();
|
||||
}
|
||||
}
|
||||
|
||||
void RequestHandler::invoke(ONS_RENEW_MAPPING& command, rpc_context context) {
|
||||
}
|
||||
|
||||
void RequestHandler::invoke(ONS_UPDATE_MAPPING& command, rpc_context context) {
|
||||
oxen::log::info(logcat, "RPC Handler received ONS_UPDATE_MAPPING command");
|
||||
if (auto w = wallet.lock())
|
||||
{
|
||||
cryptonote::tx_destination_entry change_dest;
|
||||
change_dest.original = w->keys->get_main_address();
|
||||
cryptonote::address_parse_info change_addr_info;
|
||||
cryptonote::get_account_address_from_str(change_addr_info, w->nettype, change_dest.original);
|
||||
change_dest.amount = 0;
|
||||
change_dest.addr = change_addr_info.address;
|
||||
change_dest.is_subaddress = change_addr_info.is_subaddress;
|
||||
change_dest.is_integrated = change_addr_info.has_payment_id;
|
||||
|
||||
auto ptx = w->tx_constructor->create_ons_update_transaction(
|
||||
change_dest,
|
||||
command.request.type ,
|
||||
command.request.owner,
|
||||
command.request.backup_owner,
|
||||
command.request.name,
|
||||
command.request.value,
|
||||
w->keys
|
||||
);
|
||||
|
||||
w->keys->sign_transaction(ptx);
|
||||
|
||||
auto submit_future = w->daemon_comms->submit_transaction(ptx.tx, false);
|
||||
|
||||
if (submit_future.wait_for(5s) != std::future_status::ready)
|
||||
throw rpc_error(500, "request to daemon timed out");
|
||||
|
||||
command.response["status"] = "200";
|
||||
command.response["result"] = submit_future.get();
|
||||
}
|
||||
}
|
||||
|
||||
void RequestHandler::invoke(ONS_MAKE_UPDATE_SIGNATURE& command, rpc_context context) {
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "decoy_selection/decoy_selection.hpp"
|
||||
#include "db_schema.hpp"
|
||||
|
||||
#include <oxenc/base64.h>
|
||||
|
||||
#include <cryptonote_basic/hardfork.h>
|
||||
|
||||
//TODO: nettype-based tx construction parameters
|
||||
|
@ -29,6 +31,179 @@ namespace wallet
|
|||
return new_tx;
|
||||
}
|
||||
|
||||
PendingTransaction
|
||||
TransactionConstructor::create_ons_buy_transaction(
|
||||
const cryptonote::tx_destination_entry& change_recipient,
|
||||
const std::string& type_str,
|
||||
const std::string& owner_str,
|
||||
const std::string& backup_owner_str,
|
||||
const std::string& name,
|
||||
const std::string& value
|
||||
)
|
||||
{
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
PendingTransaction new_tx(recipients);
|
||||
auto [hf, hf_uint8] = cryptonote::get_ideal_block_version(db->network_type(), db->scan_target_height());
|
||||
cryptonote::oxen_construct_tx_params tx_params{hf, cryptonote::txtype::oxen_name_system, 0, 0};
|
||||
new_tx.tx.version = cryptonote::transaction::get_max_version_for_hf(tx_params.hf_version);
|
||||
new_tx.tx.type = tx_params.tx_type;
|
||||
new_tx.fee_per_byte = fee_per_byte;
|
||||
new_tx.fee_per_output = fee_per_output;
|
||||
new_tx.change = change_recipient;
|
||||
new_tx.blink = false;
|
||||
|
||||
std::string reason = "";
|
||||
|
||||
const auto type = ons::parse_ons_type(type_str);
|
||||
if (!type.has_value())
|
||||
throw std::runtime_error("invalid type provided");
|
||||
|
||||
const auto lower_name = tools::lowercase_ascii_string(name);
|
||||
if (!ons::validate_ons_name(*type, lower_name, &reason))
|
||||
throw std::runtime_error(reason);
|
||||
const auto name_hash = ons::name_to_hash(lower_name);
|
||||
|
||||
ons::mapping_value encrypted_value;
|
||||
if (!ons::mapping_value::validate(nettype, *type, value, &encrypted_value, &reason))
|
||||
throw std::runtime_error(reason);
|
||||
|
||||
if (!encrypted_value.encrypt(lower_name, &name_hash))
|
||||
throw std::runtime_error("Fail to encrypt mapping value=" + value);
|
||||
|
||||
ons::generic_owner owner;
|
||||
ons::generic_owner backup_owner;
|
||||
|
||||
if (owner_str == "")
|
||||
owner = ons::make_monero_owner(change_recipient.addr, change_recipient.is_subaddress);
|
||||
else if (!ons::parse_owner_to_generic_owner(nettype, owner_str, owner, &reason))
|
||||
throw std::runtime_error(reason);
|
||||
|
||||
if (backup_owner_str != "" && !ons::parse_owner_to_generic_owner(nettype, backup_owner_str, backup_owner, &reason))
|
||||
throw std::runtime_error(reason);
|
||||
|
||||
// No prev_txid for initial ons buy
|
||||
crypto::hash prev_txid = {};
|
||||
|
||||
auto ons_buy_data = cryptonote::tx_extra_oxen_name_system::make_buy(
|
||||
owner,
|
||||
backup_owner_str != "" ? &backup_owner : nullptr,
|
||||
*type,
|
||||
name_hash,
|
||||
encrypted_value.to_string(),
|
||||
prev_txid);
|
||||
|
||||
new_tx.burn_fixed = ons::burn_needed(cryptonote::get_latest_hard_fork(nettype).version, *type);
|
||||
new_tx.update_change();
|
||||
|
||||
//Finally save the data to the extra field of our transaction
|
||||
cryptonote::add_oxen_name_system_to_tx_extra(new_tx.extra, ons_buy_data);
|
||||
cryptonote::add_burned_amount_to_tx_extra(new_tx.extra, new_tx.burn_fixed);
|
||||
|
||||
select_inputs_and_finalise(new_tx);
|
||||
return new_tx;
|
||||
}
|
||||
|
||||
PendingTransaction
|
||||
TransactionConstructor::create_ons_update_transaction(
|
||||
const cryptonote::tx_destination_entry& change_recipient,
|
||||
const std::string& type_str,
|
||||
const std::string& owner_str,
|
||||
const std::string& backup_owner_str,
|
||||
const std::string& name,
|
||||
const std::string& value,
|
||||
std::shared_ptr<Keyring> keyring
|
||||
)
|
||||
{
|
||||
if (value == "" && owner_str == "" && backup_owner_str == "")
|
||||
throw std::runtime_error("Value, owner and backup owner are not specified. Atleast one field must be specified for updating the ONS record");
|
||||
|
||||
const auto lower_name = tools::lowercase_ascii_string(name);
|
||||
std::string reason;
|
||||
const auto type = ons::parse_ons_type(type_str);
|
||||
if (!type.has_value())
|
||||
throw std::runtime_error("invalid type provided");
|
||||
if (!ons::validate_ons_name(*type, lower_name, &reason))
|
||||
throw std::runtime_error(reason);
|
||||
const auto name_hash = ons::name_to_hash(lower_name);
|
||||
|
||||
auto submit_ons_future = daemon->ons_names_to_owners(oxenc::to_base64(tools::view_guts(name_hash)), ons::db_mapping_type(*type));
|
||||
if (submit_ons_future.wait_for(5s) != std::future_status::ready)
|
||||
throw std::runtime_error("request to daemon for ons_names_to_owners timed out");
|
||||
|
||||
const auto ons_response = submit_ons_future.get();
|
||||
crypto::hash prev_txid;
|
||||
std::string curr_owner;
|
||||
|
||||
oxenc::bt_dict_consumer dc{ons_response};
|
||||
if (not dc.skip_until("owner"))
|
||||
{
|
||||
auto reason = dc.consume_string();
|
||||
throw std::runtime_error("Submit ons names to owners rejected, reason: " + reason);
|
||||
}
|
||||
curr_owner = dc.consume_string();
|
||||
|
||||
if (not dc.skip_until("txid"))
|
||||
{
|
||||
auto reason = dc.consume_string();
|
||||
throw std::runtime_error("Submit ons names to owners rejected, reason: " + reason);
|
||||
}
|
||||
tools::hex_to_type<crypto::hash>(dc.consume_string(), prev_txid);
|
||||
|
||||
ons::mapping_value encrypted_value;
|
||||
if (value != "")
|
||||
{
|
||||
if (!ons::mapping_value::validate(nettype, *type, value, &encrypted_value, &reason))
|
||||
throw std::runtime_error(reason);
|
||||
|
||||
if (!encrypted_value.encrypt(lower_name, &name_hash))
|
||||
throw std::runtime_error("Fail to encrypt name");
|
||||
}
|
||||
|
||||
ons::generic_owner owner;
|
||||
if (owner_str != "" && !ons::parse_owner_to_generic_owner(nettype, owner_str, owner, &reason))
|
||||
throw std::runtime_error(reason);
|
||||
|
||||
ons::generic_owner backup_owner;
|
||||
if (backup_owner_str != "" && !ons::parse_owner_to_generic_owner(nettype, backup_owner_str, backup_owner, &reason))
|
||||
throw std::runtime_error(reason);
|
||||
|
||||
const auto signature = keyring->generate_ons_signature(
|
||||
curr_owner,
|
||||
owner_str != "" ? &owner : nullptr,
|
||||
backup_owner_str != "" ? &backup_owner : nullptr,
|
||||
encrypted_value,
|
||||
prev_txid,
|
||||
nettype
|
||||
);
|
||||
|
||||
std::vector<cryptonote::tx_destination_entry> recipients;
|
||||
PendingTransaction new_tx(recipients);
|
||||
auto [hf, hf_uint8] = cryptonote::get_ideal_block_version(db->network_type(), db->scan_target_height());
|
||||
cryptonote::oxen_construct_tx_params tx_params{hf, cryptonote::txtype::oxen_name_system, 0, 0};
|
||||
new_tx.tx.version = cryptonote::transaction::get_max_version_for_hf(tx_params.hf_version);
|
||||
new_tx.tx.type = tx_params.tx_type;
|
||||
new_tx.fee_per_byte = fee_per_byte;
|
||||
new_tx.fee_per_output = fee_per_output;
|
||||
new_tx.change = change_recipient;
|
||||
new_tx.blink = false;
|
||||
|
||||
auto ons_update_data = cryptonote::tx_extra_oxen_name_system::make_update(
|
||||
signature,
|
||||
*type,
|
||||
name_hash,
|
||||
encrypted_value.to_string(),
|
||||
owner_str != "" ? &owner : nullptr,
|
||||
backup_owner_str != "" ? &backup_owner : nullptr,
|
||||
prev_txid);
|
||||
|
||||
//Finally save the data to the extra field of our transaction
|
||||
cryptonote::add_oxen_name_system_to_tx_extra(new_tx.extra, ons_update_data);
|
||||
new_tx.update_change();
|
||||
|
||||
select_inputs_and_finalise(new_tx);
|
||||
return new_tx;
|
||||
}
|
||||
|
||||
|
||||
// SelectInputs will choose some available unspent outputs from the database and allocate to the
|
||||
// transaction can be called multiple times and will add until enough is sufficient
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <memory>
|
||||
#include "pending_transaction.hpp"
|
||||
#include "daemon_comms.hpp"
|
||||
#include "keyring.hpp"
|
||||
#include "decoy_selection/decoy_selection.hpp"
|
||||
|
||||
namespace wallet
|
||||
|
@ -33,9 +34,32 @@ namespace wallet
|
|||
PendingTransaction
|
||||
create_transaction(const std::vector<cryptonote::tx_destination_entry>& recipients, const cryptonote::tx_destination_entry& change_recipient);
|
||||
|
||||
PendingTransaction
|
||||
create_ons_buy_transaction(
|
||||
const cryptonote::tx_destination_entry& change_recipient,
|
||||
const std::string& type_str,
|
||||
const std::string& owner_str,
|
||||
const std::string& backup_owner_str,
|
||||
const std::string& name,
|
||||
const std::string& value
|
||||
);
|
||||
|
||||
PendingTransaction
|
||||
create_ons_update_transaction(
|
||||
const cryptonote::tx_destination_entry& change_recipient,
|
||||
const std::string& type_str,
|
||||
const std::string& owner_str,
|
||||
const std::string& backup_owner_str,
|
||||
const std::string& name,
|
||||
const std::string& value,
|
||||
std::shared_ptr<Keyring> keyring
|
||||
);
|
||||
|
||||
uint64_t fee_per_byte = cryptonote::FEE_PER_BYTE_V13;
|
||||
uint64_t fee_per_output = cryptonote::FEE_PER_OUTPUT_V18;
|
||||
|
||||
cryptonote::network_type nettype = cryptonote::network_type::TESTNET;
|
||||
|
||||
std::unique_ptr<DecoySelector> decoy_selector;
|
||||
|
||||
private:
|
||||
|
|
Loading…
Reference in a new issue