Initial working base separated from top-level monero project

This commit is contained in:
Lee Clagett
2020-08-19 18:29:32 -04:00
commit a2ff89bc24
68 changed files with 11543 additions and 0 deletions

33
src/rpc/CMakeLists.txt Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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