mirror of https://github.com/oxen-io/oxen-core.git
578 lines
17 KiB
C++
578 lines
17 KiB
C++
#include "request_handler.h"
|
|
|
|
#include "commands.h"
|
|
#include "command_parser.h"
|
|
#include <wallet3/wallet.hpp>
|
|
#include <wallet3/db/walletdb.hpp>
|
|
#include <version.h>
|
|
|
|
#include <cryptonote_core/cryptonote_tx_utils.h>
|
|
|
|
#include <unordered_map>
|
|
#include <memory>
|
|
|
|
#include <common/hex.h>
|
|
#include <oxenc/base64.h>
|
|
#include "mnemonics/electrum-words.h"
|
|
|
|
|
|
|
|
namespace wallet::rpc {
|
|
|
|
using cryptonote::rpc::rpc_context;
|
|
using cryptonote::rpc::rpc_request;
|
|
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)
|
|
{
|
|
using cryptonote::rpc::RPC_COMMAND;
|
|
using cryptonote::rpc::RESTRICTED;
|
|
|
|
static_assert(std::is_base_of_v<RPC_COMMAND, RPC>);
|
|
auto cmd = std::make_shared<rpc_command>();
|
|
cmd->is_restricted = std::is_base_of_v<RESTRICTED, RPC>;
|
|
|
|
cmd->invoke = cryptonote::rpc::make_invoke<RPC, RequestHandler, rpc_command>();
|
|
|
|
for (const auto& name : RPC::names())
|
|
regs.emplace(name, cmd);
|
|
}
|
|
|
|
template <typename... RPC, typename... BinaryRPC>
|
|
std::unordered_map<std::string, std::shared_ptr<const rpc_command>> register_rpc_commands(tools::type_list<RPC...>) {
|
|
std::unordered_map<std::string, std::shared_ptr<const rpc_command>> regs;
|
|
|
|
(register_rpc_command<RPC>(regs), ...);
|
|
|
|
return regs;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
void RequestHandler::set_wallet(std::weak_ptr<wallet::Wallet> ptr)
|
|
{
|
|
wallet = ptr;
|
|
}
|
|
|
|
//TODO sean something here
|
|
std::string RequestHandler::submit_transaction(wallet::PendingTransaction& ptx)
|
|
{
|
|
std::string response;
|
|
if (auto w = wallet.lock())
|
|
{
|
|
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");
|
|
response = submit_future.get();
|
|
}
|
|
return response;
|
|
}
|
|
|
|
const std::unordered_map<std::string, std::shared_ptr<const rpc_command>> rpc_commands = register_rpc_commands(wallet_rpc_types{});
|
|
|
|
void RequestHandler::invoke(GET_BALANCE& command, rpc_context context) {
|
|
if (auto w = wallet.lock())
|
|
{
|
|
command.response["balance"] = w->get_balance();
|
|
command.response["unlocked_balance"] = w->get_unlocked_balance();
|
|
}
|
|
//TODO handle subaddresses and params passed for them
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_ADDRESS& command, rpc_context context) {
|
|
//TODO: implement fetching address/subaddress from db/keyring
|
|
if (auto w = wallet.lock())
|
|
{
|
|
command.response["address"] = w->keys->get_main_address();
|
|
//auto& addresses = (command.response["addresses"] = json::array());
|
|
//if (command.request.address_index.size() == 0)
|
|
//{
|
|
//auto address = w->get_subaddress(command.request.account_index, 0);
|
|
//addresses.push_back(json{
|
|
//{"address", address.as_str(cryptonote::network_type::MAINNET)},
|
|
//{"label", ""},
|
|
//{"address_index", command.request.address_index},
|
|
//{"used", true}
|
|
//});
|
|
//} else {
|
|
//for (const auto& address_index: command.request.address_index)
|
|
//{
|
|
//auto address = w->get_subaddress(command.request.account_index, address_index);
|
|
//addresses.push_back(json{
|
|
//{"address", address.as_str(cryptonote::network_type::MAINNET)},
|
|
//{"label", ""},
|
|
//{"address_index", command.request.address_index},
|
|
//{"used", true}
|
|
//});
|
|
//}
|
|
//}
|
|
}
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_ADDRESS_INDEX& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CREATE_ADDRESS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(LABEL_ADDRESS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_ACCOUNTS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CREATE_ACCOUNT& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(LABEL_ACCOUNT& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_ACCOUNT_TAGS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(TAG_ACCOUNTS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(UNTAG_ACCOUNTS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SET_ACCOUNT_TAG_DESCRIPTION& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_HEIGHT& command, rpc_context context) {
|
|
if (auto w = wallet.lock())
|
|
{
|
|
const auto immutable_height = w->db->scan_target_height();
|
|
const auto height = w->db->current_height();
|
|
|
|
command.response["height"] = height;
|
|
|
|
command.response["immutable_height"] = immutable_height;
|
|
}
|
|
}
|
|
|
|
void RequestHandler::invoke(TRANSFER& command, rpc_context context) {
|
|
oxen::log::info(logcat, "RPC Handler received TRANSFER command");
|
|
wallet::PendingTransaction ptx;
|
|
if (auto w = wallet.lock())
|
|
{
|
|
// TODO: arg checking
|
|
// TODO: actually use all args
|
|
|
|
std::vector<cryptonote::tx_destination_entry> recipients;
|
|
for (const auto& [dest, amount] : command.request.destinations)
|
|
{
|
|
auto& entry = recipients.emplace_back();
|
|
cryptonote::address_parse_info addr_info;
|
|
|
|
if (not cryptonote::get_account_address_from_str(addr_info, w->nettype, dest))
|
|
//TODO: is 500 the "correct" error code?
|
|
throw rpc_error(500, std::string("Invalid destination: ") + dest);
|
|
|
|
entry.original = dest;
|
|
entry.amount = amount;
|
|
entry.addr = addr_info.address;
|
|
entry.is_subaddress = addr_info.is_subaddress;
|
|
entry.is_integrated = addr_info.has_payment_id;
|
|
}
|
|
|
|
//TODO: change subaddress as arg, for now just use main address
|
|
cryptonote::tx_destination_entry change_dest;
|
|
change_dest.original = w->db->get_address(0,0);
|
|
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;
|
|
|
|
ptx = w->tx_constructor->create_transaction(recipients, change_dest);
|
|
}
|
|
command.response["result"] = submit_transaction(ptx);
|
|
command.response["status"] = "200";
|
|
}
|
|
|
|
void RequestHandler::invoke(TRANSFER_SPLIT& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(DESCRIBE_TRANSFER& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SIGN_TRANSFER& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SUBMIT_TRANSFER& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SWEEP_DUST& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SWEEP_ALL& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SWEEP_SINGLE& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(RELAY_TX& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(STORE& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_PAYMENTS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_BULK_PAYMENTS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(INCOMING_TRANSFERS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(EXPORT_VIEW_KEY& command, rpc_context context) {
|
|
if (auto w = wallet.lock())
|
|
{
|
|
const auto& keys = w->export_keys();
|
|
command.response["key"] = tools::type_to_hex(keys.m_view_secret_key);
|
|
}
|
|
}
|
|
|
|
void RequestHandler::invoke(EXPORT_SPEND_KEY& command, rpc_context context) {
|
|
if (auto w = wallet.lock())
|
|
{
|
|
const auto& keys = w->export_keys();
|
|
command.response["key"] = tools::type_to_hex(keys.m_spend_secret_key);
|
|
}
|
|
}
|
|
|
|
void RequestHandler::invoke(EXPORT_MNEMONIC_KEY& command, rpc_context context) {
|
|
if (auto w = wallet.lock())
|
|
{
|
|
const auto keys = w->export_keys();
|
|
const crypto::secret_key& key = keys.m_spend_secret_key;
|
|
//if (!passphrase.empty())
|
|
//key = cryptonote::encrypt_key(key, passphrase);
|
|
std::string electrum_words;
|
|
command.response["mnemonic"] = crypto::ElectrumWords::bytes_to_words(key, command.request.language);
|
|
}
|
|
}
|
|
void RequestHandler::invoke(MAKE_INTEGRATED_ADDRESS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SPLIT_INTEGRATED_ADDRESS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(STOP_WALLET& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(RESCAN_BLOCKCHAIN& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SET_TX_NOTES& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_TX_NOTES& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SET_ATTRIBUTE& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_ATTRIBUTE& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_TX_KEY& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CHECK_TX_KEY& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_TX_PROOF& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CHECK_TX_PROOF& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_SPEND_PROOF& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CHECK_SPEND_PROOF& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_RESERVE_PROOF& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CHECK_RESERVE_PROOF& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_TRANSFERS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_TRANSFERS_CSV& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_TRANSFER_BY_TXID& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SIGN& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(VERIFY& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(EXPORT_OUTPUTS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(EXPORT_TRANSFERS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(IMPORT_OUTPUTS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(EXPORT_KEY_IMAGES& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(IMPORT_KEY_IMAGES& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(MAKE_URI& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(PARSE_URI& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(ADD_ADDRESS_BOOK_ENTRY& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(EDIT_ADDRESS_BOOK_ENTRY& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_ADDRESS_BOOK_ENTRY& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(DELETE_ADDRESS_BOOK_ENTRY& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(RESCAN_SPENT& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(REFRESH& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(AUTO_REFRESH& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(START_MINING& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(STOP_MINING& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_LANGUAGES& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CREATE_WALLET& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(OPEN_WALLET& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CLOSE_WALLET& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CHANGE_WALLET_PASSWORD& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GENERATE_FROM_KEYS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(RESTORE_DETERMINISTIC_WALLET& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(IS_MULTISIG& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(PREPARE_MULTISIG& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(MAKE_MULTISIG& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(EXPORT_MULTISIG& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(IMPORT_MULTISIG& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(FINALIZE_MULTISIG& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(EXCHANGE_MULTISIG_KEYS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SIGN_MULTISIG& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SUBMIT_MULTISIG& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(GET_VERSION& command, rpc_context context) {
|
|
if (auto w = wallet.lock())
|
|
{
|
|
command.response["version"] = OXEN_VERSION_STR;
|
|
}
|
|
}
|
|
|
|
void RequestHandler::invoke(STAKE& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(REGISTER_SERVICE_NODE& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(REQUEST_STAKE_UNLOCK& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(CAN_REQUEST_STAKE_UNLOCK& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(VALIDATE_ADDRESS& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(SET_DAEMON& command, rpc_context context) {
|
|
if (auto w = wallet.lock())
|
|
{
|
|
if (command.request.address != "")
|
|
w->config.daemon.address = command.request.address;
|
|
|
|
if (command.request.proxy != "")
|
|
w->config.daemon.proxy = command.request.proxy;
|
|
|
|
w->config.daemon.trusted = command.request.trusted;
|
|
|
|
if (command.request.ssl_private_key_path != "")
|
|
w->config.daemon.ssl_private_key_path = command.request.ssl_private_key_path;
|
|
|
|
if (command.request.ssl_certificate_path != "")
|
|
w->config.daemon.ssl_certificate_path = command.request.ssl_certificate_path;
|
|
|
|
if (command.request.ssl_ca_file != "")
|
|
w->config.daemon.ssl_ca_file = command.request.ssl_ca_file;
|
|
|
|
w->config.daemon.ssl_allow_any_cert = command.request.ssl_allow_any_cert;
|
|
|
|
w->propogate_config();
|
|
}
|
|
}
|
|
|
|
void RequestHandler::invoke(SET_LOG_LEVEL& command, rpc_context context) {
|
|
}
|
|
|
|
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");
|
|
wallet::PendingTransaction ptx;
|
|
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;
|
|
|
|
ptx = w->tx_constructor->create_ons_buy_transaction(
|
|
command.request.name,
|
|
command.request.type,
|
|
command.request.value,
|
|
command.request.owner,
|
|
command.request.backup_owner,
|
|
change_dest
|
|
);
|
|
}
|
|
command.response["result"] = submit_transaction(ptx);
|
|
command.response["status"] = "200";
|
|
}
|
|
|
|
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");
|
|
wallet::PendingTransaction ptx;
|
|
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;
|
|
|
|
ptx = w->tx_constructor->create_ons_update_transaction(
|
|
command.request.name,
|
|
command.request.type,
|
|
command.request.value,
|
|
command.request.owner,
|
|
command.request.backup_owner,
|
|
change_dest,
|
|
w->keys
|
|
);
|
|
}
|
|
command.response["result"] = submit_transaction(ptx);
|
|
command.response["status"] = "200";
|
|
}
|
|
|
|
void RequestHandler::invoke(ONS_MAKE_UPDATE_SIGNATURE& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(ONS_HASH_NAME& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(ONS_KNOWN_NAMES& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(ONS_ADD_KNOWN_NAMES& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(ONS_ENCRYPT_VALUE& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(ONS_DECRYPT_VALUE& command, rpc_context context) {
|
|
}
|
|
|
|
void RequestHandler::invoke(STATUS& command, rpc_context context) {
|
|
if (auto w = wallet.lock())
|
|
{
|
|
const auto sync_height = w->db->current_height();
|
|
const auto target_height = w->db->scan_target_height();
|
|
|
|
command.response["sync_height"] = sync_height;
|
|
command.response["target_height"] = target_height;
|
|
|
|
command.response["syncing"] = sync_height < target_height;
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace wallet::rpc
|