forked from such-gitea/wownero-lws
Add /get_version, based on openmonero with a few extra additions (#209)
This commit is contained in:
committed by
Lee *!* Clagett
parent
c65a1f488b
commit
8cf0976557
@@ -28,6 +28,35 @@
|
|||||||
|
|
||||||
include_directories(.)
|
include_directories(.)
|
||||||
|
|
||||||
|
find_package(Git)
|
||||||
|
|
||||||
|
set(MLWS_COMMIT_BRANCH "")
|
||||||
|
set(MLWS_COMMIT_DATE "")
|
||||||
|
set(MLWS_COMMIT_HASH "")
|
||||||
|
if (GIT_FOUND)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD
|
||||||
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||||
|
OUTPUT_VARIABLE MLWS_COMMIT_HASH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" rev-parse --abbrev-ref HEAD
|
||||||
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||||
|
OUTPUT_VARIABLE MLWS_COMMIT_BRANCH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" log -1 "--date=format:\"%Y/%m/%d %T\"" "--format=\"%ad\""
|
||||||
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||||
|
OUTPUT_VARIABLE MLWS_COMMIT_DATE
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
configure_file(lws_version.h.in "${CMAKE_BINARY_DIR}/generated_include/lws_version.h")
|
||||||
|
include_directories("${CMAKE_BINARY_DIR}/generated_include")
|
||||||
|
|
||||||
add_subdirectory(lmdb)
|
add_subdirectory(lmdb)
|
||||||
add_subdirectory(wire)
|
add_subdirectory(wire)
|
||||||
add_subdirectory(db)
|
add_subdirectory(db)
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ namespace lws
|
|||||||
return "Invalid blockchain height";
|
return "Invalid blockchain height";
|
||||||
case error::bad_url:
|
case error::bad_url:
|
||||||
return "Invlaid URL";
|
return "Invlaid URL";
|
||||||
|
case error::bad_verb:
|
||||||
|
return "Incorrect HTTP verb provided";
|
||||||
case error::bad_webhook:
|
case error::bad_webhook:
|
||||||
return "Invalid webhook request";
|
return "Invalid webhook request";
|
||||||
case error::blockchain_reorg:
|
case error::blockchain_reorg:
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ namespace lws
|
|||||||
bad_daemon_response, //!< RPC Response from daemon was invalid
|
bad_daemon_response, //!< RPC Response from daemon was invalid
|
||||||
bad_height, //!< Invalid blockchain height
|
bad_height, //!< Invalid blockchain height
|
||||||
bad_url, //!< Invalid URL
|
bad_url, //!< Invalid URL
|
||||||
|
bad_verb, //!< Bad HTTP verb for endpoint
|
||||||
bad_webhook, //!< Invalid webhook request
|
bad_webhook, //!< Invalid webhook request
|
||||||
blockchain_reorg, //!< Blockchain reorg after fetching/scanning block(s)
|
blockchain_reorg, //!< Blockchain reorg after fetching/scanning block(s)
|
||||||
configuration, //!< Process configuration invalid
|
configuration, //!< Process configuration invalid
|
||||||
|
|||||||
47
src/lws_version.h.in
Normal file
47
src/lws_version.h.in
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2025, The Monero Project
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace lws { namespace version
|
||||||
|
{
|
||||||
|
constexpr const char branch[] = "@MLWS_COMMIT_BRANCH@";
|
||||||
|
constexpr const char commit[] = "@MLWS_COMMIT_HASH@";
|
||||||
|
constexpr const char date[] = "@MLWS_COMMIT_DATE@";
|
||||||
|
constexpr const char id[] = "0.4-alpha";
|
||||||
|
constexpr const char name[] = "monero-lws";
|
||||||
|
|
||||||
|
// openmonero is currently on 1.6 and we have multiple additions since then
|
||||||
|
namespace api
|
||||||
|
{
|
||||||
|
constexpr const std::uint16_t major = 1;
|
||||||
|
constexpr const std::uint16_t minor = 7;
|
||||||
|
constexpr const std::uint32_t combined = std::uint32_t(major) << 16 | minor;
|
||||||
|
}
|
||||||
|
}} // lws // version
|
||||||
|
|
||||||
@@ -159,10 +159,11 @@ namespace lws
|
|||||||
struct connection_data
|
struct connection_data
|
||||||
{
|
{
|
||||||
rest_server_data* const global; //!< Valid for lifetime of server
|
rest_server_data* const global; //!< Valid for lifetime of server
|
||||||
|
boost::beast::http::verb last_verb;
|
||||||
bool passed_login; //!< True iff a login via viewkey was successful
|
bool passed_login; //!< True iff a login via viewkey was successful
|
||||||
|
|
||||||
explicit connection_data(rest_server_data* global) noexcept
|
explicit connection_data(rest_server_data* global) noexcept
|
||||||
: global(global), passed_login(false)
|
: global(global), last_verb(boost::beast::http::verb::unknown), passed_login(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
//! \return Next request timeout, based on login status
|
//! \return Next request timeout, based on login status
|
||||||
@@ -300,7 +301,7 @@ namespace lws
|
|||||||
using response = epee::byte_slice; // sometimes async
|
using response = epee::byte_slice; // sometimes async
|
||||||
using async_response = rpc::daemon_status_response;
|
using async_response = rpc::daemon_status_response;
|
||||||
|
|
||||||
static expect<response> handle(const request&, const connection_data& data, std::function<async_complete>&& resume)
|
static expect<response> handle(request, const connection_data& data, std::function<async_complete>&& resume)
|
||||||
{
|
{
|
||||||
using info_rpc = cryptonote::rpc::GetInfo;
|
using info_rpc = cryptonote::rpc::GetInfo;
|
||||||
|
|
||||||
@@ -1303,6 +1304,33 @@ namespace lws
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct get_version
|
||||||
|
{
|
||||||
|
using request = rpc::get_version_request;
|
||||||
|
using response = rpc::get_version_response;
|
||||||
|
|
||||||
|
static expect<response> handle(request, const connection_data& data, std::function<async_complete>&&)
|
||||||
|
{
|
||||||
|
lws::db::block_id height{};
|
||||||
|
{
|
||||||
|
auto reader = data.global->disk.start_read();
|
||||||
|
if (reader)
|
||||||
|
{
|
||||||
|
auto db_height = reader->get_last_block();
|
||||||
|
if (db_height)
|
||||||
|
height = db_height->id;
|
||||||
|
else
|
||||||
|
MWARNING("Failed to get DB height: " << db_height.error().message());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
MWARNING("Failed to start db reader: " << reader.error().message());
|
||||||
|
}
|
||||||
|
|
||||||
|
// response constructor fills remaining fields
|
||||||
|
return response{height, data.global->options.max_subaddresses};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct import_request
|
struct import_request
|
||||||
{
|
{
|
||||||
using request = rpc::import_request;
|
using request = rpc::import_request;
|
||||||
@@ -1710,11 +1738,16 @@ namespace lws
|
|||||||
throw std::logic_error{"async REST handler not setup properly"};
|
throw std::logic_error{"async REST handler not setup properly"};
|
||||||
if (std::is_same<epee::byte_slice, response>() && !resume)
|
if (std::is_same<epee::byte_slice, response>() && !resume)
|
||||||
throw std::logic_error{"async REST handler not setup properly"};
|
throw std::logic_error{"async REST handler not setup properly"};
|
||||||
|
|
||||||
request req{};
|
request req{};
|
||||||
std::error_code error = wire::json::from_bytes(std::move(root), req);
|
if (!std::is_empty<request>())
|
||||||
if (error)
|
{
|
||||||
return error;
|
if (data.last_verb != boost::beast::http::verb::post)
|
||||||
|
return {error::bad_verb};
|
||||||
|
std::error_code error = wire::json::from_bytes(std::move(root), req);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
expect<response> resp = E::handle(std::move(req), data, std::move(resume));
|
expect<response> resp = E::handle(std::move(req), data, std::move(resume));
|
||||||
if (!resp)
|
if (!resp)
|
||||||
@@ -1744,6 +1777,9 @@ namespace lws
|
|||||||
expect<epee::byte_slice> call_admin(std::string&& root, connection_data& data, std::function<async_complete>&&)
|
expect<epee::byte_slice> call_admin(std::string&& root, connection_data& data, std::function<async_complete>&&)
|
||||||
{
|
{
|
||||||
using request = typename E::request;
|
using request = typename E::request;
|
||||||
|
|
||||||
|
if (data.last_verb != boost::beast::http::verb::post)
|
||||||
|
return {error::bad_verb};
|
||||||
|
|
||||||
admin<request> req{};
|
admin<request> req{};
|
||||||
{
|
{
|
||||||
@@ -1804,6 +1840,7 @@ namespace lws
|
|||||||
{"/get_subaddrs", call<get_subaddrs>, 2 * 1024, false},
|
{"/get_subaddrs", call<get_subaddrs>, 2 * 1024, false},
|
||||||
{"/get_txt_records", nullptr, 0, false},
|
{"/get_txt_records", nullptr, 0, false},
|
||||||
{"/get_unspent_outs", call<get_unspent_outs>, 2 * 1024, true},
|
{"/get_unspent_outs", call<get_unspent_outs>, 2 * 1024, true},
|
||||||
|
{"/get_version", call<get_version>, 1024, false},
|
||||||
{"/import_wallet_request", call<import_request>, 2 * 1024, false},
|
{"/import_wallet_request", call<import_request>, 2 * 1024, false},
|
||||||
{"/login", call<login>, 2 * 1024, false},
|
{"/login", call<login>, 2 * 1024, false},
|
||||||
{"/provision_subaddrs", call<provision_subaddrs>, 2 * 1024, false},
|
{"/provision_subaddrs", call<provision_subaddrs>, 2 * 1024, false},
|
||||||
@@ -2010,6 +2047,8 @@ namespace lws
|
|||||||
assert(strand_.running_in_this_thread());
|
assert(strand_.running_in_this_thread());
|
||||||
if (error.category() == wire::error::rapidjson_category() || error == lws::error::invalid_range || error == lws::error::not_enough_amount)
|
if (error.category() == wire::error::rapidjson_category() || error == lws::error::invalid_range || error == lws::error::not_enough_amount)
|
||||||
return bad_request(boost::beast::http::status::bad_request, std::forward<F>(resume));
|
return bad_request(boost::beast::http::status::bad_request, std::forward<F>(resume));
|
||||||
|
else if (error == lws::error::bad_verb)
|
||||||
|
return bad_request(boost::beast::http::status::method_not_allowed, std::forward<F>(resume));
|
||||||
else if (error == lws::error::account_not_found || error == lws::error::duplicate_request)
|
else if (error == lws::error::account_not_found || error == lws::error::duplicate_request)
|
||||||
return bad_request(boost::beast::http::status::forbidden, std::forward<F>(resume));
|
return bad_request(boost::beast::http::status::forbidden, std::forward<F>(resume));
|
||||||
else if (error == lws::error::max_subaddresses)
|
else if (error == lws::error::max_subaddresses)
|
||||||
@@ -2119,7 +2158,8 @@ namespace lws
|
|||||||
return self_->bad_request(boost::beast::http::status::bad_request, std::forward<F>(resume));
|
return self_->bad_request(boost::beast::http::status::bad_request, std::forward<F>(resume));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self_->parser_->get().method() != boost::beast::http::verb::post)
|
const boost::beast::http::verb verb = self_->parser_->get().method();
|
||||||
|
if (verb != boost::beast::http::verb::post && verb != boost::beast::http::verb::get)
|
||||||
return self_->bad_request(boost::beast::http::status::method_not_allowed, std::forward<F>(resume));
|
return self_->bad_request(boost::beast::http::status::method_not_allowed, std::forward<F>(resume));
|
||||||
|
|
||||||
std::function<async_complete> resumer;
|
std::function<async_complete> resumer;
|
||||||
@@ -2143,6 +2183,7 @@ namespace lws
|
|||||||
}
|
}
|
||||||
|
|
||||||
MDEBUG("Running REST handler " << handler->name << " on " << self_.get());
|
MDEBUG("Running REST handler " << handler->name << " on " << self_.get());
|
||||||
|
self_->data_.last_verb = verb;
|
||||||
auto body = handler->run(std::move(self_->parser_->get()).body(), self_->data_, std::move(resumer));
|
auto body = handler->run(std::move(self_->parser_->get()).body(), self_->data_, std::move(resumer));
|
||||||
if (!body)
|
if (!body)
|
||||||
return self_->bad_request(body.error(), std::forward<F>(resume));
|
return self_->bad_request(body.error(), std::forward<F>(resume));
|
||||||
|
|||||||
@@ -37,10 +37,12 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "db/string.h"
|
#include "db/string.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "lws_version.h"
|
||||||
#include "time_helper.h" // monero/contrib/epee/include
|
#include "time_helper.h" // monero/contrib/epee/include
|
||||||
#include "ringct/rctOps.h" // monero/src
|
#include "ringct/rctOps.h" // monero/src
|
||||||
#include "span.h" // monero/contrib/epee/include
|
#include "span.h" // monero/contrib/epee/include
|
||||||
#include "util/random_outputs.h"
|
#include "util/random_outputs.h"
|
||||||
|
#include "version.h" // monero/src
|
||||||
#include "wire.h"
|
#include "wire.h"
|
||||||
#include "wire/adapted/crypto.h"
|
#include "wire/adapted/crypto.h"
|
||||||
#include "wire/error.h"
|
#include "wire/error.h"
|
||||||
@@ -362,6 +364,36 @@ namespace lws
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc::get_version_response::get_version_response(const db::block_id height, const std::uint32_t max_subaddresses)
|
||||||
|
: server_type(lws::version::name),
|
||||||
|
server_version(lws::version::id),
|
||||||
|
last_git_commit_hash(lws::version::commit),
|
||||||
|
last_git_commit_date(lws::version::date),
|
||||||
|
git_branch_name(lws::version::branch),
|
||||||
|
monero_version_full(MONERO_VERSION_FULL),
|
||||||
|
blockchain_height(height),
|
||||||
|
api(lws::version::api::combined),
|
||||||
|
max_subaddresses(max_subaddresses),
|
||||||
|
network(lws::rpc::network_type(lws::config::network)),
|
||||||
|
testnet(config::network == cryptonote::TESTNET)
|
||||||
|
{}
|
||||||
|
void rpc::write_bytes(wire::json_writer& dest, const get_version_response& self)
|
||||||
|
{
|
||||||
|
wire::object(dest,
|
||||||
|
WIRE_FIELD(server_type),
|
||||||
|
WIRE_FIELD(server_version),
|
||||||
|
WIRE_FIELD(last_git_commit_hash),
|
||||||
|
WIRE_FIELD(last_git_commit_date),
|
||||||
|
WIRE_FIELD(git_branch_name),
|
||||||
|
WIRE_FIELD(monero_version_full),
|
||||||
|
WIRE_FIELD_COPY(blockchain_height),
|
||||||
|
WIRE_FIELD(api),
|
||||||
|
WIRE_FIELD_COPY(max_subaddresses),
|
||||||
|
wire::field("network_type", self.network),
|
||||||
|
WIRE_FIELD_COPY(testnet)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void rpc::read_bytes(wire::json_reader& source, import_request& self)
|
void rpc::read_bytes(wire::json_reader& source, import_request& self)
|
||||||
{
|
{
|
||||||
std::string address;
|
std::string address;
|
||||||
|
|||||||
@@ -224,6 +224,35 @@ namespace rpc
|
|||||||
void write_bytes(wire::json_writer&, const get_subaddrs_response&);
|
void write_bytes(wire::json_writer&, const get_subaddrs_response&);
|
||||||
|
|
||||||
|
|
||||||
|
struct get_version_request
|
||||||
|
{
|
||||||
|
get_version_request() = delete;
|
||||||
|
};
|
||||||
|
inline void read_bytes(const wire::reader&, const get_version_request&)
|
||||||
|
{}
|
||||||
|
|
||||||
|
struct get_version_response
|
||||||
|
{
|
||||||
|
//! Defaults to current network in unavailable state
|
||||||
|
get_version_response(lws::db::block_id height, std::uint32_t max_subaddresses);
|
||||||
|
|
||||||
|
|
||||||
|
const std::string server_type;
|
||||||
|
const std::string server_version;
|
||||||
|
const std::string last_git_commit_hash;
|
||||||
|
const std::string last_git_commit_date;
|
||||||
|
const std::string git_branch_name;
|
||||||
|
const std::string monero_version_full;
|
||||||
|
|
||||||
|
const db::block_id blockchain_height;
|
||||||
|
const std::uint32_t api;
|
||||||
|
const std::uint32_t max_subaddresses;
|
||||||
|
const network_type network;
|
||||||
|
const bool testnet;
|
||||||
|
};
|
||||||
|
void write_bytes(wire::json_writer&, const get_version_response&);
|
||||||
|
|
||||||
|
|
||||||
struct import_request
|
struct import_request
|
||||||
{
|
{
|
||||||
import_request() = delete;
|
import_request() = delete;
|
||||||
|
|||||||
Reference in New Issue
Block a user