mirror of
https://codeberg.org/wownero/wownero-lws
synced 2026-01-11 08:05:16 -08:00
Add support for admin REST server (#62)
This commit is contained in:
committed by
Lee *!* Clagett
parent
c958ac7963
commit
7b1e1e58f8
@@ -30,6 +30,7 @@
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include <boost/range/adaptor/filtered.hpp>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
@@ -47,63 +48,45 @@
|
||||
#include "db/string.h"
|
||||
#include "options.h"
|
||||
#include "misc_log_ex.h" // monero/contrib/epee/include
|
||||
#include "rpc/admin.h"
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "string_tools.h" // monero/contrib/epee/include
|
||||
#include "wire/crypto.h"
|
||||
#include "wire/filters.h"
|
||||
#include "wire/json/write.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Do not output "full" debug data provided by `db::data.h` header; truncate output
|
||||
// wrapper for custom output for admin accounts
|
||||
template<typename T>
|
||||
struct truncated
|
||||
struct admin_display
|
||||
{
|
||||
T value;
|
||||
};
|
||||
|
||||
void write_bytes(wire::json_writer& dest, const truncated<lws::db::account>& self)
|
||||
void write_bytes(wire::json_writer& dest, const admin_display<lws::db::account>& source)
|
||||
{
|
||||
wire::object(dest,
|
||||
wire::field("address", lws::db::address_string(self.value.address)),
|
||||
wire::field("scan_height", self.value.scan_height),
|
||||
wire::field("access_time", self.value.access)
|
||||
);
|
||||
};
|
||||
|
||||
void write_bytes(wire::json_writer& dest, const truncated<lws::db::request_info>& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
wire::field("address", lws::db::address_string(self.value.address)),
|
||||
wire::field("start_height", self.value.start_height)
|
||||
wire::field("address", lws::db::address_string(source.value.address)),
|
||||
wire::field("key", std::cref(source.value.key))
|
||||
);
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void write_bytes(wire::json_writer& dest, const truncated<boost::iterator_range<lmdb::value_iterator<V>>> self)
|
||||
void write_bytes(wire::json_writer& dest, admin_display<boost::iterator_range<lmdb::value_iterator<lws::db::account>>> source)
|
||||
{
|
||||
const auto truncate = [] (V src) { return truncated<V>{std::move(src)}; };
|
||||
wire::array(dest, std::move(self.value), truncate);
|
||||
const auto filter = [](const lws::db::account& src)
|
||||
{ return bool(src.flags & lws::db::account_flags::admin_account); };
|
||||
const auto transform = [] (lws::db::account src)
|
||||
{ return admin_display<lws::db::account>{std::move(src)}; };
|
||||
|
||||
wire::array(dest, (source.value | boost::adaptors::filtered(filter)), transform);
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
void stream_json_object(std::ostream& dest, boost::iterator_range<lmdb::key_iterator<K, V>> self)
|
||||
template<typename F, typename... T>
|
||||
void run_command(F f, std::ostream& dest, T&&... args)
|
||||
{
|
||||
using value_range = boost::iterator_range<lmdb::value_iterator<V>>;
|
||||
const auto truncate = [] (value_range src) -> truncated<value_range>
|
||||
{
|
||||
return {std::move(src)};
|
||||
};
|
||||
|
||||
wire::json_stream_writer json{dest};
|
||||
wire::dynamic_object(json, std::move(self), wire::enum_as_string, truncate);
|
||||
json.finish();
|
||||
}
|
||||
|
||||
void write_json_addresses(std::ostream& dest, epee::span<const lws::db::account_address> self)
|
||||
{
|
||||
// writes an array of monero base58 address strings
|
||||
wire::json_stream_writer stream{dest};
|
||||
wire::object(stream, wire::field("updated", wire::as_array(self, lws::db::address_string)));
|
||||
MONERO_UNWRAP(f(stream, std::forward<T>(args)...));
|
||||
stream.finish();
|
||||
}
|
||||
|
||||
@@ -151,6 +134,7 @@ namespace
|
||||
arguments.remove_prefix(1);
|
||||
|
||||
std::vector<lws::db::account_address> addresses{};
|
||||
addresses.reserve(arguments.size());
|
||||
for (std::string const& address : arguments)
|
||||
addresses.push_back(lws::db::address_string(address).value());
|
||||
return addresses;
|
||||
@@ -161,15 +145,11 @@ namespace
|
||||
if (prog.arguments.size() < 2)
|
||||
throw std::runtime_error{"accept_requests requires 2 or more arguments"};
|
||||
|
||||
const lws::db::request req =
|
||||
MONERO_UNWRAP(lws::db::request_from_string(prog.arguments[0]));
|
||||
std::vector<lws::db::account_address> addresses =
|
||||
get_addresses(epee::to_span(prog.arguments));
|
||||
|
||||
const std::vector<lws::db::account_address> updated =
|
||||
prog.disk.accept_requests(req, epee::to_span(addresses)).value();
|
||||
|
||||
write_json_addresses(out, epee::to_span(updated));
|
||||
lws::rpc::address_requests req{
|
||||
get_addresses(epee::to_span(prog.arguments)),
|
||||
MONERO_UNWRAP(lws::db::request_from_string(prog.arguments[0]))
|
||||
};
|
||||
run_command(lws::rpc::accept_requests, out, std::move(prog.disk), std::move(req));
|
||||
}
|
||||
|
||||
void add_account(program prog, std::ostream& out)
|
||||
@@ -177,13 +157,31 @@ namespace
|
||||
if (prog.arguments.size() != 2)
|
||||
throw std::runtime_error{"add_account needs exactly two arguments"};
|
||||
|
||||
const lws::db::account_address address[1] = {
|
||||
lws::db::address_string(prog.arguments[0]).value()
|
||||
lws::rpc::add_account_req req{
|
||||
lws::db::address_string(prog.arguments[0]).value(),
|
||||
get_key(prog.arguments[1])
|
||||
};
|
||||
const crypto::secret_key key{get_key(prog.arguments[1])};
|
||||
run_command(lws::rpc::add_account, out, std::move(prog.disk), std::move(req));
|
||||
}
|
||||
|
||||
MONERO_UNWRAP(prog.disk.add_account(address[0], key));
|
||||
write_json_addresses(out, address);
|
||||
void create_admin(program prog, std::ostream& out)
|
||||
{
|
||||
if (!prog.arguments.empty())
|
||||
throw std::runtime_error{"create_admin takes zero arguments"};
|
||||
|
||||
admin_display<lws::db::account> account{};
|
||||
{
|
||||
crypto::secret_key auth{};
|
||||
crypto::generate_keys(account.value.address.view_public, auth);
|
||||
MONERO_UNWRAP(prog.disk.add_account(account.value.address, auth, lws::db::account_flags::admin_account));
|
||||
|
||||
static_assert(sizeof(auth) == sizeof(account.value.key), "bad memcpy");
|
||||
std::memcpy(std::addressof(account.value.key), std::addressof(auth), sizeof(auth));
|
||||
}
|
||||
|
||||
wire::json_stream_writer json{out};
|
||||
write_bytes(json, account);
|
||||
json.finish();
|
||||
}
|
||||
|
||||
void debug_database(program prog, std::ostream& out)
|
||||
@@ -199,20 +197,31 @@ namespace
|
||||
{
|
||||
if (!prog.arguments.empty())
|
||||
throw std::runtime_error{"list_accounts takes zero arguments"};
|
||||
run_command(lws::rpc::list_accounts, out, std::move(prog.disk));
|
||||
}
|
||||
|
||||
auto reader = prog.disk.start_read().value();
|
||||
auto stream = reader.get_accounts().value();
|
||||
stream_json_object(out, stream.make_range());
|
||||
void list_admin(program prog, std::ostream& out)
|
||||
{
|
||||
if (!prog.arguments.empty())
|
||||
throw std::runtime_error{"list_admin takes zero arguments"};
|
||||
|
||||
using value_range = boost::iterator_range<lmdb::value_iterator<lws::db::account>>;
|
||||
const auto transform = [] (value_range user)
|
||||
{ return admin_display<value_range>{std::move(user)}; };
|
||||
|
||||
auto reader = MONERO_UNWRAP(prog.disk.start_read());
|
||||
wire::json_stream_writer json{out};
|
||||
wire::dynamic_object(
|
||||
json, reader.get_accounts().value().make_range(), wire::enum_as_string, transform
|
||||
);
|
||||
json.finish();
|
||||
}
|
||||
|
||||
void list_requests(program prog, std::ostream& out)
|
||||
{
|
||||
if (!prog.arguments.empty())
|
||||
throw std::runtime_error{"list_requests takes zero arguments"};
|
||||
|
||||
auto reader = prog.disk.start_read().value();
|
||||
auto stream = reader.get_requests().value();
|
||||
stream_json_object(out, stream.make_range());
|
||||
run_command(lws::rpc::list_requests, out, std::move(prog.disk));
|
||||
}
|
||||
|
||||
void modify_account(program prog, std::ostream& out)
|
||||
@@ -220,15 +229,11 @@ namespace
|
||||
if (prog.arguments.size() < 2)
|
||||
throw std::runtime_error{"modify_account_status requires 2 or more arguments"};
|
||||
|
||||
const lws::db::account_status status =
|
||||
lws::db::account_status_from_string(prog.arguments[0]).value();
|
||||
std::vector<lws::db::account_address> addresses =
|
||||
get_addresses(epee::to_span(prog.arguments));
|
||||
|
||||
const std::vector<lws::db::account_address> updated =
|
||||
prog.disk.change_status(status, epee::to_span(addresses)).value();
|
||||
|
||||
write_json_addresses(out, epee::to_span(updated));
|
||||
lws::rpc::modify_account_req req{
|
||||
get_addresses(epee::to_span(prog.arguments)),
|
||||
lws::db::account_status_from_string(prog.arguments[0]).value()
|
||||
};
|
||||
run_command(lws::rpc::modify_account, out, std::move(prog.disk), std::move(req));
|
||||
}
|
||||
|
||||
void reject_requests(program prog, std::ostream& out)
|
||||
@@ -236,12 +241,11 @@ namespace
|
||||
if (prog.arguments.size() < 2)
|
||||
MONERO_THROW(common_error::kInvalidArgument, "reject_requests requires 2 or more arguments");
|
||||
|
||||
const lws::db::request req =
|
||||
lws::db::request_from_string(prog.arguments[0]).value();
|
||||
std::vector<lws::db::account_address> addresses =
|
||||
get_addresses(epee::to_span(prog.arguments));
|
||||
|
||||
MONERO_UNWRAP(prog.disk.reject_requests(req, epee::to_span(addresses)));
|
||||
lws::rpc::address_requests req{
|
||||
get_addresses(epee::to_span(prog.arguments)),
|
||||
lws::db::request_from_string(prog.arguments[0]).value()
|
||||
};
|
||||
run_command(lws::rpc::reject_requests, out, std::move(prog.disk), std::move(req));
|
||||
}
|
||||
|
||||
void rescan(program prog, std::ostream& out)
|
||||
@@ -249,14 +253,11 @@ namespace
|
||||
if (prog.arguments.size() < 2)
|
||||
throw std::runtime_error{"rescan requires 2 or more arguments"};
|
||||
|
||||
const auto height = lws::db::block_id(std::stoull(prog.arguments[0]));
|
||||
const std::vector<lws::db::account_address> addresses =
|
||||
get_addresses(epee::to_span(prog.arguments));
|
||||
|
||||
const std::vector<lws::db::account_address> updated =
|
||||
prog.disk.rescan(height, epee::to_span(addresses)).value();
|
||||
|
||||
write_json_addresses(out, epee::to_span(updated));
|
||||
lws::rpc::rescan_req req{
|
||||
get_addresses(epee::to_span(prog.arguments)),
|
||||
lws::db::block_id(std::stoull(prog.arguments[0]))
|
||||
};
|
||||
run_command(lws::rpc::rescan, out, std::move(prog.disk), std::move(req));
|
||||
}
|
||||
|
||||
void rollback(program prog, std::ostream& out)
|
||||
@@ -277,14 +278,16 @@ namespace
|
||||
char const* const name;
|
||||
void (*const handler)(program, std::ostream&);
|
||||
char const* const parameters;
|
||||
};
|
||||
};
|
||||
|
||||
static constexpr const command commands[] =
|
||||
{
|
||||
{"accept_requests", &accept_requests, "<\"create\"|\"import\"> <base58 address> [base 58 address]..."},
|
||||
{"add_account", &add_account, "<base58 address> <view key hex>"},
|
||||
{"create_admin", &create_admin, ""},
|
||||
{"debug_database", &debug_database, ""},
|
||||
{"list_accounts", &list_accounts, ""},
|
||||
{"list_admin", &list_admin, ""},
|
||||
{"list_requests", &list_requests, ""},
|
||||
{"modify_account_status", &modify_account, "<\"active\"|\"inactive\"|\"hidden\"> <base58 address> [base 58 address]..."},
|
||||
{"reject_requests", &reject_requests, "<\"create\"|\"import\"> <base58 address> [base 58 address]..."},
|
||||
|
||||
Reference in New Issue
Block a user