mirror of
https://codeberg.org/wownero/wownero-lws
synced 2026-01-10 15:45:15 -08:00
Initial working base separated from top-level monero project
This commit is contained in:
33
src/rpc/CMakeLists.txt
Normal file
33
src/rpc/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2020, 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.
|
||||
|
||||
set(monero-lws-rpc_sources client.cpp daemon_zmq.cpp light_wallet.cpp rates.cpp)
|
||||
set(monero-lws-rpc_headers client.h daemon_zmq.h fwd.h json.h light_wallet.h rates.h)
|
||||
|
||||
add_library(monero-lws-rpc ${monero-lws-rpc_sources} ${monero-lws-rpc_headers})
|
||||
target_link_libraries(monero-lws-rpc monero::libraries monero-lws-wire-json)
|
||||
325
src/rpc/client.cpp
Normal file
325
src/rpc/client.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
// Copyright (c) 2018-2020, 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.
|
||||
|
||||
#include "client.h"
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <cassert>
|
||||
#include <system_error>
|
||||
|
||||
#include "common/error.h" // monero/contrib/epee/include
|
||||
#include "error.h"
|
||||
#include "net/http_client.h" // monero/contrib/epee/include/net
|
||||
#include "net/zmq.h" // monero/src
|
||||
|
||||
namespace lws
|
||||
{
|
||||
namespace rpc
|
||||
{
|
||||
namespace http = epee::net_utils::http;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const char signal_endpoint[] = "inproc://signal";
|
||||
constexpr const char abort_scan_signal[] = "SCAN";
|
||||
constexpr const char abort_process_signal[] = "PROCESS";
|
||||
constexpr const int daemon_zmq_linger = 0;
|
||||
|
||||
struct terminate
|
||||
{
|
||||
void operator()(void* ptr) const noexcept
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
while (zmq_term(ptr))
|
||||
{
|
||||
if (zmq_errno() != EINTR)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
using zcontext = std::unique_ptr<void, terminate>;
|
||||
|
||||
expect<void> do_wait(void* daemon, void* signal_sub, short events, std::chrono::milliseconds timeout) noexcept
|
||||
{
|
||||
if (timeout <= std::chrono::seconds{0})
|
||||
return {lws::error::daemon_timeout};
|
||||
|
||||
zmq_pollitem_t items[2] {
|
||||
{daemon, 0, short(events | ZMQ_POLLERR), 0},
|
||||
{signal_sub, 0, short(ZMQ_POLLIN | ZMQ_POLLERR), 0}
|
||||
};
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
const int ready = zmq_poll(items, 2, timeout.count());
|
||||
const auto end = std::chrono::steady_clock::now();
|
||||
const auto spent = std::chrono::duration_cast<std::chrono::milliseconds>(start - end);
|
||||
timeout -= std::min(spent, timeout);
|
||||
|
||||
if (ready == 0)
|
||||
return {lws::error::daemon_timeout};
|
||||
if (0 < ready)
|
||||
break;
|
||||
const int err = zmq_errno();
|
||||
if (err != EINTR)
|
||||
return net::zmq::make_error_code(err);
|
||||
}
|
||||
if (items[0].revents)
|
||||
return success();
|
||||
|
||||
char buf[1];
|
||||
MONERO_ZMQ_CHECK(zmq_recv(signal_sub, buf, 1, 0));
|
||||
|
||||
switch (buf[0])
|
||||
{
|
||||
case 'P':
|
||||
return {lws::error::signal_abort_process};
|
||||
case 'S':
|
||||
return {lws::error::signal_abort_scan};
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {lws::error::signal_unknown};
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
expect<void> do_signal(void* signal_pub, const char (&signal)[N]) noexcept
|
||||
{
|
||||
MONERO_ZMQ_CHECK(zmq_send(signal_pub, signal, sizeof(signal), 0));
|
||||
return success();
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
expect<void> do_subscribe(void* signal_sub, const char (&signal)[N]) noexcept
|
||||
{
|
||||
MONERO_ZMQ_CHECK(zmq_setsockopt(signal_sub, ZMQ_SUBSCRIBE, signal, sizeof(signal)));
|
||||
return success();
|
||||
}
|
||||
} // anonymous
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct context
|
||||
{
|
||||
explicit context(zcontext comm, socket signal_pub, std::string daemon_addr, std::chrono::minutes interval)
|
||||
: comm(std::move(comm))
|
||||
, signal_pub(std::move(signal_pub))
|
||||
, daemon_addr(std::move(daemon_addr))
|
||||
, rates_conn()
|
||||
, cache_time()
|
||||
, cache_interval(interval)
|
||||
, cached{}
|
||||
, sync_rates()
|
||||
{
|
||||
if (std::chrono::minutes{0} < cache_interval)
|
||||
rates_conn.set_server(crypto_compare.host, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_enabled);
|
||||
}
|
||||
|
||||
zcontext comm;
|
||||
socket signal_pub;
|
||||
std::string daemon_addr;
|
||||
http::http_simple_client rates_conn;
|
||||
std::chrono::steady_clock::time_point cache_time;
|
||||
const std::chrono::minutes cache_interval;
|
||||
rates cached;
|
||||
boost::mutex sync_rates;
|
||||
};
|
||||
} // detail
|
||||
|
||||
expect<std::string> client::get_message(std::chrono::seconds timeout)
|
||||
{
|
||||
MONERO_PRECOND(ctx != nullptr);
|
||||
assert(daemon != nullptr);
|
||||
assert(signal_sub != nullptr);
|
||||
|
||||
expect<std::string> msg{common_error::kInvalidArgument};
|
||||
while (!(msg = net::zmq::receive(daemon.get(), ZMQ_DONTWAIT)))
|
||||
{
|
||||
if (msg != net::zmq::make_error_code(EAGAIN))
|
||||
break;
|
||||
|
||||
MONERO_CHECK(do_wait(daemon.get(), signal_sub.get(), ZMQ_POLLIN, timeout));
|
||||
timeout = std::chrono::seconds{0};
|
||||
}
|
||||
// std::string move constructor is noexcept
|
||||
return msg;
|
||||
}
|
||||
|
||||
expect<client> client::make(std::shared_ptr<detail::context> ctx) noexcept
|
||||
{
|
||||
MONERO_PRECOND(ctx != nullptr);
|
||||
|
||||
const int linger = daemon_zmq_linger;
|
||||
client out{std::move(ctx)};
|
||||
|
||||
out.daemon.reset(zmq_socket(out.ctx->comm.get(), ZMQ_REQ));
|
||||
if (out.daemon.get() == nullptr)
|
||||
return net::zmq::get_error_code();
|
||||
MONERO_ZMQ_CHECK(zmq_connect(out.daemon.get(), out.ctx->daemon_addr.c_str()));
|
||||
MONERO_ZMQ_CHECK(zmq_setsockopt(out.daemon.get(), ZMQ_LINGER, &linger, sizeof(linger)));
|
||||
|
||||
out.signal_sub.reset(zmq_socket(out.ctx->comm.get(), ZMQ_SUB));
|
||||
if (out.signal_sub.get() == nullptr)
|
||||
return net::zmq::get_error_code();
|
||||
MONERO_ZMQ_CHECK(zmq_connect(out.signal_sub.get(), signal_endpoint));
|
||||
|
||||
MONERO_CHECK(do_subscribe(out.signal_sub.get(), abort_process_signal));
|
||||
return {std::move(out)};
|
||||
}
|
||||
|
||||
client::~client() noexcept
|
||||
{}
|
||||
|
||||
expect<void> client::watch_scan_signals() noexcept
|
||||
{
|
||||
MONERO_PRECOND(ctx != nullptr);
|
||||
assert(signal_sub != nullptr);
|
||||
return do_subscribe(signal_sub.get(), abort_scan_signal);
|
||||
}
|
||||
|
||||
expect<void> client::wait(std::chrono::seconds timeout) noexcept
|
||||
{
|
||||
MONERO_PRECOND(ctx != nullptr);
|
||||
assert(daemon != nullptr);
|
||||
assert(signal_sub != nullptr);
|
||||
return do_wait(daemon.get(), signal_sub.get(), 0, timeout);
|
||||
}
|
||||
|
||||
expect<void> client::send(epee::byte_slice message, std::chrono::seconds timeout) noexcept
|
||||
{
|
||||
MONERO_PRECOND(ctx != nullptr);
|
||||
assert(daemon != nullptr);
|
||||
assert(signal_sub != nullptr);
|
||||
|
||||
expect<void> sent;
|
||||
while (!(sent = net::zmq::send(message.clone(), daemon.get(), ZMQ_DONTWAIT)))
|
||||
{
|
||||
if (sent != net::zmq::make_error_code(EAGAIN))
|
||||
return sent.error();
|
||||
|
||||
MONERO_CHECK(do_wait(daemon.get(), signal_sub.get(), ZMQ_POLLOUT, timeout));
|
||||
timeout = std::chrono::seconds{0};
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
expect<rates> client::get_rates() const
|
||||
{
|
||||
MONERO_PRECOND(ctx != nullptr);
|
||||
if (ctx->cache_interval <= std::chrono::minutes{0})
|
||||
return {lws::error::exchange_rates_disabled};
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const boost::unique_lock<boost::mutex> lock{ctx->sync_rates};
|
||||
if (now - ctx->cache_time >= ctx->cache_interval + std::chrono::seconds{30})
|
||||
return {lws::error::exchange_rates_old};
|
||||
return ctx->cached;
|
||||
}
|
||||
|
||||
context context::make(std::string daemon_addr, std::chrono::minutes rates_interval)
|
||||
{
|
||||
zcontext comm{zmq_init(1)};
|
||||
if (comm == nullptr)
|
||||
MONERO_THROW(net::zmq::get_error_code(), "zmq_init");
|
||||
|
||||
detail::socket pub{zmq_socket(comm.get(), ZMQ_PUB)};
|
||||
if (pub == nullptr)
|
||||
MONERO_THROW(net::zmq::get_error_code(), "zmq_socket");
|
||||
if (zmq_bind(pub.get(), signal_endpoint) < 0)
|
||||
MONERO_THROW(net::zmq::get_error_code(), "zmq_bind");
|
||||
|
||||
return context{
|
||||
std::make_shared<detail::context>(
|
||||
std::move(comm), std::move(pub), std::move(daemon_addr), rates_interval
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
context::~context() noexcept
|
||||
{
|
||||
if (ctx)
|
||||
raise_abort_process();
|
||||
}
|
||||
|
||||
std::string const& context::daemon_address() const
|
||||
{
|
||||
if (ctx == nullptr)
|
||||
MONERO_THROW(common_error::kInvalidArgument, "Invalid lws::rpc::context");
|
||||
return ctx->daemon_addr;
|
||||
}
|
||||
|
||||
expect<void> context::raise_abort_scan() noexcept
|
||||
{
|
||||
MONERO_PRECOND(ctx != nullptr);
|
||||
assert(ctx->signal_pub != nullptr);
|
||||
return do_signal(ctx->signal_pub.get(), abort_scan_signal);
|
||||
}
|
||||
|
||||
expect<void> context::raise_abort_process() noexcept
|
||||
{
|
||||
MONERO_PRECOND(ctx != nullptr);
|
||||
assert(ctx->signal_pub != nullptr);
|
||||
return do_signal(ctx->signal_pub.get(), abort_process_signal);
|
||||
}
|
||||
|
||||
expect<boost::optional<lws::rates>> context::retrieve_rates()
|
||||
{
|
||||
MONERO_PRECOND(ctx != nullptr);
|
||||
|
||||
if (ctx->cache_interval <= std::chrono::minutes{0})
|
||||
return boost::make_optional(false, ctx->cached);
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now - ctx->cache_time < ctx->cache_interval)
|
||||
return boost::make_optional(false, ctx->cached);
|
||||
|
||||
expect<rates> fresh{lws::error::exchange_rates_fetch};
|
||||
|
||||
const http::http_response_info* info = nullptr;
|
||||
const bool retrieved =
|
||||
ctx->rates_conn.invoke_get(crypto_compare.path, std::chrono::seconds{20}, std::string{}, std::addressof(info)) &&
|
||||
info != nullptr &&
|
||||
info->m_response_code == 200;
|
||||
|
||||
// \TODO Remove copy below
|
||||
if (retrieved)
|
||||
fresh = crypto_compare(std::string{info->m_body});
|
||||
|
||||
const boost::unique_lock<boost::mutex> lock{ctx->sync_rates};
|
||||
ctx->cache_time = now;
|
||||
if (fresh)
|
||||
{
|
||||
ctx->cached = *fresh;
|
||||
return boost::make_optional(*fresh);
|
||||
}
|
||||
return fresh.error();
|
||||
}
|
||||
} // rpc
|
||||
} // lws
|
||||
219
src/rpc/client.h
Normal file
219
src/rpc/client.h
Normal file
@@ -0,0 +1,219 @@
|
||||
// Copyright (c) 2018-2020, 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 <boost/optional/optional.hpp>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <zmq.h>
|
||||
|
||||
#include "byte_slice.h" // monero/contrib/epee/include
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "rates.h"
|
||||
#include "rpc/message.h" // monero/src
|
||||
|
||||
namespace lws
|
||||
{
|
||||
namespace rpc
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
struct close
|
||||
{
|
||||
void operator()(void* ptr) const noexcept
|
||||
{
|
||||
if (ptr)
|
||||
zmq_close(ptr);
|
||||
}
|
||||
};
|
||||
using socket = std::unique_ptr<void, close>;
|
||||
|
||||
struct context;
|
||||
}
|
||||
|
||||
//! Abstraction for ZMQ RPC client. Only `get_rates()` thread-safe; use `clone()`.
|
||||
class client
|
||||
{
|
||||
std::shared_ptr<detail::context> ctx;
|
||||
detail::socket daemon;
|
||||
detail::socket signal_sub;
|
||||
|
||||
explicit client(std::shared_ptr<detail::context> ctx)
|
||||
: ctx(std::move(ctx)), daemon(), signal_sub()
|
||||
{}
|
||||
|
||||
public:
|
||||
//! A client with no connection (all send/receive functions fail).
|
||||
explicit client() noexcept
|
||||
: ctx(), daemon(), signal_sub()
|
||||
{}
|
||||
|
||||
static expect<client> make(std::shared_ptr<detail::context> ctx) noexcept;
|
||||
|
||||
client(client&&) = default;
|
||||
client(client const&) = delete;
|
||||
|
||||
~client() noexcept;
|
||||
|
||||
client& operator=(client&&) = default;
|
||||
client& operator=(client const&) = delete;
|
||||
|
||||
/*!
|
||||
\note `watch_scan_signals()` status is not cloned.
|
||||
\note The copy is not cheap - it creates a new ZMQ socket.
|
||||
\return A client connected to same daemon as `this`.
|
||||
*/
|
||||
expect<client> clone() const noexcept
|
||||
{
|
||||
return make(ctx);
|
||||
}
|
||||
|
||||
//! \return True if `this` is valid (i.e. not default or moved from).
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return ctx != nullptr;
|
||||
}
|
||||
|
||||
//! `wait`, `send`, and `receive` will watch for `raise_abort_scan()`.
|
||||
expect<void> watch_scan_signals() noexcept;
|
||||
|
||||
//! Block until `timeout` or until `context::stop()` is invoked.
|
||||
expect<void> wait(std::chrono::seconds timeout) noexcept;
|
||||
|
||||
//! \return A JSON message for RPC request `M`.
|
||||
template<typename M>
|
||||
static epee::byte_slice make_message(char const* const name, const M& message)
|
||||
{
|
||||
return cryptonote::rpc::FullMessage::getRequest(name, message, 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
Queue `message` for sending to daemon. If the queue is full, wait a
|
||||
maximum of `timeout` seconds or until `context::raise_abort_scan` or
|
||||
`context::raise_abort_process()` is called.
|
||||
*/
|
||||
expect<void> send(epee::byte_slice message, std::chrono::seconds timeout) noexcept;
|
||||
|
||||
//! \return Next available RPC message response from server
|
||||
expect<std::string> get_message(std::chrono::seconds timeout);
|
||||
|
||||
//! \return RPC response `M`, waiting a max of `timeout` seconds.
|
||||
template<typename M>
|
||||
expect<M> receive(std::chrono::seconds timeout)
|
||||
{
|
||||
expect<std::string> message = get_message(timeout);
|
||||
if (!message)
|
||||
return message.error();
|
||||
|
||||
cryptonote::rpc::FullMessage fm{std::move(*message)};
|
||||
M out{};
|
||||
out.fromJson(fm.getMessage());
|
||||
return out;
|
||||
}
|
||||
|
||||
/*!
|
||||
\note This is the one function that IS thread-safe. Multiple threads can
|
||||
call this function with the same `this` argument.
|
||||
|
||||
\return Recent exchange rates.
|
||||
*/
|
||||
expect<rates> get_rates() const;
|
||||
};
|
||||
|
||||
//! Owns ZMQ context, and ZMQ PUB socket for signalling child `client`s.
|
||||
class context
|
||||
{
|
||||
std::shared_ptr<detail::context> ctx;
|
||||
|
||||
explicit context(std::shared_ptr<detail::context> ctx)
|
||||
: ctx(std::move(ctx))
|
||||
{}
|
||||
|
||||
public:
|
||||
/*! Use `daemon_addr` for call child client objects.
|
||||
|
||||
\throw std::bad_alloc if internal `shared_ptr` allocation failed.
|
||||
\throw std::system_error if any ZMQ errors occur.
|
||||
|
||||
\note All errors are exceptions; no recovery can occur.
|
||||
|
||||
\param daemon_addr Location of ZMQ enabled `monerod` RPC.
|
||||
\param rates_interval Frequency to retrieve exchange rates. Set value to
|
||||
`<= 0` to disable exchange rate retrieval.
|
||||
*/
|
||||
static context make(std::string daemon_addr, std::chrono::minutes rates_interval);
|
||||
|
||||
context(context&&) = default;
|
||||
context(context const&) = delete;
|
||||
|
||||
//! Calls `raise_abort_process()`. Clients can safely destruct later.
|
||||
~context() noexcept;
|
||||
|
||||
context& operator=(context&&) = default;
|
||||
context& operator=(context const&) = delete;
|
||||
|
||||
// Do not create clone method, only one of these should exist right now.
|
||||
|
||||
//! \return The full address of the monerod ZMQ daemon.
|
||||
std::string const& daemon_address() const;
|
||||
|
||||
//! \return Client connection. Thread-safe.
|
||||
expect<client> connect() const noexcept
|
||||
{
|
||||
return client::make(ctx);
|
||||
}
|
||||
|
||||
/*!
|
||||
All block `client::send`, `client::receive`, and `client::wait` calls
|
||||
originating from `this` object AND whose `watch_scan_signal` method was
|
||||
invoked, will immediately return with `lws::error::kSignlAbortScan`. This
|
||||
is NOT signal-safe NOR signal-safe NOR thread-safe.
|
||||
*/
|
||||
expect<void> raise_abort_scan() noexcept;
|
||||
|
||||
/*!
|
||||
All blocked `client::send`, `client::receive`, and `client::wait` calls
|
||||
originating from `this` object will immediately return with
|
||||
`lws::error::kSignalAbortProcess`. This call is NOT signal-safe NOR
|
||||
thread-safe.
|
||||
*/
|
||||
expect<void> raise_abort_process() noexcept;
|
||||
|
||||
/*!
|
||||
Retrieve exchange rates, if enabled and past cache interval. Not
|
||||
thread-safe (this can be invoked from one thread only, but this is
|
||||
thread-safe with `client::get_rates()`). All clients will see new rates
|
||||
immediately.
|
||||
|
||||
\return Rates iff they were updated.
|
||||
*/
|
||||
expect<boost::optional<lws::rates>> retrieve_rates();
|
||||
};
|
||||
} // rpc
|
||||
} // lws
|
||||
169
src/rpc/daemon_zmq.cpp
Normal file
169
src/rpc/daemon_zmq.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2020, 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.
|
||||
|
||||
#include "daemon_zmq.h"
|
||||
|
||||
#include "crypto/crypto.h" // monero/src
|
||||
#include "rpc/message_data_structs.h" // monero/src
|
||||
#include "wire/crypto.h"
|
||||
#include "wire/json.h"
|
||||
#include "wire/vector.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const std::size_t default_blocks_fetched = 1000;
|
||||
constexpr const std::size_t default_transaction_count = 100;
|
||||
constexpr const std::size_t default_inputs = 2;
|
||||
constexpr const std::size_t default_outputs = 4;
|
||||
constexpr const std::size_t default_txextra_size = 2048;
|
||||
}
|
||||
|
||||
namespace rct
|
||||
{
|
||||
static void read_bytes(wire::json_reader& source, ctkey& self)
|
||||
{
|
||||
self.dest = {};
|
||||
read_bytes(source, self.mask);
|
||||
}
|
||||
|
||||
static void read_bytes(wire::json_reader& source, ecdhTuple& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(mask), WIRE_FIELD(amount));
|
||||
}
|
||||
|
||||
static void read_bytes(wire::json_reader& source, rctSig& self)
|
||||
{
|
||||
self.outPk.reserve(default_inputs);
|
||||
wire::object(source,
|
||||
WIRE_FIELD(type),
|
||||
wire::field("encrypted", std::ref(self.ecdhInfo)),
|
||||
wire::field("commitments", std::ref(self.outPk)),
|
||||
wire::field("fee", std::ref(self.txnFee))
|
||||
);
|
||||
}
|
||||
} // rct
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
static void read_bytes(wire::json_reader& source, txout_to_script& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(keys), WIRE_FIELD(script));
|
||||
}
|
||||
static void read_bytes(wire::json_reader& source, txout_to_scripthash& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(hash));
|
||||
}
|
||||
static void read_bytes(wire::json_reader& source, txout_to_key& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(key));
|
||||
}
|
||||
static void read_bytes(wire::json_reader& source, tx_out& self)
|
||||
{
|
||||
wire::object(source,
|
||||
WIRE_FIELD(amount),
|
||||
wire::variant_field("transaction output variant", std::ref(self.target),
|
||||
wire::option<txout_to_key>{"to_key"},
|
||||
wire::option<txout_to_script>{"to_script"},
|
||||
wire::option<txout_to_scripthash>{"to_scripthash"}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void read_bytes(wire::json_reader& source, txin_gen& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(height));
|
||||
}
|
||||
static void read_bytes(wire::json_reader& source, txin_to_script& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(prev), WIRE_FIELD(prevout), WIRE_FIELD(sigset));
|
||||
}
|
||||
static void read_bytes(wire::json_reader& source, txin_to_scripthash& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(prev), WIRE_FIELD(prevout), WIRE_FIELD(script), WIRE_FIELD(sigset));
|
||||
}
|
||||
static void read_bytes(wire::json_reader& source, txin_to_key& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(amount), WIRE_FIELD(key_offsets), wire::field("key_image", std::ref(self.k_image)));
|
||||
}
|
||||
static void read_bytes(wire::json_reader& source, txin_v& self)
|
||||
{
|
||||
wire::object(source,
|
||||
wire::variant_field("transaction input variant", std::ref(self),
|
||||
wire::option<txin_to_key>{"to_key"},
|
||||
wire::option<txin_gen>{"gen"},
|
||||
wire::option<txin_to_script>{"to_script"},
|
||||
wire::option<txin_to_scripthash>{"to_scripthash"}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void read_bytes(wire::json_reader& source, transaction& self)
|
||||
{
|
||||
self.vin.reserve(default_inputs);
|
||||
self.vout.reserve(default_outputs);
|
||||
self.extra.reserve(default_txextra_size);
|
||||
wire::object(source,
|
||||
WIRE_FIELD(version),
|
||||
WIRE_FIELD(unlock_time),
|
||||
wire::field("inputs", std::ref(self.vin)),
|
||||
wire::field("outputs", std::ref(self.vout)),
|
||||
WIRE_FIELD(extra),
|
||||
wire::field("ringct", std::ref(self.rct_signatures))
|
||||
);
|
||||
}
|
||||
|
||||
static void read_bytes(wire::json_reader& source, block& self)
|
||||
{
|
||||
self.tx_hashes.reserve(default_transaction_count);
|
||||
wire::object(source,
|
||||
WIRE_FIELD(major_version),
|
||||
WIRE_FIELD(minor_version),
|
||||
WIRE_FIELD(timestamp),
|
||||
WIRE_FIELD(miner_tx),
|
||||
WIRE_FIELD(tx_hashes),
|
||||
WIRE_FIELD(prev_id),
|
||||
WIRE_FIELD(nonce)
|
||||
);
|
||||
}
|
||||
|
||||
namespace rpc
|
||||
{
|
||||
static void read_bytes(wire::json_reader& source, block_with_transactions& self)
|
||||
{
|
||||
self.transactions.reserve(default_transaction_count);
|
||||
wire::object(source, WIRE_FIELD(block), WIRE_FIELD(transactions));
|
||||
}
|
||||
} // rpc
|
||||
} // cryptonote
|
||||
|
||||
void lws::rpc::read_bytes(wire::json_reader& source, get_blocks_fast_response& self)
|
||||
{
|
||||
self.blocks.reserve(default_blocks_fetched);
|
||||
self.output_indices.reserve(default_blocks_fetched);
|
||||
wire::object(source, WIRE_FIELD(blocks), WIRE_FIELD(output_indices), WIRE_FIELD(start_height), WIRE_FIELD(current_height));
|
||||
}
|
||||
|
||||
75
src/rpc/daemon_zmq.h
Normal file
75
src/rpc/daemon_zmq.h
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2020, 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>
|
||||
#include <vector>
|
||||
|
||||
#include "common/pod-class.h" // monero/src
|
||||
#include "wire/json/fwd.h"
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
POD_CLASS hash;
|
||||
}
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace rpc
|
||||
{
|
||||
struct block_with_transactions;
|
||||
}
|
||||
}
|
||||
|
||||
namespace lws
|
||||
{
|
||||
namespace rpc
|
||||
{
|
||||
struct get_blocks_fast_request
|
||||
{
|
||||
get_blocks_fast_request() = delete;
|
||||
std::vector<crypto::hash> block_ids;
|
||||
std::uint64_t start_height;
|
||||
bool prune;
|
||||
};
|
||||
struct get_blocks_fast_response
|
||||
{
|
||||
get_blocks_fast_response() = delete;
|
||||
std::vector<cryptonote::rpc::block_with_transactions> blocks;
|
||||
std::vector<std::vector<std::vector<std::uint64_t>>> output_indices;
|
||||
std::uint64_t start_height;
|
||||
std::uint64_t current_height;
|
||||
};
|
||||
struct get_blocks_fast
|
||||
{
|
||||
using request = get_blocks_fast_request;
|
||||
using response = get_blocks_fast_response;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, get_blocks_fast_response&);
|
||||
} // rpc
|
||||
} // lws
|
||||
33
src/rpc/fwd.h
Normal file
33
src/rpc/fwd.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2020, 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
|
||||
|
||||
namespace lws
|
||||
{
|
||||
struct rates;
|
||||
}
|
||||
96
src/rpc/json.h
Normal file
96
src/rpc/json.h
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2020, 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.
|
||||
|
||||
#include "wire/json.h"
|
||||
|
||||
namespace lws
|
||||
{
|
||||
namespace rpc
|
||||
{
|
||||
struct json_request_base
|
||||
{
|
||||
static constexpr const char jsonrpc[] = "2.0";
|
||||
|
||||
//! `method` must be in static memory.
|
||||
explicit json_request_base(const char* method)
|
||||
: id(0), method(method)
|
||||
{}
|
||||
|
||||
unsigned id;
|
||||
const char* method; //!< Must be in static memory
|
||||
};
|
||||
const char json_request_base::jsonrpc[];
|
||||
|
||||
//! \tparam W implements the WRITE concept \tparam M implements the METHOD concept
|
||||
template<typename W, typename M>
|
||||
struct json_request : json_request_base
|
||||
{
|
||||
template<typename... U>
|
||||
explicit json_request(U&&... args)
|
||||
: json_request_base(M::name()),
|
||||
params{std::forward<U>(args)...}
|
||||
{}
|
||||
|
||||
W params;
|
||||
};
|
||||
|
||||
template<typename W, typename M>
|
||||
inline void write_bytes(wire::json_writer& dest, const json_request<W, M>& self)
|
||||
{
|
||||
// pull fields from base class into the same object
|
||||
wire::object(dest, WIRE_FIELD_COPY(id), WIRE_FIELD_COPY(jsonrpc), WIRE_FIELD_COPY(method), WIRE_FIELD(params));
|
||||
}
|
||||
|
||||
|
||||
//! \tparam R implements the READ concept
|
||||
template<typename R>
|
||||
struct json_response
|
||||
{
|
||||
json_response() = delete;
|
||||
|
||||
unsigned id;
|
||||
R result;
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(wire::json_reader& source, json_response<R>& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(id), WIRE_FIELD(result));
|
||||
}
|
||||
|
||||
|
||||
/*! Implements the RPC concept (JSON-RPC 2.0).
|
||||
\tparam M must implement the METHOD concept. */
|
||||
template<typename M>
|
||||
struct json
|
||||
{
|
||||
using wire_type = wire::json;
|
||||
using request = json_request<typename M::request, M>;
|
||||
using response = json_response<typename M::response>;
|
||||
};
|
||||
} // rpc
|
||||
} // lws
|
||||
338
src/rpc/light_wallet.cpp
Normal file
338
src/rpc/light_wallet.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
// Copyright (c) 2018-2020, 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.
|
||||
|
||||
#include "light_wallet.h"
|
||||
|
||||
#include <boost/range/adaptor/indexed.hpp>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "db/string.h"
|
||||
#include "error.h"
|
||||
#include "misc_os_dependent.h" // monero/contrib/epee/include
|
||||
#include "ringct/rctOps.h" // monero/src
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "util/random_outputs.h"
|
||||
#include "wire/crypto.h"
|
||||
#include "wire/error.h"
|
||||
#include "wire/json.h"
|
||||
#include "wire/traits.h"
|
||||
#include "wire/vector.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
enum class iso_timestamp : std::uint64_t {};
|
||||
|
||||
struct rct_bytes
|
||||
{
|
||||
rct::key commitment;
|
||||
rct::key mask;
|
||||
rct::key amount;
|
||||
};
|
||||
static_assert(sizeof(rct_bytes) == 32 * 3, "padding in rct struct");
|
||||
|
||||
struct expand_outputs
|
||||
{
|
||||
const std::pair<lws::db::output, std::vector<crypto::key_image>>& data;
|
||||
const crypto::secret_key& user_key;
|
||||
};
|
||||
} // anonymous
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<>
|
||||
struct is_blob<rct_bytes>
|
||||
: std::true_type
|
||||
{};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void write_bytes(wire::json_writer& dest, const iso_timestamp self)
|
||||
{
|
||||
static_assert(std::is_integral<std::time_t>::value, "unexpected time_t type");
|
||||
if (std::numeric_limits<std::time_t>::max() < std::uint64_t(self))
|
||||
throw std::runtime_error{"Exceeded max time_t value"};
|
||||
|
||||
std::tm value;
|
||||
if (!epee::misc_utils::get_gmt_time(std::time_t(self), value))
|
||||
throw std::runtime_error{"Failed to convert std::time_t to std::tm"};
|
||||
|
||||
char buf[28] = {0};
|
||||
if (sizeof(buf) - 1 != std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.0-00:00", std::addressof(value)))
|
||||
throw std::runtime_error{"strftime failed"};
|
||||
|
||||
dest.string({buf, sizeof(buf) - 1});
|
||||
}
|
||||
|
||||
void write_bytes(wire::json_writer& dest, const expand_outputs self)
|
||||
{
|
||||
/*! \TODO Sending the public key for the output isn't necessary, as it can be
|
||||
re-computed from the other parts. Same with the rct commitment and rct
|
||||
amount. Consider dropping these from the API after client upgrades. Not
|
||||
storing them in the DB saves 96-bytes per received out. */
|
||||
|
||||
rct_bytes rct{};
|
||||
rct_bytes const* optional_rct = nullptr;
|
||||
if (unpack(self.data.first.extra).first & lws::db::ringct_output)
|
||||
{
|
||||
crypto::key_derivation derived;
|
||||
if (!crypto::generate_key_derivation(self.data.first.spend_meta.tx_public, self.user_key, derived))
|
||||
MONERO_THROW(lws::error::crypto_failure, "generate_key_derivation failed");
|
||||
|
||||
crypto::secret_key scalar;
|
||||
rct::ecdhTuple encrypted{self.data.first.ringct_mask, rct::d2h(self.data.first.spend_meta.amount)};
|
||||
|
||||
crypto::derivation_to_scalar(derived, self.data.first.spend_meta.index, scalar);
|
||||
rct::ecdhEncode(encrypted, rct::sk2rct(scalar), false);
|
||||
|
||||
rct.commitment = rct::commit(self.data.first.spend_meta.amount, self.data.first.ringct_mask);
|
||||
rct.mask = encrypted.mask;
|
||||
rct.amount = encrypted.amount;
|
||||
|
||||
optional_rct = std::addressof(rct);
|
||||
}
|
||||
|
||||
wire::object(dest,
|
||||
wire::field("amount", lws::rpc::safe_uint64(self.data.first.spend_meta.amount)),
|
||||
wire::field("public_key", self.data.first.pub),
|
||||
wire::field("index", self.data.first.spend_meta.index),
|
||||
wire::field("global_index", self.data.first.spend_meta.id.low),
|
||||
wire::field("tx_id", self.data.first.spend_meta.id.low),
|
||||
wire::field("tx_hash", std::cref(self.data.first.link.tx_hash)),
|
||||
wire::field("tx_prefix_hash", std::cref(self.data.first.tx_prefix_hash)),
|
||||
wire::field("tx_pub_key", self.data.first.spend_meta.tx_public),
|
||||
wire::field("timestamp", iso_timestamp(self.data.first.timestamp)),
|
||||
wire::field("height", self.data.first.link.height),
|
||||
wire::field("spend_key_images", std::cref(self.data.second)),
|
||||
wire::optional_field("rct", optional_rct)
|
||||
);
|
||||
}
|
||||
|
||||
void convert_address(const boost::string_ref source, lws::db::account_address& dest)
|
||||
{
|
||||
expect<lws::db::account_address> bytes = lws::db::address_string(source);
|
||||
if (!bytes)
|
||||
WIRE_DLOG_THROW(wire::error::schema::fixed_binary, "invalid Monero address format - " << bytes.error());
|
||||
dest = std::move(*bytes);
|
||||
}
|
||||
} // anonymous
|
||||
|
||||
namespace lws
|
||||
{
|
||||
static void write_bytes(wire::json_writer& dest, random_output const& self)
|
||||
{
|
||||
const rct_bytes rct{self.keys.mask, rct::zero(), rct::zero()};
|
||||
wire::object(dest,
|
||||
wire::field("global_index", rpc::safe_uint64(self.index)),
|
||||
wire::field("public_key", std::cref(self.keys.key)),
|
||||
wire::field("rct", std::cref(rct))
|
||||
);
|
||||
}
|
||||
static void write_bytes(wire::json_writer& dest, random_ring const& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
wire::field("amount", rpc::safe_uint64(self.amount)),
|
||||
wire::field("outputs", std::cref(self.ring))
|
||||
);
|
||||
};
|
||||
|
||||
void rpc::read_bytes(wire::json_reader& source, safe_uint64& self)
|
||||
{
|
||||
self = safe_uint64(wire::integer::convert_to<std::uint64_t>(source.safe_unsigned_integer()));
|
||||
}
|
||||
void rpc::write_bytes(wire::json_writer& dest, const safe_uint64 self)
|
||||
{
|
||||
auto buf = wire::json_writer::to_string(std::uint64_t(self));
|
||||
dest.string(buf.data());
|
||||
}
|
||||
void rpc::read_bytes(wire::json_reader& source, safe_uint64_array& self)
|
||||
{
|
||||
for (std::size_t count = source.start_array(); !source.is_array_end(count); --count)
|
||||
self.values.emplace_back(wire::integer::convert_to<std::uint64_t>(source.safe_unsigned_integer()));
|
||||
source.end_array();
|
||||
}
|
||||
|
||||
void rpc::read_bytes(wire::json_reader& source, account_credentials& self)
|
||||
{
|
||||
std::string address;
|
||||
wire::object(source,
|
||||
wire::field("address", std::ref(address)),
|
||||
wire::field("view_key", std::ref(unwrap(unwrap(self.key))))
|
||||
);
|
||||
convert_address(address, self.address);
|
||||
}
|
||||
|
||||
void rpc::write_bytes(wire::json_writer& dest, const transaction_spend& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
wire::field("amount", safe_uint64(self.meta.amount)),
|
||||
wire::field("key_image", std::cref(self.possible_spend.image)),
|
||||
wire::field("tx_pub_key", std::cref(self.meta.tx_public)),
|
||||
wire::field("out_index", self.meta.index),
|
||||
wire::field("mixin", self.possible_spend.mixin_count)
|
||||
);
|
||||
}
|
||||
|
||||
void rpc::write_bytes(wire::json_writer& dest, const get_address_info_response& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
WIRE_FIELD_COPY(locked_funds),
|
||||
WIRE_FIELD_COPY(total_received),
|
||||
WIRE_FIELD_COPY(total_sent),
|
||||
WIRE_FIELD_COPY(scanned_height),
|
||||
WIRE_FIELD_COPY(scanned_block_height),
|
||||
WIRE_FIELD_COPY(start_height),
|
||||
WIRE_FIELD_COPY(transaction_height),
|
||||
WIRE_FIELD_COPY(blockchain_height),
|
||||
WIRE_FIELD(spent_outputs),
|
||||
WIRE_OPTIONAL_FIELD(rates)
|
||||
);
|
||||
}
|
||||
|
||||
namespace rpc
|
||||
{
|
||||
static void write_bytes(wire::json_writer& dest, boost::range::index_value<const get_address_txs_response::transaction&> self)
|
||||
{
|
||||
epee::span<const std::uint8_t> const* payment_id = nullptr;
|
||||
epee::span<const std::uint8_t> payment_id_bytes;
|
||||
|
||||
const auto extra = db::unpack(self.value().info.extra);
|
||||
if (extra.second)
|
||||
{
|
||||
payment_id = std::addressof(payment_id_bytes);
|
||||
|
||||
if (extra.second == sizeof(self.value().info.payment_id.short_))
|
||||
payment_id_bytes = epee::as_byte_span(self.value().info.payment_id.short_);
|
||||
else
|
||||
payment_id_bytes = epee::as_byte_span(self.value().info.payment_id.long_);
|
||||
}
|
||||
|
||||
const bool is_coinbase = (extra.first & db::coinbase_output);
|
||||
|
||||
wire::object(dest,
|
||||
wire::field("id", std::uint64_t(self.index())),
|
||||
wire::field("hash", std::cref(self.value().info.link.tx_hash)),
|
||||
wire::field("timestamp", iso_timestamp(self.value().info.timestamp)),
|
||||
wire::field("total_received", safe_uint64(self.value().info.spend_meta.amount)),
|
||||
wire::field("total_sent", safe_uint64(self.value().spent)),
|
||||
wire::field("unlock_time", self.value().info.unlock_time),
|
||||
wire::field("height", self.value().info.link.height),
|
||||
wire::optional_field("payment_id", payment_id),
|
||||
wire::field("coinbase", is_coinbase),
|
||||
wire::field("mempool", false),
|
||||
wire::field("mixin", self.value().info.spend_meta.mixin_count),
|
||||
wire::field("spent_outputs", std::cref(self.value().spends))
|
||||
);
|
||||
}
|
||||
} // rpc
|
||||
void rpc::write_bytes(wire::json_writer& dest, const get_address_txs_response& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
wire::field("total_received", safe_uint64(self.total_received)),
|
||||
WIRE_FIELD_COPY(scanned_height),
|
||||
WIRE_FIELD_COPY(scanned_block_height),
|
||||
WIRE_FIELD_COPY(start_height),
|
||||
WIRE_FIELD_COPY(transaction_height),
|
||||
WIRE_FIELD_COPY(blockchain_height),
|
||||
wire::field("transactions", wire::as_array(boost::adaptors::index(self.transactions)))
|
||||
);
|
||||
}
|
||||
|
||||
void rpc::read_bytes(wire::json_reader& source, get_random_outs_request& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(count), WIRE_FIELD(amounts));
|
||||
}
|
||||
void rpc::write_bytes(wire::json_writer& dest, const get_random_outs_response& self)
|
||||
{
|
||||
wire::object(dest, WIRE_FIELD(amount_outs));
|
||||
}
|
||||
|
||||
void rpc::read_bytes(wire::json_reader& source, get_unspent_outs_request& self)
|
||||
{
|
||||
std::string address;
|
||||
wire::object(source,
|
||||
wire::field("address", std::ref(address)),
|
||||
wire::field("view_key", std::ref(unwrap(unwrap(self.creds.key)))),
|
||||
WIRE_FIELD(amount),
|
||||
WIRE_OPTIONAL_FIELD(mixin),
|
||||
WIRE_OPTIONAL_FIELD(use_dust),
|
||||
WIRE_OPTIONAL_FIELD(dust_threshold)
|
||||
);
|
||||
convert_address(address, self.creds.address);
|
||||
}
|
||||
void rpc::write_bytes(wire::json_writer& dest, const get_unspent_outs_response& self)
|
||||
{
|
||||
const auto expand = [&self] (const std::pair<db::output, std::vector<crypto::key_image>>& src)
|
||||
{
|
||||
return expand_outputs{src, self.user_key};
|
||||
};
|
||||
wire::object(dest,
|
||||
WIRE_FIELD_COPY(per_kb_fee),
|
||||
WIRE_FIELD_COPY(fee_mask),
|
||||
WIRE_FIELD_COPY(amount),
|
||||
wire::field("outputs", wire::as_array(std::cref(self.outputs), expand))
|
||||
);
|
||||
}
|
||||
|
||||
void rpc::write_bytes(wire::json_writer& dest, const import_response& self)
|
||||
{
|
||||
wire::object(dest,
|
||||
WIRE_FIELD_COPY(import_fee),
|
||||
WIRE_FIELD_COPY(status),
|
||||
WIRE_FIELD_COPY(new_request),
|
||||
WIRE_FIELD_COPY(request_fulfilled)
|
||||
);
|
||||
}
|
||||
|
||||
void rpc::read_bytes(wire::json_reader& source, login_request& self)
|
||||
{
|
||||
std::string address;
|
||||
wire::object(source,
|
||||
wire::field("address", std::ref(address)),
|
||||
wire::field("view_key", std::ref(unwrap(unwrap(self.creds.key)))),
|
||||
WIRE_FIELD(create_account),
|
||||
WIRE_FIELD(generated_locally)
|
||||
);
|
||||
convert_address(address, self.creds.address);
|
||||
}
|
||||
void rpc::write_bytes(wire::json_writer& dest, const login_response self)
|
||||
{
|
||||
wire::object(dest, WIRE_FIELD_COPY(new_address), WIRE_FIELD_COPY(generated_locally));
|
||||
}
|
||||
|
||||
void rpc::read_bytes(wire::json_reader& source, submit_raw_tx_request& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(tx));
|
||||
}
|
||||
void rpc::write_bytes(wire::json_writer& dest, const submit_raw_tx_response self)
|
||||
{
|
||||
wire::object(dest, WIRE_FIELD_COPY(status));
|
||||
}
|
||||
} // lws
|
||||
198
src/rpc/light_wallet.h
Normal file
198
src/rpc/light_wallet.h
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright (c) 2018-2020, 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 <boost/optional/optional.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "crypto/crypto.h" // monero/src
|
||||
#include "db/data.h"
|
||||
#include "rpc/rates.h"
|
||||
#include "util/fwd.h"
|
||||
#include "wire/json/fwd.h"
|
||||
|
||||
namespace lws
|
||||
{
|
||||
namespace rpc
|
||||
{
|
||||
//! Read/write uint64 value as JSON string.
|
||||
enum class safe_uint64 : std::uint64_t {};
|
||||
void read_bytes(wire::json_reader&, safe_uint64&);
|
||||
void write_bytes(wire::json_writer&, safe_uint64);
|
||||
|
||||
//! Read an array of uint64 values as JSON strings.
|
||||
struct safe_uint64_array
|
||||
{
|
||||
std::vector<std::uint64_t> values; // so this can be passed to another function without copy
|
||||
};
|
||||
void read_bytes(wire::json_reader&, safe_uint64_array&);
|
||||
|
||||
|
||||
struct account_credentials
|
||||
{
|
||||
lws::db::account_address address;
|
||||
crypto::secret_key key;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, account_credentials&);
|
||||
|
||||
|
||||
struct transaction_spend
|
||||
{
|
||||
transaction_spend() = delete;
|
||||
lws::db::output::spend_meta_ meta;
|
||||
lws::db::spend possible_spend;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, const transaction_spend&);
|
||||
|
||||
|
||||
struct get_address_info_response
|
||||
{
|
||||
get_address_info_response() = delete;
|
||||
safe_uint64 locked_funds;
|
||||
safe_uint64 total_received;
|
||||
safe_uint64 total_sent;
|
||||
std::uint64_t scanned_height;
|
||||
std::uint64_t scanned_block_height;
|
||||
std::uint64_t start_height;
|
||||
std::uint64_t transaction_height;
|
||||
std::uint64_t blockchain_height;
|
||||
std::vector<transaction_spend> spent_outputs;
|
||||
expect<lws::rates> rates;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, const get_address_info_response&);
|
||||
|
||||
|
||||
struct get_address_txs_response
|
||||
{
|
||||
get_address_txs_response() = delete;
|
||||
struct transaction
|
||||
{
|
||||
transaction() = delete;
|
||||
db::output info;
|
||||
std::vector<transaction_spend> spends;
|
||||
std::uint64_t spent;
|
||||
};
|
||||
|
||||
safe_uint64 total_received;
|
||||
std::uint64_t scanned_height;
|
||||
std::uint64_t scanned_block_height;
|
||||
std::uint64_t start_height;
|
||||
std::uint64_t transaction_height;
|
||||
std::uint64_t blockchain_height;
|
||||
std::vector<transaction> transactions;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, const get_address_txs_response&);
|
||||
|
||||
|
||||
struct get_random_outs_request
|
||||
{
|
||||
get_random_outs_request() = delete;
|
||||
std::uint64_t count;
|
||||
safe_uint64_array amounts;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, get_random_outs_request&);
|
||||
|
||||
struct get_random_outs_response
|
||||
{
|
||||
get_random_outs_response() = delete;
|
||||
std::vector<random_ring> amount_outs;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, const get_random_outs_response&);
|
||||
|
||||
|
||||
struct get_unspent_outs_request
|
||||
{
|
||||
get_unspent_outs_request() = delete;
|
||||
safe_uint64 amount;
|
||||
boost::optional<safe_uint64> dust_threshold;
|
||||
boost::optional<std::uint32_t> mixin;
|
||||
boost::optional<bool> use_dust;
|
||||
account_credentials creds;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, get_unspent_outs_request&);
|
||||
|
||||
struct get_unspent_outs_response
|
||||
{
|
||||
get_unspent_outs_response() = delete;
|
||||
std::uint64_t per_kb_fee;
|
||||
std::uint64_t fee_mask;
|
||||
safe_uint64 amount;
|
||||
std::vector<std::pair<db::output, std::vector<crypto::key_image>>> outputs;
|
||||
crypto::secret_key user_key;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, const get_unspent_outs_response&);
|
||||
|
||||
|
||||
struct import_response
|
||||
{
|
||||
import_response() = delete;
|
||||
safe_uint64 import_fee;
|
||||
const char* status;
|
||||
bool new_request;
|
||||
bool request_fulfilled;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, const import_response&);
|
||||
|
||||
|
||||
struct login_request
|
||||
{
|
||||
login_request() = delete;
|
||||
account_credentials creds;
|
||||
bool create_account;
|
||||
bool generated_locally;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, login_request&);
|
||||
|
||||
struct login_response
|
||||
{
|
||||
login_response() = delete;
|
||||
bool new_address;
|
||||
bool generated_locally;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, login_response);
|
||||
|
||||
|
||||
struct submit_raw_tx_request
|
||||
{
|
||||
submit_raw_tx_request() = delete;
|
||||
std::string tx;
|
||||
};
|
||||
void read_bytes(wire::json_reader&, submit_raw_tx_request&);
|
||||
|
||||
struct submit_raw_tx_response
|
||||
{
|
||||
submit_raw_tx_response() = delete;
|
||||
const char* status;
|
||||
};
|
||||
void write_bytes(wire::json_writer&, submit_raw_tx_response);
|
||||
} // rpc
|
||||
} // lws
|
||||
78
src/rpc/rates.cpp
Normal file
78
src/rpc/rates.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2018-2020, 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.
|
||||
#include "rates.h"
|
||||
|
||||
#include "wire/json.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename F, typename T>
|
||||
void map_rates(F& format, T& self)
|
||||
{
|
||||
wire::object(format,
|
||||
WIRE_FIELD(AUD),
|
||||
WIRE_FIELD(BRL),
|
||||
WIRE_FIELD(BTC),
|
||||
WIRE_FIELD(CAD),
|
||||
WIRE_FIELD(CHF),
|
||||
WIRE_FIELD(CNY),
|
||||
WIRE_FIELD(EUR),
|
||||
WIRE_FIELD(GBP),
|
||||
WIRE_FIELD(HKD),
|
||||
WIRE_FIELD(INR),
|
||||
WIRE_FIELD(JPY),
|
||||
WIRE_FIELD(KRW),
|
||||
WIRE_FIELD(MXN),
|
||||
WIRE_FIELD(NOK),
|
||||
WIRE_FIELD(NZD),
|
||||
WIRE_FIELD(SEK),
|
||||
WIRE_FIELD(SGD),
|
||||
WIRE_FIELD(TRY),
|
||||
WIRE_FIELD(USD),
|
||||
WIRE_FIELD(RUB),
|
||||
WIRE_FIELD(ZAR)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
namespace lws
|
||||
{
|
||||
WIRE_JSON_DEFINE_OBJECT(rates, map_rates);
|
||||
|
||||
namespace rpc
|
||||
{
|
||||
const char crypto_compare_::host[] = "https://min-api.cryptocompare.com:443";
|
||||
const char crypto_compare_::path[] =
|
||||
"/data/price?fsym=XMR&tsyms=AUD,BRL,BTC,CAD,CHF,CNY,EUR,GBP,"
|
||||
"HKD,INR,JPY,KRW,MXN,NOK,NZD,SEK,SGD,TRY,USD,RUB,ZAR";
|
||||
|
||||
expect<lws::rates> crypto_compare_::operator()(std::string&& body) const
|
||||
{
|
||||
return wire::json::from_bytes<lws::rates>(std::move(body));
|
||||
}
|
||||
} // rpc
|
||||
} // lws
|
||||
74
src/rpc/rates.h
Normal file
74
src/rpc/rates.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2018-2020, 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 <string>
|
||||
|
||||
#include "byte_slice.h"
|
||||
#include "common/expect.h"
|
||||
#include "wire/json/fwd.h"
|
||||
|
||||
namespace lws
|
||||
{
|
||||
struct rates
|
||||
{
|
||||
double AUD;
|
||||
double BRL;
|
||||
double BTC;
|
||||
double CAD;
|
||||
double CHF;
|
||||
double CNY;
|
||||
double EUR;
|
||||
double GBP;
|
||||
double HKD;
|
||||
double INR;
|
||||
double JPY;
|
||||
double KRW;
|
||||
double MXN;
|
||||
double NOK;
|
||||
double NZD;
|
||||
double SEK;
|
||||
double SGD;
|
||||
double TRY;
|
||||
double USD;
|
||||
double RUB;
|
||||
double ZAR;
|
||||
};
|
||||
WIRE_JSON_DECLARE_OBJECT(rates);
|
||||
|
||||
namespace rpc
|
||||
{
|
||||
struct crypto_compare_
|
||||
{
|
||||
static const char host[];
|
||||
static const char path[];
|
||||
|
||||
expect<lws::rates> operator()(std::string&& body) const;
|
||||
};
|
||||
constexpr const crypto_compare_ crypto_compare{};
|
||||
} // rpc
|
||||
} // lws
|
||||
Reference in New Issue
Block a user